From eda876f2d752c3aa90c555e224da3080492e129e Mon Sep 17 00:00:00 2001 From: Alex Sharov Date: Sat, 25 Feb 2023 18:02:01 +0700 Subject: [PATCH] cursors pool --- mdbx/cursor.go | 37 +++++++++++-- mdbx/cursor_test.go | 127 ++++++++++++++++++++++++++++++++++++++++---- mdbx/error_unix.go | 1 + 3 files changed, 151 insertions(+), 14 deletions(-) diff --git a/mdbx/cursor.go b/mdbx/cursor.go index 4b9301d..02dcb7e 100644 --- a/mdbx/cursor.go +++ b/mdbx/cursor.go @@ -7,6 +7,8 @@ package mdbx */ import "C" import ( + "fmt" + "sync" "unsafe" ) @@ -71,14 +73,24 @@ func openCursor(txn *Txn, db DBI) (*Cursor, error) { return c, nil } -// Renew associates readonly cursor with txn. +// Renew associates cursor with txn. // // See mdb_cursor_renew. func (c *Cursor) Renew(txn *Txn) error { ret := C.mdbx_cursor_renew(txn._txn, c._c) - err := operrno("mdbx_cursor_renew", ret) - if err != nil { - return err + if ret != success { + return operrno("mdbx_cursor_renew", ret) + } + c.txn = txn + return nil +} + +// Bind Using of the `mdbx_cursor_bind()` is equivalent to calling mdbx_cursor_renew() but with specifying an arbitrary +// dbi handle. +func (c *Cursor) Bind(txn *Txn, db DBI) error { + ret := C.mdbx_cursor_bind(txn._txn, c._c, C.MDBX_dbi(db)) + if ret != success { + return operrno("mdbx_cursor_bind", ret) } c.txn = txn return nil @@ -308,3 +320,20 @@ func (c *Cursor) Count() (uint64, error) { } return uint64(_size), nil } + +var cursorPool = sync.Pool{ + New: func() interface{} { + return CreateCursor() + }, +} + +func CursorFromPool() *Cursor { return cursorPool.Get().(*Cursor) } +func CursorToPool(c *Cursor) { cursorPool.Put(c) } + +func CreateCursor() *Cursor { + c := &Cursor{_c: C.mdbx_cursor_create(nil)} + if c._c == nil { + panic(fmt.Errorf("mdbx.CreateCursor: OOM")) + } + return c +} diff --git a/mdbx/cursor_test.go b/mdbx/cursor_test.go index 2caeb07..efeb240 100644 --- a/mdbx/cursor_test.go +++ b/mdbx/cursor_test.go @@ -886,6 +886,90 @@ func TestCursor_Renew(t *testing.T) { } } +func TestCursor_Bind(t *testing.T) { + env := setup(t) + + var db1, db2 DBI + err := env.Update(func(txn *Txn) (err error) { + db1, err = txn.CreateDBI("testing1") + return err + }) + if err != nil { + t.Error(err) + return + } + + err = env.Update(func(txn *Txn) (err error) { + put := func(k, v string) { + if err == nil { + err = txn.Put(db1, []byte(k), []byte(v), 0) + } + } + put("k1", "v1") + put("k2", "v2") + put("k3", "v3") + return err + }) + if err != nil { + t.Error("err") + } + + var cur *Cursor + err = env.View(func(txn *Txn) (err error) { + cur, err = txn.OpenCursor(db2) + if err != nil { + return err + } + return nil + }) + if err != nil { + t.Error(err) + } + + err = env.View(func(txn *Txn) (err error) { + { + err = cur.Bind(txn, db1) + if err != nil { + return err + } + + k, v, err := cur.Get(nil, nil, Next) + if err != nil { + return err + } + if string(k) != "k1" { + return fmt.Errorf("key: %q", k) + } + if string(v) != "v1" { + return fmt.Errorf("val: %q", v) + } + } + + { + c2 := CreateCursor() + err = c2.Bind(txn, db1) + if err != nil { + return err + } + + k, v, err := c2.Get(nil, nil, Next) + if err != nil { + return err + } + if string(k) != "k1" { + return fmt.Errorf("key: %q", k) + } + if string(v) != "v1" { + return fmt.Errorf("val: %q", v) + } + } + return nil + }) + if err != nil { + t.Error(err) + } +} + func BenchmarkCursor(b *testing.B) { env := setup(b) @@ -924,9 +1008,10 @@ func BenchmarkCursor(b *testing.B) { func BenchmarkCursor_Renew(b *testing.B) { env := setup(b) + var db DBI var cur *Cursor err := env.View(func(txn *Txn) (err error) { - db, err := txn.OpenRoot(0) + db, err = txn.OpenRoot(0) if err != nil { return err } @@ -939,16 +1024,38 @@ func BenchmarkCursor_Renew(b *testing.B) { } _ = env.View(func(txn *Txn) (err error) { - b.ResetTimer() - defer b.StopTimer() - - for i := 0; i < b.N; i++ { - err = cur.Renew(txn) - if err != nil { - return err + b.Run("1", func(b *testing.B) { + for i := 0; i < b.N; i++ { + if err := cur.Renew(txn); err != nil { + panic(err) + } } - } - + }) + b.Run("2", func(b *testing.B) { + for i := 0; i < b.N; i++ { + if err := cur.Bind(txn, db); err != nil { + panic(err) + } + } + }) + b.Run("3", func(b *testing.B) { + for i := 0; i < b.N; i++ { + c, err := txn.OpenCursor(db) + if err != nil { + panic(err) + } + c.Close() + } + }) + b.Run("4", func(b *testing.B) { + for i := 0; i < b.N; i++ { + cur := CursorFromPool() + if err := cur.Bind(txn, db); err != nil { + panic(err) + } + CursorToPool(cur) + } + }) return nil }) } diff --git a/mdbx/error_unix.go b/mdbx/error_unix.go index 71c57b1..9981ed7 100644 --- a/mdbx/error_unix.go +++ b/mdbx/error_unix.go @@ -1,3 +1,4 @@ +//go:build !windows // +build !windows package mdbx