diff options
author | EuAndreh <eu@euandre.org> | 2025-01-23 12:36:19 -0300 |
---|---|---|
committer | EuAndreh <eu@euandre.org> | 2025-01-23 12:36:19 -0300 |
commit | c13c4625dae8ff64f540caf08df425729eaf804a (patch) | |
tree | f6521318fba3b651a3863c3e35a4fbcef63f4c87 | |
parent | src/dedo.go: Instantiate &DB{} only after opening the file; simplify Open() (diff) | |
download | dedo-c13c4625dae8ff64f540caf08df425729eaf804a.tar.gz dedo-c13c4625dae8ff64f540caf08df425729eaf804a.tar.xz |
src/dedo.go: Simplify initDB()
-rw-r--r-- | src/dedo.go | 172 | ||||
-rw-r--r-- | tests/dedo.go | 67 |
2 files changed, 129 insertions, 110 deletions
diff --git a/src/dedo.go b/src/dedo.go index 416d575..a7bc629 100644 --- a/src/dedo.go +++ b/src/dedo.go @@ -1729,16 +1729,15 @@ func (db *DB) String() string { } func newDB(path string, file *os.File) *DB { - db := &DB{ + return &DB{ MaxBatchSize: DefaultMaxBatchSize, MaxBatchDelay: DefaultMaxBatchDelay, AllocSize: DefaultAllocSize, opened: true, path: path, file: file, - ops: dbops{file.WriteAt}, + ops: dbops{writeAt: file.WriteAt}, } - return db } func openFile(path string) (*os.File, error) { @@ -1760,9 +1759,82 @@ func openFile(path string) (*os.File, error) { return os.OpenFile(path, flagsRO, fileMode) } +func readPageSize(db *DB) (int, error) { + // Read the first meta page to determine the page size. + const size4KiB = 0x1000 + buf := [0x1000]byte{} + _, err := db.file.ReadAt(buf[:], 0) + if err != nil { + return 0, err + } + + m := pageInBuffer(db, buf[:], 0).meta() + err = m.validate() + if err != nil { + return 0, err + } + + return int(m.pageSize), nil +} + +// init creates a new database file and initializes its meta pages. +func initDB(db *DB, size int64) error { + if size != 0 { + pageSize, err := readPageSize(db) + if err != nil { + return err + } + + db.pageSize = pageSize + return nil + } + // Set the page size to the OS page size. + db.pageSize = os.Getpagesize() + + // Create two meta pages on a buffer. + buf := make([]byte, db.pageSize*4) + for i := 0; i < 2; i++ { + p := pageInBuffer(db, buf[:], pgid(i)) + p.id = pgid(i) + p.flags = metaPageFlag + + // Initialize the meta page. + m := p.meta() + m.magic = magic + m.version = version + m.pageSize = uint32(db.pageSize) + m.freelist = 2 + m.root = bucket{root: 3} + m.pgid = 4 + m.txid = txid(i) + m.checksum = m.sum64() + } + + // Write an empty freelist at page 3. + p := pageInBuffer(db, buf[:], pgid(2)) + p.id = pgid(2) + p.flags = freelistPageFlag + p.count = 0 + + // Write an empty leaf page at page 4. + p = pageInBuffer(db, buf[:], pgid(3)) + p.id = pgid(3) + p.flags = leafPageFlag + p.count = 0 + + // Write the buffer to our data file. + if _, err := db.ops.writeAt(buf, 0); err != nil { + return err + } + if err := fdatasync(db); err != nil { + return err + } + + return nil +} + // Open creates and opens a database at the given path. // If the file does not exist then it will be created automatically. -// Passing in nil options will cause Bolt to open the database with the default options. func Open(path string) (*DB, error) { file, err := openFile(path) if err != nil { @@ -1776,14 +1848,9 @@ func Open(path string) (*DB, error) { } db := newDB(path, file) - // Lock file so that other processes using Bolt in read-write mode cannot - // use the database at the same time. This would cause corruption since - // the two processes would write meta pages and free pages separately. - // The database file is locked exclusively (only one process can grab the lock) - // if !options.ReadOnly. - // The database file is locked using the shared lock (more than one process may - // hold a lock at the same time) otherwise (options.ReadOnly is set). - if err := flock(db); err != nil { + + err = flock(db) + if err != nil { _ = db.close() return nil, err } @@ -1801,7 +1868,8 @@ func Open(path string) (*DB, error) { } // Memory map the data file. - if err := db.mmap(0); err != nil { + err = db.mmap(0) + if err != nil { _ = db.close() return nil, err } @@ -1913,80 +1981,6 @@ func (db *DB) mmapSize(size int) (int, error) { return int(sz), nil } -func readPageSize(db *DB) (int, error) { - // Read the first meta page to determine the page size. - var buf [0x1000]byte - _, err := db.file.ReadAt(buf[:], 0) - if err != nil { - return 0, err - } - - m := db.pageInBuffer(buf[:], 0).meta() - err = m.validate() - if err != nil { - // return 0, err - return os.Getpagesize(), nil - } - - return int(m.pageSize), nil -} - -// init creates a new database file and initializes its meta pages. -func initDB(db *DB, size int64) error { - if size != 0 { - pageSize, err := readPageSize(db) - if err != nil { - return err - } - - db.pageSize = pageSize - return nil - } - // Set the page size to the OS page size. - db.pageSize = os.Getpagesize() - - // Create two meta pages on a buffer. - buf := make([]byte, db.pageSize*4) - for i := 0; i < 2; i++ { - p := db.pageInBuffer(buf[:], pgid(i)) - p.id = pgid(i) - p.flags = metaPageFlag - - // Initialize the meta page. - m := p.meta() - m.magic = magic - m.version = version - m.pageSize = uint32(db.pageSize) - m.freelist = 2 - m.root = bucket{root: 3} - m.pgid = 4 - m.txid = txid(i) - m.checksum = m.sum64() - } - - // Write an empty freelist at page 3. - p := db.pageInBuffer(buf[:], pgid(2)) - p.id = pgid(2) - p.flags = freelistPageFlag - p.count = 0 - - // Write an empty leaf page at page 4. - p = db.pageInBuffer(buf[:], pgid(3)) - p.id = pgid(3) - p.flags = leafPageFlag - p.count = 0 - - // Write the buffer to our data file. - if _, err := db.ops.writeAt(buf, 0); err != nil { - return err - } - if err := fdatasync(db); err != nil { - return err - } - - return nil -} - // Close releases all database resources. // All transactions must be closed before closing the database. func (db *DB) Close() error { @@ -2356,7 +2350,7 @@ func (db *DB) page(id pgid) *page { } // pageInBuffer retrieves a page reference from a given byte array based on the current page size. -func (db *DB) pageInBuffer(b []byte, id pgid) *page { +func pageInBuffer(db *DB, b []byte, id pgid) *page { return (*page)(unsafe.Pointer(&b[id*pgid(db.pageSize)])) } @@ -4003,7 +3997,7 @@ func (tx *Tx) write() error { func (tx *Tx) writeMeta() error { // Create a temporary buffer for the meta page. buf := make([]byte, tx.db.pageSize) - p := tx.db.pageInBuffer(buf, 0) + p := pageInBuffer(tx.db, buf, 0) tx.meta.write(p) // Write the meta page to file. diff --git a/tests/dedo.go b/tests/dedo.go index 0def91d..1ab2b61 100644 --- a/tests/dedo.go +++ b/tests/dedo.go @@ -34,6 +34,25 @@ import ( func test_newDB() { g.TestStart("newDB()") + g.Testing("different path are given back", func() { + db1 := newDB("path1", nil) + db2 := newDB("path2", nil) + + g.TAssertEqual(db1.path, "path1") + g.TAssertEqual(db2.path, "path2") + }) + + g.Testing("diferent *os.File pointers", func() { + f1 := new(os.File) + f2 := new(os.File) + + db1 := newDB("path", f1) + db2 := newDB("path", f2) + + g.TAssertEqual(db1.ops.writeAt == nil, false) + g.TAssertEqual(db2.ops.writeAt == nil, false) + g.TAssertEqual(db1 == db2, false) + }) } func test_openFile() { @@ -81,6 +100,29 @@ func test_openFile() { }) } +func test_readPageSize() { + g.TestStart("readPageSize()") + + g.Testing("empty page gives us EOF", func() { + }) + g.Testing("page smaller than 4KiB also gives us EOF", func() { }) + g.Testing("zeroed 4KiB page gives us ErrInvalid", func() { + }) +} + +func test_initDB() { + g.TestStart("initDB()") +} + +func test_Open() { + g.TestStart("Open()") +} + +// Ensure that opening a file that is not a Bolt database returns ErrInvalid. +func TestOpen_ErrInvalid(t *testing.T) { + return +} + // Ensure that a bucket that gets a non-existent key returns nil. func TestBucket_Get_NonExistent(t *testing.T) { db := MustOpenDB() @@ -2884,27 +2926,6 @@ func TestOpen_ErrNotExists(t *testing.T) { } } -// Ensure that opening a file that is not a Bolt database returns ErrInvalid. -func TestOpen_ErrInvalid(t *testing.T) { - path := tempfile() - defer os.Remove(path) - - f, err := os.Create(path) - if err != nil { - t.Fatal(err) - } - if _, err := fmt.Fprintln(f, "this is not a bolt database"); err != nil { - t.Fatal(err) - } - if err := f.Close(); err != nil { - t.Fatal(err) - } - - if _, err := Open(path); err != ErrInvalid { - t.Fatalf("unexpected error: %s", err) - } -} - // Ensure that opening a file with two invalid versions returns ErrVersionMismatch. func TestOpen_ErrVersionMismatch(t *testing.T) { if pageSize != os.Getpagesize() { @@ -6264,7 +6285,11 @@ func walkBucket(parent *Bucket, k []byte, v []byte, w io.Writer) error { func MainTest() { tempdir = getTempdir() + test_newDB() test_openFile() + test_readPageSize() + test_initDB() + test_Open() tests := []testing.InternalTest{ { "TestBucket_Get_NonExistent", TestBucket_Get_NonExistent }, |