diff --git a/benchmark/bench_test.go b/benchmark/bench_test.go index a1e363a..a8790d3 100644 --- a/benchmark/bench_test.go +++ b/benchmark/bench_test.go @@ -14,13 +14,12 @@ var walFile *wal.WAL func init() { dir, _ := os.MkdirTemp("", "wal-benchmark-test") - opts := wal.Options{ - DirPath: dir, - SegmentFileExt: ".SEG", - SegmentSize: wal.GB, - } var err error - walFile, err = wal.Open(opts) + walFile, err = wal.Open( + wal.WithDirPath(dir), + wal.WithSegmentFileExt(".SEG"), + wal.WithSegmentSize(wal.GB), + ) if err != nil { panic(err) } diff --git a/examples/main.go b/examples/main.go index c8bfa92..134dffd 100644 --- a/examples/main.go +++ b/examples/main.go @@ -9,7 +9,7 @@ import ( ) func main() { - walFile, _ := wal.Open(wal.DefaultOptions) + walFile, _ := wal.Open() // write some data chunkPosition, _ := walFile.Write([]byte("some data 1")) // read by the position diff --git a/options.go b/options.go index 2e17ca1..3e58cf1 100644 --- a/options.go +++ b/options.go @@ -2,6 +2,8 @@ package wal import "os" +type Option func(*Options) + // Options represents the configuration options for a Write-Ahead Log (WAL). type Options struct { // DirPath specifies the directory path where the WAL segment files will be stored. @@ -45,3 +47,38 @@ var DefaultOptions = Options{ Sync: false, BytesPerSync: 0, } + +// WithDirPath sets the directory path where the WAL segment files will be stored. +func WithDirPath(dir string) Option { + return func(o *Options) { + o.DirPath = dir + } +} + +// WithSegmentSize sets the maximum size of each segment file in bytes. +func WithSegmentSize(size int64) Option { + return func(o *Options) { + o.SegmentSize = size + } +} + +// WithSegmentFileExt sets the file extension of the segment files. +func WithSegmentFileExt(ext string) Option { + return func(o *Options) { + o.SegmentFileExt = ext + } +} + +// WithSync sets the whether to synchronize writes through os buffer cache and down onto the actual disk. +func WithSync(sync bool) Option { + return func(o *Options) { + o.Sync = sync + } +} + +// WithBytesPerSync sets the number of bytes to write before calling fsync. +func WithBytesPerSync(bytesPerSync uint32) Option { + return func(o *Options) { + o.BytesPerSync = bytesPerSync + } +} diff --git a/wal.go b/wal.go index a8bd55c..81dd721 100644 --- a/wal.go +++ b/wal.go @@ -33,7 +33,7 @@ var ( type WAL struct { activeSegment *segment // active segment file, used for new incoming writes. olderSegments map[SegmentID]*segment // older segment files, only used for read. - options Options + options *Options mu sync.RWMutex bytesWrite uint32 renameIds []SegmentID @@ -56,7 +56,12 @@ type Reader struct { // Open opens a WAL with the given options. // It will create the directory if not exists, and open all segment files in the directory. // If there is no segment file in the directory, it will create a new one. -func Open(options Options) (*WAL, error) { +func Open(opts ...Option) (*WAL, error) { + options := &DefaultOptions + for _, opt := range opts { + opt(options) + } + if !strings.HasPrefix(options.SegmentFileExt, ".") { return nil, fmt.Errorf("segment file extension must start with '.'") } diff --git a/wal_test.go b/wal_test.go index 853514c..f2a246d 100644 --- a/wal_test.go +++ b/wal_test.go @@ -1,11 +1,12 @@ package wal import ( - "github.com/stretchr/testify/assert" "io" "os" "strings" "testing" + + "github.com/stretchr/testify/assert" ) func destroyWAL(wal *WAL) { @@ -17,12 +18,11 @@ func destroyWAL(wal *WAL) { func TestWAL_WriteALL(t *testing.T) { dir, _ := os.MkdirTemp("", "wal-test-write-batch-1") - opts := Options{ - DirPath: dir, - SegmentFileExt: ".SEG", - SegmentSize: 32 * 1024 * 1024, - } - wal, err := Open(opts) + wal, err := Open( + WithDirPath(dir), + WithSegmentFileExt(".SEG"), + WithSegmentSize(32*1024*1024), + ) assert.Nil(t, err) defer destroyWAL(wal) @@ -35,12 +35,11 @@ func TestWAL_WriteALL(t *testing.T) { func TestWAL_Write(t *testing.T) { dir, _ := os.MkdirTemp("", "wal-test-write1") - opts := Options{ - DirPath: dir, - SegmentFileExt: ".SEG", - SegmentSize: 32 * 1024 * 1024, - } - wal, err := Open(opts) + wal, err := Open( + WithDirPath(dir), + WithSegmentFileExt(".SEG"), + WithSegmentSize(32*1024*1024), + ) assert.Nil(t, err) defer destroyWAL(wal) @@ -68,12 +67,11 @@ func TestWAL_Write(t *testing.T) { func TestWAL_Write_large(t *testing.T) { dir, _ := os.MkdirTemp("", "wal-test-write2") - opts := Options{ - DirPath: dir, - SegmentFileExt: ".SEG", - SegmentSize: 32 * 1024 * 1024, - } - wal, err := Open(opts) + wal, err := Open( + WithDirPath(dir), + WithSegmentFileExt(".SEG"), + WithSegmentSize(32*1024*1024), + ) assert.Nil(t, err) defer destroyWAL(wal) @@ -82,12 +80,11 @@ func TestWAL_Write_large(t *testing.T) { func TestWAL_Write_large2(t *testing.T) { dir, _ := os.MkdirTemp("", "wal-test-write3") - opts := Options{ - DirPath: dir, - SegmentFileExt: ".SEG", - SegmentSize: 32 * 1024 * 1024, - } - wal, err := Open(opts) + wal, err := Open( + WithDirPath(dir), + WithSegmentFileExt(".SEG"), + WithSegmentSize(32*1024*1024), + ) assert.Nil(t, err) defer destroyWAL(wal) @@ -96,12 +93,11 @@ func TestWAL_Write_large2(t *testing.T) { func TestWAL_OpenNewActiveSegment(t *testing.T) { dir, _ := os.MkdirTemp("", "wal-test-new-active-segment") - opts := Options{ - DirPath: dir, - SegmentFileExt: ".SEG", - SegmentSize: 32 * 1024 * 1024, - } - wal, err := Open(opts) + wal, err := Open( + WithDirPath(dir), + WithSegmentFileExt(".SEG"), + WithSegmentSize(32*1024*1024), + ) assert.Nil(t, err) defer destroyWAL(wal) @@ -119,12 +115,11 @@ func TestWAL_OpenNewActiveSegment(t *testing.T) { func TestWAL_IsEmpty(t *testing.T) { dir, _ := os.MkdirTemp("", "wal-test-is-empty") - opts := Options{ - DirPath: dir, - SegmentFileExt: ".SEG", - SegmentSize: 32 * 1024 * 1024, - } - wal, err := Open(opts) + wal, err := Open( + WithDirPath(dir), + WithSegmentFileExt(".SEG"), + WithSegmentSize(32*1024*1024), + ) assert.Nil(t, err) defer destroyWAL(wal) @@ -135,12 +130,11 @@ func TestWAL_IsEmpty(t *testing.T) { func TestWAL_Reader(t *testing.T) { dir, _ := os.MkdirTemp("", "wal-test-wal-reader") - opts := Options{ - DirPath: dir, - SegmentFileExt: ".SEG", - SegmentSize: 32 * 1024 * 1024, - } - wal, err := Open(opts) + wal, err := Open( + WithDirPath(dir), + WithSegmentFileExt(".SEG"), + WithSegmentSize(32*1024*1024), + ) assert.Nil(t, err) defer destroyWAL(wal) @@ -174,7 +168,11 @@ func TestWAL_Reader(t *testing.T) { err = wal.Close() assert.Nil(t, err) - wal2, err := Open(opts) + wal2, err := Open( + WithDirPath(dir), + WithSegmentFileExt(".SEG"), + WithSegmentSize(32*1024*1024), + ) assert.Nil(t, err) defer func() { _ = wal2.Close() @@ -239,12 +237,11 @@ func testWriteAndIterate(t *testing.T, wal *WAL, size int, valueSize int) { func TestWAL_Delete(t *testing.T) { dir, _ := os.MkdirTemp("", "wal-test-delete") - opts := Options{ - DirPath: dir, - SegmentFileExt: ".SEG", - SegmentSize: 32 * 1024 * 1024, - } - wal, err := Open(opts) + wal, err := Open( + WithDirPath(dir), + WithSegmentFileExt(".SEG"), + WithSegmentSize(32*1024*1024), + ) assert.Nil(t, err) testWriteAndIterate(t, wal, 2000, 512) assert.False(t, wal.IsEmpty()) @@ -253,19 +250,22 @@ func TestWAL_Delete(t *testing.T) { err = wal.Delete() assert.Nil(t, err) - wal, err = Open(opts) + wal, err = Open( + WithDirPath(dir), + WithSegmentFileExt(".SEG"), + WithSegmentSize(32*1024*1024), + ) assert.Nil(t, err) assert.True(t, wal.IsEmpty()) } func TestWAL_ReaderWithStart(t *testing.T) { dir, _ := os.MkdirTemp("", "wal-test-wal-reader-with-start") - opts := Options{ - DirPath: dir, - SegmentFileExt: ".SEG", - SegmentSize: 8 * 1024 * 1024, - } - wal, err := Open(opts) + wal, err := Open( + WithDirPath(dir), + WithSegmentFileExt(".SEG"), + WithSegmentSize(8*1024*1024), + ) assert.Nil(t, err) defer destroyWAL(wal) @@ -295,12 +295,11 @@ func TestWAL_ReaderWithStart(t *testing.T) { func TestWAL_RenameFileExt(t *testing.T) { dir, _ := os.MkdirTemp("", "wal-test-rename-ext") - opts := Options{ - DirPath: dir, - SegmentFileExt: ".VLOG.1.temp", - SegmentSize: 8 * 1024 * 1024, - } - wal, err := Open(opts) + wal, err := Open( + WithDirPath(dir), + WithSegmentFileExt(".VLOG.1.temp"), + WithSegmentSize(8*1024*1024), + ) assert.Nil(t, err) defer destroyWAL(wal) testWriteAndIterate(t, wal, 20000, 512) @@ -311,8 +310,7 @@ func TestWAL_RenameFileExt(t *testing.T) { err = wal.RenameFileExt(".VLOG.1") assert.Nil(t, err) - opts.SegmentFileExt = ".VLOG.1" - wal2, err := Open(opts) + wal2, err := Open(WithSegmentFileExt(".VLOG.1")) assert.Nil(t, err) defer func() { _ = wal2.Close()