Eric Mastro 4a70312ee9
feat: introduce blockstore manager
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>
2022-02-08 13:20:09 +11:00

138 lines
3.2 KiB
Nim

import std/os
import std/options
import pkg/asynctest
import pkg/chronos
import pkg/stew/byteutils
import pkg/nitro
import pkg/libp2p
import pkg/dagger/stores
import pkg/dagger/blockexchange
import pkg/dagger/chunker
import pkg/dagger/node
import pkg/dagger/blocksmanifest
import pkg/dagger/blocktype as bt
import ./helpers
suite "Test Node":
let
(path, _, _) = instantiationInfo(-2, fullPaths = true) # get this file's name
var
file: File
chunker: Chunker
switch: Switch
wallet: WalletRef
network: BlockExcNetwork
localStore: BlockStore
engine: BlockExcEngine
blockStoreMgr: BlockStoreManager
node: DaggerNodeRef
setup:
file = open(path.splitFile().dir /../ "fixtures" / "test.jpg")
chunker = FileChunker.new(file = file)
switch = newStandardSwitch()
wallet = WalletRef.new(EthPrivateKey.random())
network = BlockExcNetwork.new(switch)
localStore = MemoryStore.new()
blockStoreMgr = BlockStoreManager.new(@[localStore])
engine = BlockExcEngine.new(wallet, network, blockStoreMgr)
node = DaggerNodeRef.new(switch, engine)
await node.start()
teardown:
close(file)
await node.stop()
test "Store Data Stream":
let
stream = BufferStream.new()
storeFut = node.store(stream)
var
manifest = BlocksManifest.init().tryGet()
try:
while (
let chunk = await chunker.getBytes();
chunk.len > 0):
await stream.pushData(chunk)
manifest.put(bt.Block.init(chunk).tryGet().cid)
finally:
await stream.pushEof()
await stream.close()
let
manifestCid = (await storeFut).tryGet()
check:
manifestCid in localStore
var
manifestBlock = (await localStore.getBlock(manifestCid)).tryGet()
localManifest = BlocksManifest.init(manifestBlock.data).tryGet()
check:
manifest.len == localManifest.len
manifest.cid == localManifest.cid
test "Retrieve Data Stream":
var
manifest = BlocksManifest.init().tryGet()
original: seq[byte]
while (
let chunk = await chunker.getBytes();
chunk.len > 0):
let
blk = bt.Block.init(chunk).tryGet()
original &= chunk
check await localStore.putBlock(blk)
manifest.put(blk.cid)
let
manifestBlock = bt.Block.init(
manifest.encode().tryGet(),
codec = ManifestCodec).tryGet()
check await localStore.putBlock(manifestBlock)
let stream = BufferStream.new()
check (await node.retrieve(stream, manifestBlock.cid)).isOk
var data: seq[byte]
while true:
var
buf = newSeq[byte](FileChunkSize)
res = await stream.readOnce(addr buf[0], buf.len)
if res <= 0:
break
buf.setLen(res)
data &= buf
check data == original
test "Retrieve One Block":
let
testString = "Block 1"
blk = bt.Block.init(testString.toBytes).tryGet()
var
stream = BufferStream.new()
check (await localStore.putBlock(blk))
check (await node.retrieve(stream, blk.cid)).isOk
var data = newSeq[byte](testString.len)
await stream.readExactly(addr data[0], data.len)
check string.fromBytes(data) == testString