mirror of
https://github.com/logos-storage/nim-datastore.git
synced 2026-01-05 23:23:10 +00:00
handle error passing and conversion better
This commit is contained in:
parent
3d33820729
commit
0beb3d3c90
@ -6,6 +6,8 @@ import pkg/questionable/results
|
|||||||
|
|
||||||
import ./key
|
import ./key
|
||||||
import ./types
|
import ./types
|
||||||
|
|
||||||
|
export types
|
||||||
export options, SortOrder
|
export options, SortOrder
|
||||||
|
|
||||||
type
|
type
|
||||||
@ -17,7 +19,6 @@ type
|
|||||||
sort*: SortOrder # Sort order - not available in all backends
|
sort*: SortOrder # Sort order - not available in all backends
|
||||||
|
|
||||||
QueryResponse* = tuple[key: ?Key, data: seq[byte]]
|
QueryResponse* = tuple[key: ?Key, data: seq[byte]]
|
||||||
QueryEndedError* = object of DatastoreError
|
|
||||||
|
|
||||||
GetNext* = proc(): Future[?!QueryResponse] {.upraises: [], gcsafe.}
|
GetNext* = proc(): Future[?!QueryResponse] {.upraises: [], gcsafe.}
|
||||||
IterDispose* = proc(): Future[?!void] {.upraises: [], gcsafe.}
|
IterDispose* = proc(): Future[?!void] {.upraises: [], gcsafe.}
|
||||||
|
|||||||
@ -26,14 +26,14 @@ import ../datastore
|
|||||||
|
|
||||||
import ./asyncsemaphore
|
import ./asyncsemaphore
|
||||||
import ./databuffer
|
import ./databuffer
|
||||||
|
import ./threadresult
|
||||||
|
|
||||||
|
export threadresult
|
||||||
|
|
||||||
|
logScope:
|
||||||
|
topics = "datastore threadproxyds"
|
||||||
|
|
||||||
type
|
type
|
||||||
ErrorEnum {.pure.} = enum
|
|
||||||
DatastoreErr, DatastoreKeyNotFoundErr, CatchableErr
|
|
||||||
|
|
||||||
ThreadTypes = void | bool | SomeInteger | DataBuffer | tuple | Atomic
|
|
||||||
ThreadResult[T: ThreadTypes] = Result[T, DataBuffer]
|
|
||||||
|
|
||||||
TaskCtx[T: ThreadTypes] = object
|
TaskCtx[T: ThreadTypes] = object
|
||||||
ds: Datastore
|
ds: Datastore
|
||||||
res: ptr ThreadResult[T]
|
res: ptr ThreadResult[T]
|
||||||
@ -56,7 +56,7 @@ template withLocks(
|
|||||||
ctx: TaskCtx,
|
ctx: TaskCtx,
|
||||||
key: ?Key = Key.none,
|
key: ?Key = Key.none,
|
||||||
fut: Future[void],
|
fut: Future[void],
|
||||||
body: untyped) =
|
body: untyped): untyped =
|
||||||
try:
|
try:
|
||||||
if key.isSome and key.get in self.tasks:
|
if key.isSome and key.get in self.tasks:
|
||||||
if self.withLocks:
|
if self.withLocks:
|
||||||
@ -65,7 +65,9 @@ template withLocks(
|
|||||||
|
|
||||||
if self.withLocks:
|
if self.withLocks:
|
||||||
await self.queryLock.acquire() # only lock if it's required (fsds)
|
await self.queryLock.acquire() # only lock if it's required (fsds)
|
||||||
body
|
|
||||||
|
block:
|
||||||
|
body
|
||||||
finally:
|
finally:
|
||||||
if self.withLocks:
|
if self.withLocks:
|
||||||
if key.isSome and key.get in self.tasks:
|
if key.isSome and key.get in self.tasks:
|
||||||
@ -74,26 +76,31 @@ template withLocks(
|
|||||||
self.queryLock.release()
|
self.queryLock.release()
|
||||||
|
|
||||||
# TODO: needs rework, we can't use `result` with async
|
# TODO: needs rework, we can't use `result` with async
|
||||||
template dispatchTask(
|
template dispatchTask[T](
|
||||||
self: ThreadDatastore,
|
self: ThreadDatastore,
|
||||||
ctx: TaskCtx,
|
ctx: TaskCtx[T],
|
||||||
key: ?Key = Key.none,
|
key: ?Key = Key.none,
|
||||||
runTask: proc): untyped =
|
runTask: proc): auto =
|
||||||
try:
|
try:
|
||||||
await self.semaphore.acquire()
|
await self.semaphore.acquire()
|
||||||
ctx.signal = ThreadSignalPtr.new().valueOr:
|
let signal = ThreadSignalPtr.new()
|
||||||
result = failure(error())
|
if signal.isErr:
|
||||||
return
|
failure(signal.error)
|
||||||
|
else:
|
||||||
|
ctx.signal = signal.get()
|
||||||
|
let
|
||||||
|
fut = wait(ctx.signal)
|
||||||
|
|
||||||
let
|
withLocks(self, ctx, key, fut):
|
||||||
fut = wait(ctx.signal)
|
runTask()
|
||||||
|
await fut
|
||||||
withLocks(self, ctx, key, fut):
|
if ctx.res[].isErr:
|
||||||
runTask()
|
failure ctx.res[].error
|
||||||
await fut
|
else:
|
||||||
|
when result.T isnot void:
|
||||||
if ctx.res[].isErr:
|
success result.T(ctx.res[].get)
|
||||||
result = failure(ctx.res[].error()) # TODO: fix this, result shouldn't be accessed from a thread
|
else:
|
||||||
|
success()
|
||||||
except CancelledError as exc:
|
except CancelledError as exc:
|
||||||
trace "Cancelling thread future!", exc = exc.msg
|
trace "Cancelling thread future!", exc = exc.msg
|
||||||
ctx.cancelled = true
|
ctx.cancelled = true
|
||||||
@ -156,8 +163,7 @@ method has*(self: ThreadDatastore, key: Key): Future[?!bool] {.async.} =
|
|||||||
proc runTask() =
|
proc runTask() =
|
||||||
self.tp.spawn hasTask(addr ctx, unsafeAddr key)
|
self.tp.spawn hasTask(addr ctx, unsafeAddr key)
|
||||||
|
|
||||||
self.dispatchTask(ctx, key.some, runTask)
|
return self.dispatchTask(ctx, key.some, runTask)
|
||||||
return success(res.get())
|
|
||||||
|
|
||||||
proc asyncDelTask(ctx: ptr TaskCtx[void], key: ptr Key) {.async.} =
|
proc asyncDelTask(ctx: ptr TaskCtx[void], key: ptr Key) {.async.} =
|
||||||
defer:
|
defer:
|
||||||
@ -194,8 +200,7 @@ method delete*(
|
|||||||
proc runTask() =
|
proc runTask() =
|
||||||
self.tp.spawn delTask(addr ctx, unsafeAddr key)
|
self.tp.spawn delTask(addr ctx, unsafeAddr key)
|
||||||
|
|
||||||
self.dispatchTask(ctx, key.some, runTask)
|
return self.dispatchTask(ctx, key.some, runTask)
|
||||||
return success()
|
|
||||||
|
|
||||||
method delete*(
|
method delete*(
|
||||||
self: ThreadDatastore,
|
self: ThreadDatastore,
|
||||||
@ -257,8 +262,7 @@ method put*(
|
|||||||
makeUncheckedArray(baseAddr data),
|
makeUncheckedArray(baseAddr data),
|
||||||
data.len)
|
data.len)
|
||||||
|
|
||||||
self.dispatchTask(ctx, key.some, runTask)
|
return self.dispatchTask(ctx, key.some, runTask)
|
||||||
return success()
|
|
||||||
|
|
||||||
method put*(
|
method put*(
|
||||||
self: ThreadDatastore,
|
self: ThreadDatastore,
|
||||||
@ -285,6 +289,7 @@ proc asyncGetTask(
|
|||||||
ctx[].res[].err(error)
|
ctx[].res[].err(error)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
trace "Got data in get", data
|
||||||
ctx[].res[].ok(DataBuffer.new(data))
|
ctx[].res[].ok(DataBuffer.new(data))
|
||||||
|
|
||||||
proc getTask(
|
proc getTask(
|
||||||
@ -311,11 +316,7 @@ method get*(
|
|||||||
proc runTask() =
|
proc runTask() =
|
||||||
self.tp.spawn getTask(addr ctx, unsafeAddr key)
|
self.tp.spawn getTask(addr ctx, unsafeAddr key)
|
||||||
|
|
||||||
self.dispatchTask(ctx, key.some, runTask)
|
return self.dispatchTask(ctx, key.some, runTask)
|
||||||
if err =? res.errorOption:
|
|
||||||
return failure err
|
|
||||||
|
|
||||||
return success(@(res.get()))
|
|
||||||
|
|
||||||
method close*(self: ThreadDatastore): Future[?!void] {.async.} =
|
method close*(self: ThreadDatastore): Future[?!void] {.async.} =
|
||||||
for fut in self.tasks.values.toSeq:
|
for fut in self.tasks.values.toSeq:
|
||||||
@ -339,14 +340,15 @@ proc asyncQueryTask(
|
|||||||
return
|
return
|
||||||
|
|
||||||
if res.key.isNone:
|
if res.key.isNone:
|
||||||
ctx[].res[].ok((false, default(DataBuffer), default(DataBuffer)))
|
ctx[].res[].ok((default(DataBuffer), default(DataBuffer)))
|
||||||
return
|
return
|
||||||
|
|
||||||
var
|
var
|
||||||
keyBuf = DataBuffer.new($(res.key.get()))
|
keyBuf = DataBuffer.new($(res.key.get()))
|
||||||
dataBuf = DataBuffer.new(res.data)
|
dataBuf = DataBuffer.new(res.data)
|
||||||
|
|
||||||
ctx[].res[].ok((true, keyBuf, dataBuf))
|
trace "Got query result", key = $res.key.get(), data = res.data
|
||||||
|
ctx[].res[].ok((keyBuf, dataBuf))
|
||||||
|
|
||||||
proc queryTask(
|
proc queryTask(
|
||||||
ctx: ptr TaskCtx,
|
ctx: ptr TaskCtx,
|
||||||
@ -385,25 +387,15 @@ method query*(
|
|||||||
return success (Key.none, EmptyBytes)
|
return success (Key.none, EmptyBytes)
|
||||||
|
|
||||||
var
|
var
|
||||||
res = ThreadResult[(bool, DataBuffer, DataBuffer)]()
|
res = ThreadResult[ThreadQueryRes]()
|
||||||
ctx = TaskCtx[(bool, DataBuffer, DataBuffer)](
|
ctx = TaskCtx[ThreadQueryRes](
|
||||||
ds: self.ds,
|
ds: self.ds,
|
||||||
res: addr res)
|
res: addr res)
|
||||||
|
|
||||||
proc runTask() =
|
proc runTask() =
|
||||||
self.tp.spawn queryTask(addr ctx, addr childIter)
|
self.tp.spawn queryTask(addr ctx, addr childIter)
|
||||||
|
|
||||||
self.dispatchTask(ctx, Key.none, runTask)
|
return self.dispatchTask(ctx, Key.none, runTask)
|
||||||
if err =? res.errorOption:
|
|
||||||
trace "Query failed", err = $err
|
|
||||||
return failure err
|
|
||||||
|
|
||||||
let (ok, key, data) = res.get()
|
|
||||||
if not ok:
|
|
||||||
iter.finished = true
|
|
||||||
return success (Key.none, EmptyBytes)
|
|
||||||
|
|
||||||
return success (Key.init($key).expect("should not fail").some, @(data))
|
|
||||||
|
|
||||||
iter.next = next
|
iter.next = next
|
||||||
return success iter
|
return success iter
|
||||||
@ -415,17 +407,9 @@ proc new*(
|
|||||||
tp: Taskpool): ?!ThreadDatastore =
|
tp: Taskpool): ?!ThreadDatastore =
|
||||||
doAssert tp.numThreads > 1, "ThreadDatastore requires at least 2 threads"
|
doAssert tp.numThreads > 1, "ThreadDatastore requires at least 2 threads"
|
||||||
|
|
||||||
case withLocks:
|
success ThreadDatastore(
|
||||||
of true:
|
tp: tp,
|
||||||
success ThreadDatastore(
|
ds: ds,
|
||||||
tp: tp,
|
withLocks: withLocks,
|
||||||
ds: ds,
|
queryLock: newAsyncLock(),
|
||||||
withLocks: true,
|
semaphore: AsyncSemaphore.new(tp.numThreads - 1))
|
||||||
queryLock: newAsyncLock(),
|
|
||||||
semaphore: AsyncSemaphore.new(tp.numThreads - 1))
|
|
||||||
else:
|
|
||||||
success ThreadDatastore(
|
|
||||||
tp: tp,
|
|
||||||
ds: ds,
|
|
||||||
withLocks: false,
|
|
||||||
semaphore: AsyncSemaphore.new(tp.numThreads - 1))
|
|
||||||
|
|||||||
43
datastore/threads/threadresult.nim
Normal file
43
datastore/threads/threadresult.nim
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import std/atomics
|
||||||
|
import std/options
|
||||||
|
|
||||||
|
import pkg/questionable/results
|
||||||
|
import pkg/results
|
||||||
|
|
||||||
|
import ../types
|
||||||
|
import ../query
|
||||||
|
import ../key
|
||||||
|
|
||||||
|
import ./databuffer
|
||||||
|
|
||||||
|
type
|
||||||
|
ErrorEnum* {.pure.} = enum
|
||||||
|
DatastoreErr,
|
||||||
|
DatastoreKeyNotFoundErr,
|
||||||
|
QueryEndedErr,
|
||||||
|
CatchableErr
|
||||||
|
|
||||||
|
ThreadTypes* = void | bool | SomeInteger | DataBuffer | tuple | Atomic
|
||||||
|
ThreadResErr* = (ErrorEnum, DataBuffer)
|
||||||
|
ThreadQueryRes* = (DataBuffer, DataBuffer)
|
||||||
|
ThreadResult*[T: ThreadTypes] = Result[T, ThreadResErr]
|
||||||
|
|
||||||
|
converter toThreadErr*(e: ref CatchableError): ThreadResErr {.inline, raises: [].} =
|
||||||
|
if e of DatastoreKeyNotFound: (ErrorEnum.DatastoreKeyNotFoundErr, DataBuffer.new(e.msg))
|
||||||
|
elif e of QueryEndedError: (ErrorEnum.QueryEndedErr, DataBuffer.new(e.msg))
|
||||||
|
elif e of DatastoreError: (DatastoreErr, DataBuffer.new(e.msg))
|
||||||
|
elif e of CatchableError: (CatchableErr, DataBuffer.new(e.msg))
|
||||||
|
else: raise (ref Defect)(msg: e.msg)
|
||||||
|
|
||||||
|
converter toExc*(e: ThreadResErr): ref CatchableError =
|
||||||
|
case e[0]:
|
||||||
|
of ErrorEnum.DatastoreKeyNotFoundErr: (ref DatastoreKeyNotFound)(msg: $e[1])
|
||||||
|
of ErrorEnum.QueryEndedErr: (ref QueryEndedError)(msg: $e[1])
|
||||||
|
of ErrorEnum.DatastoreErr: (ref DatastoreError)(msg: $e[1])
|
||||||
|
of ErrorEnum.CatchableErr: (ref CatchableError)(msg: $e[1])
|
||||||
|
|
||||||
|
converter toQueryResponse*(r: ThreadQueryRes): QueryResponse =
|
||||||
|
if not r[0].isNil and r[0].len > 0 and key =? Key.init($r[0]):
|
||||||
|
(key.some, @(r[1]))
|
||||||
|
else:
|
||||||
|
(Key.none, EmptyBytes)
|
||||||
@ -6,5 +6,6 @@ const
|
|||||||
type
|
type
|
||||||
DatastoreError* = object of CatchableError
|
DatastoreError* = object of CatchableError
|
||||||
DatastoreKeyNotFound* = object of DatastoreError
|
DatastoreKeyNotFound* = object of DatastoreError
|
||||||
|
QueryEndedError* = object of DatastoreError
|
||||||
|
|
||||||
Datastore* = ref object of RootObj
|
Datastore* = ref object of RootObj
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user