Documentation ¶
Overview ¶
Package lmdbsync provides advanced synchronization for LMDB environments at the cost of performance. The package provides a drop-in replacement for *lmdb.Env that can be used in situations where the database may be resized or where the flag lmdb.NoLock is used.
Bypassing an Env's methods to access the underlying lmdb.Env is not safe. The severity of such usage depends such behavior should be strictly avoided as it may produce undefined behavior from the LMDB C library.
Resizing the environment ¶
The Env type synchronizes all calls to Env.SetMapSize so that it may, with some caveats, be safely called in the presence of concurrent transactions after an environment has been opened. All running transactions must complete before the method will be called on the underlying lmdb.Env.
If an open transaction depends on a call to Env.SetMapSize then the Env will deadlock and block all future transactions. When using a Handler to automatically call Env.SetMapSize this implies the restriction that transactions must terminate independently of the creation/termination of other transactions to avoid deadlock.
In the simplest example, a view transaction that attempts an update on the underlying Env will deadlock the environment if the map is full and a Handler attempts to resize the map so the update may be retried.
env.View(func(txn *lmdb.Txn) (err error) { v, err := txn.Get(db, key) if err != nil { return err } err = env.Update(func(txn *lmdb.Txn) (err error) { // deadlock on lmdb.MapFull! txn.Put(dbi, key, append(v, b...)) }) return err }
The update should instead be prepared inside the view and then executed following its termination. This removes the implicit dependence of the view on calls to Env.SetMapSize().
var v []byte env.View(func(txn *lmdb.Txn) (err error) { // RawRead isn't used because the value will be used outside the // transaction. v, err = txn.Get(db, key) if err != nil { return err } return nil } if err != nil { // ... } err = env.Update(func(txn *lmdb.Txn) (err error) { // no deadlock, even if env is resized! txn.Put(dbi, key, append(v, b...)) })
The developers of LMDB officially recommend against applications changing the memory map size for an open database. It requires careful synchronization by all processes accessing the database file. And, a large memory map will not affect disk usage on operating systems that support sparse files (e.g. Linux, not OS X).
See mdb_env_set_mapsize.
MapFull ¶
The MapFullHandler function configures an Env to automatically call increase the map size with Env.SetMapSize and retry transactions when a lmdb.MapFull error prevents an update from being committed.
Because updates may need to execute multiple times in the presence of lmdb.MapFull it is important to make sure their TxnOp functions are idempotent and do not cause unwanted additive change to the program state.
See mdb_txn_commit and MDB_MAP_FULL.
MapResized ¶
When multiple processes access and resize an environment it is not uncommon to encounter a MapResized error which prevents the TxnOp from being executed and requires a synchronized call to Env.SetMapSize before continuing normal operation.
The MapResizedHandler function configures an Env to automatically adopt a new map size when a lmdb.MapResized error is encountered and retry execution of the TxnOp.
See mdb_txn_begin and MDB_MAP_RESIZED.
NoLock ¶
When the lmdb.NoLock flag is set on an environment Env handles all transaction synchronization using Go structures and is an experimental feature. It is unclear what benefits this provides.
Usage of lmdb.NoLock requires that update transactions acquire an exclusive lock on the environment. In such cases it is required that view transactions execute independently of update transactions, a requirement more strict than that from handling MapFull.
See mdb_env_open and MDB_NOLOCK.
Index ¶
- Variables
- type DelayFunc
- type Env
- func (r *Env) BeginTxn(parent *lmdb.Txn, flags uint) (*lmdb.Txn, error)
- func (r *Env) Open(path string, flags uint, mode os.FileMode) error
- func (r *Env) RunTxn(flags uint, op lmdb.TxnOp) (err error)
- func (r *Env) SetMapSize(size int64) error
- func (r *Env) Update(op lmdb.TxnOp) error
- func (r *Env) UpdateLocked(op lmdb.TxnOp) error
- func (r *Env) View(op lmdb.TxnOp) error
- func (r *Env) WithHandler(h Handler) TxnRunner
- type Handler
- type HandlerChain
- type MapFullFunc
- type TxnRunner
Constants ¶
This section is empty.
Variables ¶
var ErrTxnRetry = errors.New("lmdbsync: retry failed txn")
ErrTxnRetry is returned by a Handler to have the Env retry the transaction.
var MapResizedDefaultDelay = ExponentialBackoff(time.Millisecond, 5*time.Millisecond, 2)
MapResizedDefaultDelay is the default DelayFunc when MapResizedHandler is passed a nil value.
var MapResizedDefaultRetry = 2
MapResizedDefaultRetry is the default number of attempts MapResizedHandler will make adopt a new map size when lmdb.MapResized is encountered repeatedly.
Functions ¶
This section is empty.
Types ¶
type DelayFunc ¶ added in v1.6.0
DelayFunc takes as input the number of previous attempts and returns the delay before making another attempt.
type Env ¶
type Env struct { *lmdb.Env Handlers HandlerChain // contains filtered or unexported fields }
Env wraps an *lmdb.Env, receiving all the same methods and proxying some to provide transaction management. Transactions run by an Env handle lmdb.MapResized error transparently through additional synchronization. Additionally, Env is safe to use on environments setting the lmdb.NoLock flag. When in NoLock mode write transactions block all read transactions from running (in addition to blocking other write transactions like a normal lmdb.Env would).
Env proxies several methods to provide synchronization required for safe operation in some scenarios. It is important not byprass proxies and call the methods directly on the underlying lmdb.Env or synchronization may be interfered with. Calling proxied methods directly on the lmdb.Env may result in poor transaction performance or unspecified behavior in from the C library.
func NewEnv ¶
NewEnv returns an newly allocated Env that wraps env. If env is nil then lmdb.NewEnv() will be called to allocate an lmdb.Env.
func (*Env) BeginTxn ¶ added in v1.1.1
BeginTxn overrides the r.Env.BeginTxn and always returns an error. An unmanaged transaction.
func (*Env) Open ¶
Open is a proxy for r.Env.Open() that detects the lmdb.NoLock flag to properly manage transaction synchronization.
func (*Env) RunTxn ¶
RunTxn is a proxy for r.Env.RunTxn().
If lmdb.NoLock is set on r.Env then RunTxn will block while other updates are in progress, regardless of flags.
func (*Env) SetMapSize ¶
SetMapSize is a proxy for r.Env.SetMapSize() that blocks while concurrent transactions are in progress.
func (*Env) Update ¶
Update is a proxy for r.Env.Update().
If lmdb.NoLock is set on r.Env then Update blocks until all other transactions have terminated and blocks all other transactions from running while in progress (including readonly transactions).
func (*Env) UpdateLocked ¶
UpdateLocked is a proxy for r.Env.UpdateLocked().
If lmdb.NoLock is set on r.Env then UpdateLocked blocks until all other transactions have terminated and blocks all other transactions from running while in progress (including readonly transactions).
func (*Env) View ¶
View is a proxy for r.Env.View().
If lmdb.NoLock is set on r.Env then View will block until any running update completes.
func (*Env) WithHandler ¶
WithHandler returns a TxnRunner than handles transaction errors r.Handlers chained with h.
type Handler ¶
type Handler interface {
HandleTxnErr(ctx context.Context, env *Env, err error) (context.Context, error)
}
Handler can intercept errors returned by a transaction and handle them in an application-specific way, including by resizing the environment and retrying the transaction by returning ErrTxnRetry.
func MapFullHandler ¶
func MapFullHandler(fn MapFullFunc) Handler
MapFullHandler returns a Handler that retries updates which failed to commit due to lmdb.MapFull errors. When lmdb.MapFull is encountered fn is used to set a new new map size before opening a new transaction and executing the lmdb.TxnOp again.
When using MapFullHandler it is important that updates are idempotent. An Env.Update that encounters lmdb.MapFull may execute its lmdb.TxnOp function multiple times before successfully committing it (or aborting).
Open view transactions must not wait for updates to complete when using MapFullHandler or the environment will deadlock.
func MapResizedHandler ¶
MapResizedHandler returns a Handler that transparently adopts map sizes set by external processes and retries any transactions that failed to start because of lmdb.MapResized.
If the database is growing too rapidly and maxRetry consecutive transactions fail due to lmdb.MapResized then the Handler returned by MapResizedHandler gives up and returns the lmdb.MapResized error to the caller. Delay will be called before each call to Env.SetMapSize to insert an optional delay.
Open transactions must not directly create new (non-child) transactions when using MapResizedHandler or the environment will deadlock.
type HandlerChain ¶
type HandlerChain []Handler
HandlerChain is a Handler implementation that iteratively calls each handler in the underlying slice when handling an error.
func (HandlerChain) Append ¶
func (c HandlerChain) Append(h ...Handler) HandlerChain
Append returns a new HandlerChain that will evaluate h in sequence after the Handlers already in C are evaluated. Append does not modify the storage undelying c.
func (HandlerChain) HandleTxnErr ¶
func (c HandlerChain) HandleTxnErr(ctx context.Context, env *Env, err error) (context.Context, error)
HandleTxnErr implements the Handler interface. Each handler in c processes the context.Context and error returned by the previous handler.
type MapFullFunc ¶
MapFullFunc is a function for resizing a memory map after it has become full. The function receives the current map size as its argument and returns a new map size. The new size will only be applied if the second return value is true.
type TxnRunner ¶
type TxnRunner interface { RunTxn(flags uint, op lmdb.TxnOp) error View(op lmdb.TxnOp) error Update(op lmdb.TxnOp) error UpdateLocked(op lmdb.TxnOp) error WithHandler(h Handler) TxnRunner }
TxnRunner is an interface for types that can run lmdb transactions. TxnRunner is satisfied by Env.
Directories ¶
Path | Synopsis |
---|---|
Command testresize is a utility used by the lmdbsync tests to validate its multiprocessing capabilities.
|
Command testresize is a utility used by the lmdbsync tests to validate its multiprocessing capabilities. |