[node] support self.cache=nil in SQLiteStore

also fix a discrepancy where cli option `--cache-size` is documented as
`0 disables the cache` in `codex/conf.nim`, but previously the value `0` would
result in a cache being constructed with default parameter values for
`CacheStore.new()`

Closes #180
This commit is contained in:
Michael Bradley, Jr 2022-08-04 18:51:05 -05:00 committed by Michael Bradley
parent b7df2d151c
commit 2ecf750959
3 changed files with 217 additions and 195 deletions

View File

@ -102,11 +102,11 @@ proc new*(T: type CodexServer, config: CodexConf): T =
.withTcpTransport({ServerFlags.ReuseAddr}) .withTcpTransport({ServerFlags.ReuseAddr})
.build() .build()
let cache = var
if config.cacheSize > 0: cache: CacheStore
CacheStore.new(cacheSize = config.cacheSize * MiB)
else: if config.cacheSize > 0:
CacheStore.new() cache = CacheStore.new(cacheSize = config.cacheSize * MiB)
let let
discoveryBootstrapNodes = config.bootstrapNodes discoveryBootstrapNodes = config.bootstrapNodes

View File

@ -70,17 +70,21 @@ method getBlock*(
## Save a copy to the cache if present in the database but not in the cache ## Save a copy to the cache if present in the database but not in the cache
## ##
trace "Getting block from cache or database", cid if not self.cache.isNil:
trace "Getting block from cache or database", cid
else:
trace "Getting block from database", cid
if cid.isEmpty: if cid.isEmpty:
trace "Empty block, ignoring" trace "Empty block, ignoring"
return success cid.emptyBlock.some return success cid.emptyBlock.some
without cachedBlkOpt =? await self.cache.getBlock(cid), error: if not self.cache.isNil:
trace "Unable to read block from cache", cid, error = error.msg without cachedBlkOpt =? await self.cache.getBlock(cid), error:
trace "Unable to read block from cache", cid, error = error.msg
if cachedBlkOpt.isSome: if cachedBlkOpt.isSome:
return success cachedBlkOpt return success cachedBlkOpt
without blkKey =? blockKey(cid), error: without blkKey =? blockKey(cid), error:
return failure error return failure error
@ -96,11 +100,12 @@ method getBlock*(
trace "Unable to construct block from data", cid, error = error.msg trace "Unable to construct block from data", cid, error = error.msg
return failure error return failure error
let if not self.cache.isNil:
putCachedRes = await self.cache.putBlock(blk) let
putCachedRes = await self.cache.putBlock(blk)
if putCachedRes.isErr: if putCachedRes.isErr:
trace "Unable to store block in cache", cid, error = putCachedRes.error.msg trace "Unable to store block in cache", cid, error = putCachedRes.error.msg
return success blk.some return success blk.some
@ -111,7 +116,10 @@ method putBlock*(
## Save a copy to the cache ## Save a copy to the cache
## ##
trace "Putting block into database and cache", cid = blk.cid if not self.cache.isNil:
trace "Putting block into database and cache", cid = blk.cid
else:
trace "Putting block into database", cid = blk.cid
if blk.isEmpty: if blk.isEmpty:
trace "Empty block, ignoring" trace "Empty block, ignoring"
@ -127,11 +135,12 @@ method putBlock*(
trace "Unable to store block in database", key = blkKey.id, error = putRes.error.msg trace "Unable to store block in database", key = blkKey.id, error = putRes.error.msg
return failure putRes.error return failure putRes.error
let if not self.cache.isNil:
putCachedRes = await self.cache.putBlock(blk) let
putCachedRes = await self.cache.putBlock(blk)
if putCachedRes.isErr: if putCachedRes.isErr:
trace "Unable to store block in cache", cid = blk.cid, error = putCachedRes.error.msg trace "Unable to store block in cache", cid = blk.cid, error = putCachedRes.error.msg
return success() return success()
@ -141,17 +150,21 @@ method delBlock*(
## Delete a block from the database and cache ## Delete a block from the database and cache
## ##
trace "Deleting block from cache and database", cid if not self.cache.isNil:
trace "Deleting block from cache and database", cid
else:
trace "Deleting block from database", cid
if cid.isEmpty: if cid.isEmpty:
trace "Empty block, ignoring" trace "Empty block, ignoring"
return success() return success()
let if not self.cache.isNil:
delCachedRes = await self.cache.delBlock(cid) let
delCachedRes = await self.cache.delBlock(cid)
if delCachedRes.isErr: if delCachedRes.isErr:
trace "Unable to delete block from cache", cid, error = delCachedRes.error.msg trace "Unable to delete block from cache", cid, error = delCachedRes.error.msg
without blkKey =? blockKey(cid), error: without blkKey =? blockKey(cid), error:
return failure error return failure error

View File

@ -15,211 +15,220 @@ import pkg/codex/stores
import ../helpers import ../helpers
suite "SQLite Store": proc runSuite(cache: bool) =
randomize() suite "SQLite Store " & (if cache: "(cache enabled)" else: "(cache disabled)"):
randomize()
var
store: SQLiteStore
let
repoDir = getAppDir() / "repo"
proc randomBlock(): bt.Block =
let
blockRes = bt.Block.new(($genOid()).toBytes)
require(blockRes.isOk)
blockRes.get
var
newBlock: bt.Block
setup:
removeDir(repoDir)
require(not dirExists(repoDir))
store = SQLiteStore.new(repoDir)
newBlock = randomBlock()
teardown:
if not store.isNil: await store.close
store = nil
removeDir(repoDir)
require(not dirExists(repoDir))
test "putBlock":
let
blkKeyRes = blockKey(newBlock.cid)
assert blkKeyRes.isOk
let
blkKey = blkKeyRes.get
var var
# bypass cache store: SQLiteStore
let
repoDir = getAppDir() / "repo"
proc randomBlock(): bt.Block =
let
blockRes = bt.Block.new(($genOid()).toBytes)
require(blockRes.isOk)
blockRes.get
var
newBlock: bt.Block
setup:
removeDir(repoDir)
require(not dirExists(repoDir))
if cache:
store = SQLiteStore.new(repoDir)
else:
store = SQLiteStore.new(repoDir, nil)
newBlock = randomBlock()
teardown:
if not store.isNil: await store.close
store = nil
removeDir(repoDir)
require(not dirExists(repoDir))
test "putBlock":
let
blkKeyRes = blockKey(newBlock.cid)
assert blkKeyRes.isOk
let
blkKey = blkKeyRes.get
var
# bypass enabled cache
containsRes = await store.datastore.contains(blkKey)
assert containsRes.isOk
assert not containsRes.get
let
putRes = await store.putBlock(newBlock)
check: putRes.isOk
# bypass enabled cache
containsRes = await store.datastore.contains(blkKey) containsRes = await store.datastore.contains(blkKey)
assert containsRes.isOk assert containsRes.isOk
assert not containsRes.get
let check: containsRes.get
putRes = await store.putBlock(newBlock)
check: putRes.isOk test "getBlock":
var
r = rand(100)
# bypass cache # put `r` number of random blocks before putting newBlock
containsRes = await store.datastore.contains(blkKey) if r > 0:
for _ in 0..r:
let
b = randomBlock()
kRes = blockKey(b.cid)
assert containsRes.isOk assert kRes.isOk
check: containsRes.get let
# bypass enabled cache
pRes = await store.datastore.put(kRes.get, b.data)
assert pRes.isOk
let
blkKeyRes = blockKey(newBlock.cid)
assert blkKeyRes.isOk
var
# bypass enabled cache
putRes = await store.datastore.put(blkKeyRes.get, newBlock.data)
assert putRes.isOk
test "getBlock":
var
r = rand(100) r = rand(100)
# put `r` number of random blocks before putting newBlock # put `r` number of random blocks after putting newBlock
if r > 0: if r > 0:
for _ in 0..r: for _ in 0..r:
let let
b = randomBlock() b = randomBlock()
kRes = blockKey(b.cid) kRes = blockKey(b.cid)
assert kRes.isOk assert kRes.isOk
let let
# bypass cache # bypass enabled cache
pRes = await store.datastore.put(kRes.get, b.data) pRes = await store.datastore.put(kRes.get, b.data)
assert pRes.isOk assert pRes.isOk
let var
blkKeyRes = blockKey(newBlock.cid) # get from database
getRes = await store.getBlock(newBlock.cid)
assert blkKeyRes.isOk check: getRes.isOk
var var
# bypass cache blkOpt = getRes.get
putRes = await store.datastore.put(blkKeyRes.get, newBlock.data)
assert putRes.isOk check:
blkOpt.isSome
blkOpt.get == newBlock
r = rand(100) # get from enabled cache
# put `r` number of random blocks after putting newBlock
if r > 0:
for _ in 0..r:
let
b = randomBlock()
kRes = blockKey(b.cid)
assert kRes.isOk
let
# bypass cache
pRes = await store.datastore.put(kRes.get, b.data)
assert pRes.isOk
var
# get from database
getRes = await store.getBlock(newBlock.cid) getRes = await store.getBlock(newBlock.cid)
check: getRes.isOk check: getRes.isOk
var
blkOpt = getRes.get blkOpt = getRes.get
check: check:
blkOpt.isSome blkOpt.isSome
blkOpt.get == newBlock blkOpt.get == newBlock
# get from cache test "fail getBlock":
getRes = await store.getBlock(newBlock.cid)
check: getRes.isOk
blkOpt = getRes.get
check:
blkOpt.isSome
blkOpt.get == newBlock
test "fail getBlock":
let
getRes = await store.getBlock(newBlock.cid)
assert getRes.isOk
let
blkOpt = getRes.get
check: blkOpt.isNone
test "hasBlock":
let
putRes = await store.putBlock(newBlock)
assert putRes.isOk
let
hasRes = await store.hasBlock(newBlock.cid)
check:
hasRes.isOk
hasRes.get
await newBlock.cid in store
test "fail hasBlock":
let
hasRes = await store.hasBlock(newBlock.cid)
check:
hasRes.isOk
not hasRes.get
not (await newBlock.cid in store)
test "listBlocks":
var
newBlocks: seq[bt.Block]
for _ in 0..99:
let let
b = randomBlock() getRes = await store.getBlock(newBlock.cid)
pRes = await store.putBlock(b)
assert pRes.isOk assert getRes.isOk
newBlocks.add(b) let
blkOpt = getRes.get
var check: blkOpt.isNone
called = 0
cids = toHashSet(newBlocks.mapIt(it.cid))
let
onBlock = proc(cid: Cid) {.async, gcsafe.} =
check: cid in cids
if cid in cids:
inc called
cids.excl(cid)
listRes = await store.listBlocks(onBlock) test "hasBlock":
let
putRes = await store.putBlock(newBlock)
check: assert putRes.isOk
listRes.isOk
called == newBlocks.len
test "delBlock": let
let hasRes = await store.hasBlock(newBlock.cid)
putRes = await store.putBlock(newBlock)
assert putRes.isOk check:
assert (await newBlock.cid in store) hasRes.isOk
hasRes.get
await newBlock.cid in store
let test "fail hasBlock":
delRes = await store.delBlock(newBlock.cid) let
hasRes = await store.hasBlock(newBlock.cid)
check: check:
delRes.isOk hasRes.isOk
not (await newBlock.cid in store) not hasRes.get
not (await newBlock.cid in store)
test "listBlocks":
var
newBlocks: seq[bt.Block]
for _ in 0..99:
let
b = randomBlock()
pRes = await store.putBlock(b)
assert pRes.isOk
newBlocks.add(b)
var
called = 0
cids = toHashSet(newBlocks.mapIt(it.cid))
let
onBlock = proc(cid: Cid) {.async, gcsafe.} =
check: cid in cids
if cid in cids:
inc called
cids.excl(cid)
listRes = await store.listBlocks(onBlock)
check:
listRes.isOk
called == newBlocks.len
test "delBlock":
let
putRes = await store.putBlock(newBlock)
assert putRes.isOk
assert (await newBlock.cid in store)
let
delRes = await store.delBlock(newBlock.cid)
check:
delRes.isOk
not (await newBlock.cid in store)
runSuite(cache = true)
runSuite(cache = false)