mirror of
https://github.com/logos-storage/logos-storage-nim.git
synced 2026-01-05 23:13:09 +00:00
177 lines
5.1 KiB
Nim
177 lines
5.1 KiB
Nim
|
|
## Nim-Dagger
|
||
|
|
## 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.
|
||
|
|
|
||
|
|
{.push raises: [Defect].}
|
||
|
|
|
||
|
|
import std/sequtils
|
||
|
|
import std/sugar
|
||
|
|
|
||
|
|
import pkg/chronicles
|
||
|
|
import pkg/chronos
|
||
|
|
import pkg/questionable
|
||
|
|
import pkg/questionable/results
|
||
|
|
|
||
|
|
import ./blockstore
|
||
|
|
|
||
|
|
logScope:
|
||
|
|
topics = "dagger blockstoremanager"
|
||
|
|
|
||
|
|
type
|
||
|
|
BlockStoreManager* = ref object
|
||
|
|
stores: seq[BlockStore]
|
||
|
|
|
||
|
|
method getBlock*(
|
||
|
|
self: BlockStoreManager,
|
||
|
|
cid: Cid): Future[?!Block] {.async.} =
|
||
|
|
## Cycle through stores, in order of insertion, to get a block.
|
||
|
|
## Cycling short circuits once a block is found.
|
||
|
|
## In practice, this should query from most local to most remote, eg:
|
||
|
|
## MemoryStore > FSStore
|
||
|
|
##
|
||
|
|
|
||
|
|
for store in self.stores:
|
||
|
|
logScope:
|
||
|
|
cid
|
||
|
|
store = $(typeof store)
|
||
|
|
trace "Getting block"
|
||
|
|
let blk = await store.getBlock(cid)
|
||
|
|
if blk.isOk:
|
||
|
|
trace "Retrieved block from store"
|
||
|
|
return blk
|
||
|
|
else:
|
||
|
|
trace "Couldn't get from store"
|
||
|
|
|
||
|
|
return Block.failure("Couldn't find block in any stores")
|
||
|
|
|
||
|
|
method getBlocks*(
|
||
|
|
self: BlockStoreManager,
|
||
|
|
cids: seq[Cid]): Future[seq[Block]] {.async.} =
|
||
|
|
## Gets blocks from each local store in the BlockStoreManager.
|
||
|
|
## Cycle through local stores, in order of insertion, to get a block.
|
||
|
|
## In practice, this should query from most local to least local, eg:
|
||
|
|
## MemoryStore > FSStore
|
||
|
|
## Each block request stops cycling BlockStores once a block is found.
|
||
|
|
##
|
||
|
|
|
||
|
|
let getFuts = await allFinished(cids.map(cid => self.getBlock(cid)))
|
||
|
|
return getFuts
|
||
|
|
.filterIt((not it.failed) and it.read.isOk)
|
||
|
|
.mapIt(!it.read) # extract Block value
|
||
|
|
|
||
|
|
method putBlock*(
|
||
|
|
self: BlockStoreManager,
|
||
|
|
blk: Block): Future[bool] {.async.} =
|
||
|
|
## Put a block to each local store in the BlockStoreManager.
|
||
|
|
## Cycle through local stores, in order of insertion, to put a block.
|
||
|
|
## In practice, this should query from most local to least local, eg:
|
||
|
|
## MemoryStore > FSStore
|
||
|
|
##
|
||
|
|
|
||
|
|
var success = true
|
||
|
|
for store in self.stores:
|
||
|
|
logScope:
|
||
|
|
cid = blk.cid
|
||
|
|
store = $(typeof store)
|
||
|
|
trace "Putting block in store"
|
||
|
|
# TODO: Could we use asyncSpawn here as we likely don't need to check
|
||
|
|
# if putBlock failed or not (think in terms of a network-based storage
|
||
|
|
# where an operation may take a long time)?
|
||
|
|
var storeSuccess = await store.putBlock(blk)
|
||
|
|
if not storeSuccess:
|
||
|
|
trace "Couldn't put block in store"
|
||
|
|
|
||
|
|
# allow the operation to fail without affecting the return value
|
||
|
|
# (ie which indicatees if the put operation was successful or not)
|
||
|
|
if store.canFail:
|
||
|
|
storeSuccess = true
|
||
|
|
|
||
|
|
if not store.onPutFail.isNil:
|
||
|
|
asyncSpawn store.onPutFail(store, blk)
|
||
|
|
|
||
|
|
else: trace "Put block in store"
|
||
|
|
success = success and storeSuccess
|
||
|
|
|
||
|
|
return success
|
||
|
|
|
||
|
|
method putBlocks*(
|
||
|
|
self: BlockStoreManager,
|
||
|
|
blks: seq[Block]): Future[bool] {.async.} =
|
||
|
|
## Put blocks to each local store in the BlockStoreManager.
|
||
|
|
## Cycle through local stores, in order of insertion, to put a block.
|
||
|
|
## In practice, this should query from most local to least local, eg:
|
||
|
|
## MemoryStore > FSStore
|
||
|
|
##
|
||
|
|
|
||
|
|
let
|
||
|
|
putFuts = await allFinished(blks.map(blk => self.putBlock(blk)))
|
||
|
|
success = putFuts.allIt(not it.failed and it.read) # extract bool value
|
||
|
|
|
||
|
|
return success
|
||
|
|
|
||
|
|
method delBlock*(
|
||
|
|
self: BlockStoreManager,
|
||
|
|
cid: Cid): Future[bool] {.async.} =
|
||
|
|
## Delete a block from each local block store in the BlockStoreManager.
|
||
|
|
## Cycle through local stores, in order of insertion, to delete a block.
|
||
|
|
## In practice, this should query from most local to least local, eg:
|
||
|
|
## MemoryStore > FSStore
|
||
|
|
##
|
||
|
|
|
||
|
|
var success = true
|
||
|
|
for store in self.stores:
|
||
|
|
logScope:
|
||
|
|
cid
|
||
|
|
store = $(typeof store)
|
||
|
|
trace "Deleting block from store"
|
||
|
|
# TODO: Could we use asyncSpawn here as we likely don't need to check
|
||
|
|
# if deletion failed or not?
|
||
|
|
var storeSuccess = await store.delBlock(cid)
|
||
|
|
if not storeSuccess:
|
||
|
|
trace "Couldn't delete from store"
|
||
|
|
|
||
|
|
# allow the operation to fail without affecting the return value
|
||
|
|
# (ie which indicatees if the put operation was successful or not)
|
||
|
|
if store.canFail:
|
||
|
|
storeSuccess = true
|
||
|
|
|
||
|
|
if not store.onDelFail.isNil:
|
||
|
|
asyncSpawn store.onDelFail(store, cid)
|
||
|
|
|
||
|
|
else: trace "Deleted block from store"
|
||
|
|
success = success and storeSuccess
|
||
|
|
|
||
|
|
return success
|
||
|
|
|
||
|
|
method hasBlock*(self: BlockStoreManager, cid: Cid): bool =
|
||
|
|
## Check if the block exists in the BlockStoreManager
|
||
|
|
##
|
||
|
|
|
||
|
|
for store in self.stores:
|
||
|
|
logScope:
|
||
|
|
cid
|
||
|
|
store = $(typeof store)
|
||
|
|
|
||
|
|
trace "Checking has block"
|
||
|
|
if store.hasBlock(cid):
|
||
|
|
trace "Store has block"
|
||
|
|
return true
|
||
|
|
else:
|
||
|
|
trace "Store doesn't have block"
|
||
|
|
|
||
|
|
method contains*(self: BlockStoreManager, blk: Cid): bool =
|
||
|
|
self.hasBlock(blk)
|
||
|
|
|
||
|
|
proc new*(
|
||
|
|
T: type BlockStoreManager,
|
||
|
|
stores: seq[BlockStore]): T =
|
||
|
|
|
||
|
|
let b = BlockStoreManager(
|
||
|
|
stores: stores)
|
||
|
|
|
||
|
|
return b
|