mirror of
https://github.com/logos-storage/logos-storage-nim.git
synced 2026-01-03 22:13:12 +00:00
Implement blockstore manager which executes block storage operations on its block stores, in the order to which they were added to the manager, typically in the order of most local (fastest, eg cache) to least local (slowest, eg filesystem or perhaps a network filesystem). As an example, given a `BlockStoreManager` instantiated with a `@[MemoryStore, FSStore]`, retrieving a block would first attempt to get from the `MemoryStore`, and if not found, attempt to get from the `FSStore`. Remove all dependencies on `BlockStores` (typically in the shape of `localstore`) and instead depend on `BlockStoreManager` via the `BlockExcEngine`. Modify the role of the `BlockExcEngine` to make a “local vs remote” decision on block access/storage. For all operations other than retrieving blocks, this means simply going to the `BlockStoreManager`. For retrieving blocks, however, this means going first to the `BlockStoreManager`, and then if not found, going to the Dagger network (via pending block and want/have lists). Remove `NetworkStore` as its two purposes were to defer block retrieval from a local store first, then go to the block exchange to requeest a block from the Dagger network. `BlockStoreManager` takes care of going to local storage first, and the block exchange engine handles going to Dagger network if retrieval from the store manager fails. ### Notes 1. Future work may want to consider breaking up `BlockExcEngine` further in to three modules: - `BlockExcEngine` (depends on `WantHave`, `DHT`) - `WantHave` - `DHT` (work is in progress) Co-authored-by: Michael Bradley <michaelsbradleyjr@gmail.com>
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 |