refactor: move expiry update from fetchBatched (#634)
This commit is contained in:
parent
22c31046a7
commit
0e4387d1b3
|
@ -8,6 +8,8 @@
|
||||||
## those terms.
|
## those terms.
|
||||||
|
|
||||||
import pkg/stew/results
|
import pkg/stew/results
|
||||||
|
import pkg/chronos
|
||||||
|
import pkg/questionable/results
|
||||||
|
|
||||||
export results
|
export results
|
||||||
|
|
||||||
|
@ -26,3 +28,13 @@ template mapFailure*[T, V, E](
|
||||||
|
|
||||||
template mapFailure*[T, V](exp: Result[T, V]): Result[T, ref CatchableError] =
|
template mapFailure*[T, V](exp: Result[T, V]): Result[T, ref CatchableError] =
|
||||||
mapFailure(exp, CodexError)
|
mapFailure(exp, CodexError)
|
||||||
|
|
||||||
|
proc allFutureResult*[T](fut: seq[Future[T]]): Future[?!void] {.async.} =
|
||||||
|
try:
|
||||||
|
await allFuturesThrowing(fut)
|
||||||
|
except CancelledError as exc:
|
||||||
|
raise exc
|
||||||
|
except CatchableError as exc:
|
||||||
|
return failure(exc.msg)
|
||||||
|
|
||||||
|
return success()
|
||||||
|
|
|
@ -38,6 +38,7 @@ import ./discovery
|
||||||
import ./contracts
|
import ./contracts
|
||||||
import ./node/batch
|
import ./node/batch
|
||||||
import ./utils
|
import ./utils
|
||||||
|
import ./errors
|
||||||
|
|
||||||
export batch
|
export batch
|
||||||
|
|
||||||
|
@ -126,8 +127,7 @@ proc fetchBatched*(
|
||||||
node: CodexNodeRef,
|
node: CodexNodeRef,
|
||||||
manifest: Manifest,
|
manifest: Manifest,
|
||||||
batchSize = FetchBatch,
|
batchSize = FetchBatch,
|
||||||
onBatch: BatchProc = nil,
|
onBatch: BatchProc = nil): Future[?!void] {.async, gcsafe.} =
|
||||||
expiry = SecondsSince1970.none): Future[?!void] {.async, gcsafe.} =
|
|
||||||
## Fetch manifest in batches of `batchSize`
|
## Fetch manifest in batches of `batchSize`
|
||||||
##
|
##
|
||||||
|
|
||||||
|
@ -144,18 +144,11 @@ proc fetchBatched*(
|
||||||
if not iter.finished:
|
if not iter.finished:
|
||||||
iter.next()
|
iter.next()
|
||||||
|
|
||||||
try:
|
if blocksErr =? (await allFutureResult(blocks)).errorOption:
|
||||||
await allFuturesThrowing(allFinished(blocks))
|
return failure(blocksErr)
|
||||||
|
|
||||||
if expiryValue =? expiry:
|
if not onBatch.isNil and batchErr =? (await onBatch(blocks.mapIt( it.read.get ))).errorOption:
|
||||||
await allFuturesThrowing(blocks.mapIt(node.blockStore.ensureExpiry(it.read.get.cid, expiryValue)))
|
return failure(batchErr)
|
||||||
|
|
||||||
if not onBatch.isNil:
|
|
||||||
await onBatch(blocks.mapIt( it.read.get ))
|
|
||||||
except CancelledError as exc:
|
|
||||||
raise exc
|
|
||||||
except CatchableError as exc:
|
|
||||||
return failure(exc.msg)
|
|
||||||
|
|
||||||
return success()
|
return success()
|
||||||
|
|
||||||
|
@ -434,11 +427,18 @@ proc start*(node: CodexNodeRef) {.async.} =
|
||||||
return failure(error)
|
return failure(error)
|
||||||
|
|
||||||
trace "Fetching block for manifest", cid
|
trace "Fetching block for manifest", cid
|
||||||
# TODO: This will probably require a call to `getBlock` either way,
|
let expiry = request.expiry.toSecondsSince1970
|
||||||
# since fetching of blocks will have to be selective according
|
proc expiryUpdateOnBatch(blocks: seq[bt.Block]): Future[?!void] {.async.} =
|
||||||
# to a combination of parameters, such as node slot position
|
let ensureExpiryFutures = blocks.mapIt(node.blockStore.ensureExpiry(it.cid, expiry))
|
||||||
# and dataset geometry
|
if updateExpiryErr =? (await allFutureResult(ensureExpiryFutures)).errorOption:
|
||||||
if fetchErr =? (await node.fetchBatched(manifest, onBatch = onBatch, expiry = some request.expiry.toSecondsSince1970)).errorOption:
|
return failure(updateExpiryErr)
|
||||||
|
|
||||||
|
if not onBatch.isNil and onBatchErr =? (await onBatch(blocks)).errorOption:
|
||||||
|
return failure(onBatchErr)
|
||||||
|
|
||||||
|
return success()
|
||||||
|
|
||||||
|
if fetchErr =? (await node.fetchBatched(manifest, onBatch = expiryUpdateOnBatch)).errorOption:
|
||||||
let error = newException(CodexError, "Unable to retrieve blocks")
|
let error = newException(CodexError, "Unable to retrieve blocks")
|
||||||
error.parent = fetchErr
|
error.parent = fetchErr
|
||||||
return failure(error)
|
return failure(error)
|
||||||
|
@ -465,7 +465,7 @@ proc start*(node: CodexNodeRef) {.async.} =
|
||||||
try:
|
try:
|
||||||
await hostContracts.start()
|
await hostContracts.start()
|
||||||
except CatchableError as error:
|
except CatchableError as error:
|
||||||
error "Unable to start host contract interactions: ", error=error.msg
|
error "Unable to start host contract interactions", error=error.msg
|
||||||
node.contracts.host = HostInteractions.none
|
node.contracts.host = HostInteractions.none
|
||||||
|
|
||||||
if clientContracts =? node.contracts.client:
|
if clientContracts =? node.contracts.client:
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import pkg/chronos
|
import pkg/chronos
|
||||||
|
import pkg/questionable/results
|
||||||
import pkg/upraises
|
import pkg/upraises
|
||||||
import ../blocktype as bt
|
import ../blocktype as bt
|
||||||
|
|
||||||
type
|
type
|
||||||
BatchProc* = proc(blocks: seq[bt.Block]): Future[void] {.gcsafe, upraises:[].}
|
BatchProc* = proc(blocks: seq[bt.Block]): Future[?!void] {.gcsafe, upraises:[].}
|
||||||
|
|
|
@ -51,7 +51,7 @@ method run*(state: SaleDownloading, machine: Machine): Future[?State] {.async.}
|
||||||
reservationId = reservation.id
|
reservationId = reservation.id
|
||||||
availabilityId = reservation.availabilityId
|
availabilityId = reservation.availabilityId
|
||||||
|
|
||||||
proc onBatch(blocks: seq[bt.Block]) {.async.} =
|
proc onBatch(blocks: seq[bt.Block]): Future[?!void] {.async.} =
|
||||||
# release batches of blocks as they are written to disk and
|
# release batches of blocks as they are written to disk and
|
||||||
# update availability size
|
# update availability size
|
||||||
var bytes: uint = 0
|
var bytes: uint = 0
|
||||||
|
@ -59,13 +59,9 @@ method run*(state: SaleDownloading, machine: Machine): Future[?State] {.async.}
|
||||||
bytes += blk.data.len.uint
|
bytes += blk.data.len.uint
|
||||||
|
|
||||||
trace "Releasing batch of bytes written to disk", bytes
|
trace "Releasing batch of bytes written to disk", bytes
|
||||||
let r = await reservations.release(reservation.id,
|
return await reservations.release(reservation.id,
|
||||||
reservation.availabilityId,
|
reservation.availabilityId,
|
||||||
bytes)
|
bytes)
|
||||||
# `tryGet` will raise the exception that occurred during release, if there
|
|
||||||
# was one. The exception will be caught in the closure and sent to the
|
|
||||||
# SaleErrored state.
|
|
||||||
r.tryGet()
|
|
||||||
|
|
||||||
trace "Starting download"
|
trace "Starting download"
|
||||||
if err =? (await onStore(request,
|
if err =? (await onStore(request,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import std/os
|
import std/os
|
||||||
import std/options
|
import std/options
|
||||||
import std/math
|
import std/math
|
||||||
|
import std/times
|
||||||
|
|
||||||
import pkg/asynctest
|
import pkg/asynctest
|
||||||
import pkg/chronos
|
import pkg/chronos
|
||||||
|
@ -8,6 +9,7 @@ import pkg/chronicles
|
||||||
import pkg/stew/byteutils
|
import pkg/stew/byteutils
|
||||||
import pkg/datastore
|
import pkg/datastore
|
||||||
import pkg/questionable
|
import pkg/questionable
|
||||||
|
import pkg/questionable/results
|
||||||
import pkg/stint
|
import pkg/stint
|
||||||
|
|
||||||
import pkg/nitro
|
import pkg/nitro
|
||||||
|
@ -29,6 +31,9 @@ import ./helpers
|
||||||
import ./helpers/mockmarket
|
import ./helpers/mockmarket
|
||||||
import ./helpers/mockclock
|
import ./helpers/mockclock
|
||||||
|
|
||||||
|
proc toTimesDuration(d: chronos.Duration): times.Duration =
|
||||||
|
initDuration(seconds=d.seconds)
|
||||||
|
|
||||||
asyncchecksuite "Test Node":
|
asyncchecksuite "Test Node":
|
||||||
let
|
let
|
||||||
(path, _, _) = instantiationInfo(-2, fullPaths = true) # get this file's name
|
(path, _, _) = instantiationInfo(-2, fullPaths = true) # get this file's name
|
||||||
|
@ -129,25 +134,11 @@ asyncchecksuite "Test Node":
|
||||||
(await node.fetchBatched(
|
(await node.fetchBatched(
|
||||||
manifest,
|
manifest,
|
||||||
batchSize = batchSize,
|
batchSize = batchSize,
|
||||||
proc(blocks: seq[bt.Block]) {.gcsafe, async.} =
|
proc(blocks: seq[bt.Block]): Future[?!void] {.gcsafe, async.} =
|
||||||
check blocks.len > 0 and blocks.len <= batchSize
|
check blocks.len > 0 and blocks.len <= batchSize
|
||||||
|
return success()
|
||||||
)).tryGet()
|
)).tryGet()
|
||||||
|
|
||||||
test "Block Batching with expiry":
|
|
||||||
let
|
|
||||||
manifest = await Manifest.fetch(chunker)
|
|
||||||
# The blocks have set default TTL, so in order to update it we have to have larger TTL
|
|
||||||
expectedExpiry: SecondsSince1970 = clock.now + DefaultBlockTtl.seconds + 123
|
|
||||||
|
|
||||||
(await node.fetchBatched(manifest, expiry=some expectedExpiry)).tryGet()
|
|
||||||
|
|
||||||
for index in 0..<manifest.blocksCount:
|
|
||||||
let blk = (await localStore.getBlock(manifest.treeCid, index)).tryGet
|
|
||||||
let expiryKey = (createBlockExpirationMetadataKey(blk.cid)).tryGet
|
|
||||||
let expiry = await localStoreMetaDs.get(expiryKey)
|
|
||||||
|
|
||||||
check (expiry.tryGet).toSecondsSince1970 == expectedExpiry
|
|
||||||
|
|
||||||
test "Store and retrieve Data Stream":
|
test "Store and retrieve Data Stream":
|
||||||
let
|
let
|
||||||
stream = BufferStream.new()
|
stream = BufferStream.new()
|
||||||
|
@ -252,7 +243,7 @@ asyncchecksuite "Test Node - host contracts":
|
||||||
|
|
||||||
# Setup Host Contracts and dependencies
|
# Setup Host Contracts and dependencies
|
||||||
let market = MockMarket.new()
|
let market = MockMarket.new()
|
||||||
sales = Sales.new(market, clock, localStore, 0)
|
sales = Sales.new(market, clock, localStore)
|
||||||
let hostContracts = some HostInteractions.new(clock, sales)
|
let hostContracts = some HostInteractions.new(clock, sales)
|
||||||
node.contracts = (ClientInteractions.none, hostContracts, ValidatorInteractions.none)
|
node.contracts = (ClientInteractions.none, hostContracts, ValidatorInteractions.none)
|
||||||
|
|
||||||
|
@ -296,13 +287,20 @@ asyncchecksuite "Test Node - host contracts":
|
||||||
let onStore = !sales.onStore
|
let onStore = !sales.onStore
|
||||||
var request = StorageRequest.example
|
var request = StorageRequest.example
|
||||||
request.content.cid = manifestCid
|
request.content.cid = manifestCid
|
||||||
|
request.expiry = (getTime() + DefaultBlockTtl.toTimesDuration + 1.hours).toUnix.u256
|
||||||
var fetchedBytes: uint = 0
|
var fetchedBytes: uint = 0
|
||||||
|
|
||||||
let onBatch = proc(blocks: seq[bt.Block]) {.async.} =
|
let onBatch = proc(blocks: seq[bt.Block]): Future[?!void] {.async.} =
|
||||||
for blk in blocks:
|
for blk in blocks:
|
||||||
fetchedBytes += blk.data.len.uint
|
fetchedBytes += blk.data.len.uint
|
||||||
|
return success()
|
||||||
|
|
||||||
(await onStore(request, 0.u256, onBatch)).tryGet()
|
(await onStore(request, 0.u256, onBatch)).tryGet()
|
||||||
|
|
||||||
check fetchedBytes == 2291520
|
check fetchedBytes == 2291520
|
||||||
|
|
||||||
|
for index in 0..<manifest.blocksCount:
|
||||||
|
let blk = (await localStore.getBlock(manifest.treeCid, index)).tryGet
|
||||||
|
let expiryKey = (createBlockExpirationMetadataKey(blk.cid)).tryGet
|
||||||
|
let expiry = await localStoreMetaDs.get(expiryKey)
|
||||||
|
|
||||||
|
check (expiry.tryGet).toSecondsSince1970 == request.expiry.toSecondsSince1970
|
||||||
|
|
|
@ -2,10 +2,12 @@ import std/random
|
||||||
import std/sequtils
|
import std/sequtils
|
||||||
import std/times
|
import std/times
|
||||||
import std/typetraits
|
import std/typetraits
|
||||||
|
|
||||||
import pkg/codex/contracts/requests
|
import pkg/codex/contracts/requests
|
||||||
import pkg/codex/sales/slotqueue
|
import pkg/codex/sales/slotqueue
|
||||||
import pkg/stint
|
import pkg/codex/stores
|
||||||
|
|
||||||
|
import pkg/stint
|
||||||
proc exampleString*(length: int): string =
|
proc exampleString*(length: int): string =
|
||||||
let chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
let chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||||
result = newString(length) # Create a new empty string with a given length
|
result = newString(length) # Create a new empty string with a given length
|
||||||
|
@ -46,7 +48,7 @@ proc example*(_: type StorageRequest): StorageRequest =
|
||||||
cid: "zb2rhheVmk3bLks5MgzTqyznLu1zqGH5jrfTA1eAZXrjx7Vob",
|
cid: "zb2rhheVmk3bLks5MgzTqyznLu1zqGH5jrfTA1eAZXrjx7Vob",
|
||||||
merkleRoot: array[32, byte].example
|
merkleRoot: array[32, byte].example
|
||||||
),
|
),
|
||||||
expiry: (getTime() + initDuration(hours=1)).toUnix.u256,
|
expiry: (getTime() + 1.hours).toUnix.u256,
|
||||||
nonce: Nonce.example
|
nonce: Nonce.example
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue