nim-codex/codex/stores/memorystore.nim

187 lines
4.7 KiB
Nim
Raw Normal View History

2023-03-13 12:05:02 +00:00
## 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
2023-03-13 15:16:45 +00:00
import std/lists
2023-03-13 12:05:02 +00:00
import pkg/chronicles
import pkg/chronos
import pkg/libp2p
import pkg/questionable
import pkg/questionable/results
import ./blockstore
2023-03-14 09:46:33 +00:00
import ../consts
2023-03-13 12:05:02 +00:00
import ../chunker
import ../manifest
export blockstore
logScope:
topics = "codex memorystore"
type
2023-03-13 15:16:45 +00:00
MemoryStoreNode = ref object
key: Cid
val: Block
2023-03-13 12:05:02 +00:00
MemoryStore* = ref object of BlockStore
bytesUsed*: int
capacity*: int
2023-03-13 15:16:45 +00:00
table: Table[Cid, DoublyLinkedNode[MemoryStoreNode]]
list: DoublyLinkedList[MemoryStoreNode]
2023-03-13 12:05:02 +00:00
const
DefaultMemoryStoreCapacityMiB* = 5
DefaultMemoryStoreCapacity* = DefaultMemoryStoreCapacityMiB * MiB
2023-03-13 12:05:02 +00:00
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")
2023-03-15 09:57:17 +00:00
return success self.table[cid].value.val
2023-03-13 12:05:02 +00:00
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.}) =
2023-03-13 15:16:45 +00:00
var it = self.list.head
2023-03-13 12:05:02 +00:00
return iterator(): Cid =
2023-03-13 15:16:45 +00:00
while not isNil(it):
yield it.value.key
it = it.next
2023-03-13 12:05:02 +00:00
2023-03-15 09:57:17 +00:00
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)
2023-03-13 12:05:02 +00:00
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
2023-03-15 09:57:17 +00:00
without isCorrectBlockType =? isOfBlockType(cid, blockType), err:
warn "Error checking if cid of blocktype", err = err.msg
2023-03-13 12:05:02 +00:00
return Cid.none
2023-03-15 09:57:17 +00:00
if not isCorrectBlockType:
trace "Cid does not match blocktype, skipping", cid, blockType
continue
2023-03-13 12:05:02 +00:00
2023-03-15 09:57:17 +00:00
return cid.some
2023-03-13 12:05:02 +00:00
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.")
2023-03-13 12:05:02 +00:00
2023-03-13 15:16:45 +00:00
let node = newDoublyLinkedNode[MemoryStoreNode](MemoryStoreNode(key: blk.cid, val: blk))
self.list.prepend(node)
self.table[blk.cid] = node
2023-03-13 12:05:02 +00:00
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()
2023-03-13 15:16:45 +00:00
if cid notin self.table:
2023-03-15 09:57:17 +00:00
return success()
2023-03-13 15:16:45 +00:00
let nodeToRemove = self.table[cid]
2023-03-13 12:05:02 +00:00
self.table.del(cid)
2023-03-13 15:16:45 +00:00
self.list.remove(nodeToRemove)
self.bytesUsed -= nodeToRemove.value.val.data.len
2023-03-13 12:05:02 +00:00
return success()
method close*(self: MemoryStore): Future[void] {.async.} =
discard
func new*(
_: type MemoryStore,
blocks: openArray[Block] = [],
capacity: Positive = DefaultMemoryStoreCapacity,
2023-03-13 12:05:02 +00:00
): MemoryStore {.raises: [Defect, ValueError].} =
let store = MemoryStore(
2023-03-13 15:16:45 +00:00
table: initTable[Cid, DoublyLinkedNode[MemoryStoreNode]](),
list: initDoublyLinkedList[MemoryStoreNode](),
2023-03-13 12:05:02 +00:00
bytesUsed: 0,
capacity: capacity)
2023-03-13 12:29:07 +00:00
for blk in blocks:
discard store.putBlockSync(blk)
2023-03-13 12:05:02 +00:00
return store