nim-codex/codex/stores/memorystore.nim

187 lines
4.7 KiB
Nim

## Nim-Codex
## Copyright (c) 2021 Status Research & Development GmbH
## Licensed under either of
## * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
## * MIT license ([LICENSE-MIT](LICENSE-MIT))
## at your option.
## This file may not be copied, modified, or distributed except according to
## those terms.
import pkg/upraises
push: {.upraises: [].}
import std/options
import std/tables
import std/lists
import pkg/chronicles
import pkg/chronos
import pkg/libp2p
import pkg/questionable
import pkg/questionable/results
import ./blockstore
import ../consts
import ../chunker
import ../manifest
export blockstore
logScope:
topics = "codex memorystore"
type
MemoryStoreNode = ref object
key: Cid
val: Block
MemoryStore* = ref object of BlockStore
bytesUsed*: int
capacity*: int
table: Table[Cid, DoublyLinkedNode[MemoryStoreNode]]
list: DoublyLinkedList[MemoryStoreNode]
const
DefaultMemoryStoreCapacityMiB* = 5
DefaultMemoryStoreCapacity* = DefaultMemoryStoreCapacityMiB * MiB
method getBlock*(self: MemoryStore, cid: Cid): Future[?!Block] {.async.} =
trace "Getting block from cache", cid
if cid.isEmpty:
trace "Empty block, ignoring"
return success cid.emptyBlock
if cid notin self.table:
return failure (ref BlockNotFoundError)(msg: "Block not in memory store")
return success self.table[cid].value.val
method hasBlock*(self: MemoryStore, cid: Cid): Future[?!bool] {.async.} =
trace "Checking MemoryStore for block presence", cid
if cid.isEmpty:
trace "Empty block, ignoring"
return true.success
return (cid in self.table).success
func cids(self: MemoryStore): (iterator: Cid {.gcsafe.}) =
var it = self.list.head
return iterator(): Cid =
while not isNil(it):
yield it.value.key
it = it.next
proc isOfBlockType(cid: Cid, blockType: BlockType): ?!bool =
without isManifest =? cid.isManifest, err:
trace "Error checking if cid is a manifest", err = err.msg
return failure("Unable to determine if CID is a manifest")
case blockType:
of BlockType.Manifest:
return success(isManifest)
of BlockType.Block:
return success(not isManifest)
of BlockType.Both:
return success(true)
return failure("Unknown block type: " & $blockType)
method listBlocks*(self: MemoryStore, blockType = BlockType.Manifest): Future[?!BlocksIter] {.async.} =
var
iter = BlocksIter()
let
cids = self.cids()
proc next(): Future[?Cid] {.async.} =
await idleAsync()
var cid: Cid
while true:
if iter.finished:
return Cid.none
cid = cids()
if finished(cids):
iter.finished = true
return Cid.none
without isCorrectBlockType =? isOfBlockType(cid, blockType), err:
warn "Error checking if cid of blocktype", err = err.msg
return Cid.none
if not isCorrectBlockType:
trace "Cid does not match blocktype, skipping", cid, blockType
continue
return cid.some
iter.next = next
return success iter
proc getFreeCapacity(self: MemoryStore): int =
self.capacity - self.bytesUsed
func putBlockSync(self: MemoryStore, blk: Block): ?!void =
let
freeCapacity = self.getFreeCapacity()
blkSize = blk.data.len
if blkSize > freeCapacity:
trace "Block size is larger than free capacity", blk = blkSize, freeCapacity
return failure("Unable to store block: Insufficient free capacity.")
let node = newDoublyLinkedNode[MemoryStoreNode](MemoryStoreNode(key: blk.cid, val: blk))
self.list.prepend(node)
self.table[blk.cid] = node
self.bytesUsed += blkSize
return success()
method putBlock*(self: MemoryStore, blk: Block, ttl = Duration.none): Future[?!void] {.async.} =
trace "Storing block in store", cid = blk.cid
if blk.isEmpty:
trace "Empty block, ignoring"
return success()
return self.putBlockSync(blk)
method delBlock*(self: MemoryStore, cid: Cid): Future[?!void] {.async.} =
trace "Deleting block from memory store", cid
if cid.isEmpty:
trace "Empty block, ignoring"
return success()
if cid notin self.table:
return success()
let nodeToRemove = self.table[cid]
self.table.del(cid)
self.list.remove(nodeToRemove)
self.bytesUsed -= nodeToRemove.value.val.data.len
return success()
method close*(self: MemoryStore): Future[void] {.async.} =
discard
func new*(
_: type MemoryStore,
blocks: openArray[Block] = [],
capacity: Positive = DefaultMemoryStoreCapacity,
): MemoryStore {.raises: [Defect, ValueError].} =
let store = MemoryStore(
table: initTable[Cid, DoublyLinkedNode[MemoryStoreNode]](),
list: initDoublyLinkedList[MemoryStoreNode](),
bytesUsed: 0,
capacity: capacity)
for blk in blocks:
discard store.putBlockSync(blk)
return store