Skip to content

Commit

Permalink
Add support for optimistic transaction db.
Browse files Browse the repository at this point in the history
  • Loading branch information
bhartnett committed Jul 7, 2024
1 parent 6b7de57 commit df3c610
Show file tree
Hide file tree
Showing 10 changed files with 536 additions and 11 deletions.
131 changes: 131 additions & 0 deletions rocksdb/optimistictxdb.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# Nim-RocksDB
# Copyright 2024 Status Research & Development GmbH
# Licensed under either of
#
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * GPL license, version 2.0, ([LICENSE-GPLv2](LICENSE-GPLv2) or https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.

## A `OptimisticTxDbRef` can be used to open a connection to the RocksDB database
## with support for transactional operations against multiple column families.
## To create a new transaction call `beginTransaction` which will return a
## `TransactionRef`. To commit or rollback the transaction call `commit` or
## `rollback` on the `TransactionRef` type after applying changes to the transaction.

{.push raises: [].}

import
std/[sequtils, locks],
./lib/librocksdb,
./options/[dbopts, readopts, writeopts],
./transactions/[transaction, otxopts],
./columnfamily/[cfopts, cfdescriptor, cfhandle],
./internal/[cftable, utils],
./rocksresult

export dbopts, cfdescriptor, readopts, writeopts, otxopts, transaction, rocksresult

type
OptimisticTxDbPtr* = ptr rocksdb_optimistictransactiondb_t

OptimisticTxDbRef* = ref object
lock: Lock
cPtr: OptimisticTxDbPtr
path: string
dbOpts: DbOptionsRef
cfDescriptors: seq[ColFamilyDescriptor]
defaultCfHandle: ColFamilyHandleRef
cfTable: ColFamilyTableRef

proc openOptimisticTxDb*(
path: string,
dbOpts = defaultDbOptions(autoClose = true),
columnFamilies: openArray[ColFamilyDescriptor] = [],
): RocksDBResult[OptimisticTxDbRef] =
## Open a `OptimisticTxDbRef` with the given options and column families.
## If no column families are provided the default column family will be used.
## If no options are provided the default options will be used.
## These default options will be closed when the database is closed.
## If any options are provided, they will need to be closed manually.

var cfs = columnFamilies.toSeq()
if DEFAULT_COLUMN_FAMILY_NAME notin columnFamilies.mapIt(it.name()):
cfs.add(defaultColFamilyDescriptor(autoClose = true))

var
cfNames = cfs.mapIt(it.name().cstring)
cfOpts = cfs.mapIt(it.options.cPtr)
cfHandles = newSeq[ColFamilyHandlePtr](cfs.len)
errors: cstring

let txDbPtr = rocksdb_optimistictransactiondb_open_column_families(
dbOpts.cPtr,
path.cstring,
cfNames.len().cint,
cast[cstringArray](cfNames[0].addr),
cfOpts[0].addr,
cfHandles[0].addr,
cast[cstringArray](errors.addr),
)
bailOnErrorsWithCleanup(errors):
autoCloseNonNil(dbOpts)
autoCloseAll(cfs)

let
cfTable = newColFamilyTable(cfNames.mapIt($it), cfHandles)
db = OptimisticTxDbRef(
lock: createLock(),
cPtr: txDbPtr,
path: path,
dbOpts: dbOpts,
cfDescriptors: cfs,
defaultCfHandle: cfTable.get(DEFAULT_COLUMN_FAMILY_NAME),
cfTable: cfTable,
)
ok(db)

proc getColFamilyHandle*(
db: OptimisticTxDbRef, name: string
): RocksDBResult[ColFamilyHandleRef] =
let cfHandle = db.cfTable.get(name)
if cfHandle.isNil():
err("rocksdb: unknown column family")
else:
ok(cfHandle)

proc isClosed*(db: OptimisticTxDbRef): bool {.inline.} =
## Returns `true` if the `OptimisticTxDbRef` has been closed.
db.cPtr.isNil()

proc beginTransaction*(
db: OptimisticTxDbRef,
readOpts = defaultReadOptions(autoClose = true),
writeOpts = defaultWriteOptions(autoClose = true),
otxOpts = defaultOptimisticTxOptions(autoClose = true),
cfHandle = db.defaultCfHandle,
): TransactionRef =
## Begin a new transaction against the database. The transaction will default
## to using the specified column family. If no column family is specified
## then the default column family will be used.
doAssert not db.isClosed()

let txPtr =
rocksdb_optimistictransaction_begin(db.cPtr, writeOpts.cPtr, otxOpts.cPtr, nil)

newTransaction(txPtr, readOpts, writeOpts, nil, otxOpts, cfHandle)

proc close*(db: OptimisticTxDbRef) =
## Close the `OptimisticTxDbRef`.

withLock(db.lock):
if not db.isClosed():
# the column families should be closed before the database
db.cfTable.close()

rocksdb_optimistictransactiondb_close(db.cPtr)
db.cPtr = nil

# opts should be closed after the database is closed
autoCloseNonNil(db.dbOpts)
autoCloseAll(db.cfDescriptors)
4 changes: 1 addition & 3 deletions rocksdb/transactiondb.nim
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,11 @@ proc beginTransaction*(
## Begin a new transaction against the database. The transaction will default
## to using the specified column family. If no column family is specified
## then the default column family will be used.
##
##
doAssert not db.isClosed()

let txPtr = rocksdb_transaction_begin(db.cPtr, writeOpts.cPtr, txOpts.cPtr, nil)

newTransaction(txPtr, readOpts, writeOpts, txOpts, cfHandle)
newTransaction(txPtr, readOpts, writeOpts, txOpts, nil, cfHandle)

proc close*(db: TransactionDbRef) =
## Close the `TransactionDbRef`.
Expand Down
49 changes: 49 additions & 0 deletions rocksdb/transactions/otxopts.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Nim-RocksDB
# Copyright 2024 Status Research & Development GmbH
# Licensed under either of
#
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * GPL license, version 2.0, ([LICENSE-GPLv2](LICENSE-GPLv2) or https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
#
# at your option. This file may not be copied, modified, or distributed except according to those terms.

{.push raises: [].}

import ../lib/librocksdb

type
OptimisticTxOptionsPtr* = ptr rocksdb_optimistictransaction_options_t

OptimisticTxOptionsRef* = ref object
cPtr: OptimisticTxOptionsPtr
autoClose*: bool # if true then close will be called when the transaction is closed

proc createOptimisticTxOptions*(autoClose = false): OptimisticTxOptionsRef =
OptimisticTxOptionsRef(
cPtr: rocksdb_optimistictransaction_options_create(), autoClose: autoClose
)

proc isClosed*(txOpts: OptimisticTxOptionsRef): bool {.inline.} =
txOpts.cPtr.isNil()

proc cPtr*(txOpts: OptimisticTxOptionsRef): OptimisticTxOptionsPtr =
doAssert not txOpts.isClosed()
txOpts.cPtr

template setOpt(nname, ntyp, ctyp: untyped) =
proc `nname=`*(txOpts: OptimisticTxOptionsRef, value: ntyp) =
doAssert not txOpts.isClosed()
`rocksdb_optimistictransaction_options_set nname`(txOpts.cPtr, value.ctyp)

setOpt setSnapshot, bool, uint8

proc defaultOptimisticTxOptions*(autoClose = false): OptimisticTxOptionsRef {.inline.} =
let txOpts = createOptimisticTxOptions(autoClose)

# TODO: set prefered defaults
txOpts

proc close*(txOpts: OptimisticTxOptionsRef) =
if not txOpts.isClosed():
rocksdb_optimistictransaction_options_destroy(txOpts.cPtr)
txOpts.cPtr = nil
6 changes: 5 additions & 1 deletion rocksdb/transactions/transaction.nim
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import
../options/[readopts, writeopts],
../internal/[cftable, utils],
../rocksresult,
./txopts
./[txopts, otxopts]

export rocksresult

Expand All @@ -36,20 +36,23 @@ type
readOpts: ReadOptionsRef
writeOpts: WriteOptionsRef
txOpts: TransactionOptionsRef
otxOpts: OptimisticTxOptionsRef
defaultCfHandle: ColFamilyHandleRef

proc newTransaction*(
cPtr: TransactionPtr,
readOpts: ReadOptionsRef,
writeOpts: WriteOptionsRef,
txOpts: TransactionOptionsRef,
otxOpts: OptimisticTxOptionsRef,
defaultCfHandle: ColFamilyHandleRef,
): TransactionRef =
TransactionRef(
cPtr: cPtr,
readOpts: readOpts,
writeOpts: writeOpts,
txOpts: txOpts,
otxOpts: otxOpts,
defaultCfHandle: defaultCfHandle,
)

Expand Down Expand Up @@ -182,3 +185,4 @@ proc close*(tx: TransactionRef) =
autoCloseNonNil(tx.readOpts)
autoCloseNonNil(tx.writeOpts)
autoCloseNonNil(tx.txOpts)
autoCloseNonNil(tx.otxOpts)
2 changes: 2 additions & 0 deletions tests/test_all.nim
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ import
./options/test_readopts,
./options/test_tableopts,
./options/test_writeopts,
./transactions/test_otxopts,
./transactions/test_txdbopts,
./transactions/test_txopts,
./test_backup,
./test_columnfamily,
./test_optimistictxdb,
./test_rocksdb,
./test_rocksiterator,
./test_sstfilewriter,
Expand Down
16 changes: 15 additions & 1 deletion tests/test_helper.nim
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

{.used.}

import std/sequtils, ../rocksdb/backup, ../rocksdb/rocksdb, ../rocksdb/transactiondb
import std/sequtils, ../rocksdb/[backup, rocksdb, transactiondb, optimistictxdb]

proc initReadWriteDb*(
path: string, columnFamilyNames: openArray[string] = @[]
Expand Down Expand Up @@ -57,3 +57,17 @@ proc initTransactionDb*(
echo res.error()
doAssert res.isOk()
res.value()

proc initOptimisticTxDb*(
path: string, columnFamilyNames: openArray[string] = @[]
): OptimisticTxDbRef =
let res = openOptimisticTxDb(
path,
columnFamilies = columnFamilyNames.mapIt(
initColFamilyDescriptor(it, defaultColFamilyOptions(autoClose = true))
),
)
if res.isErr():
echo res.error()
doAssert res.isOk()
res.value()
Loading

0 comments on commit df3c610

Please sign in to comment.