From 51d2b47205d8414e58be3f6a9c2f880bb4f7b78f Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Tue, 26 Sep 2023 21:03:07 -0700 Subject: [PATCH 01/74] initial take at making fsds synchronous --- datastore/fsds.nim | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index 65f5cdd4..ffb3575d 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -8,6 +8,7 @@ import pkg/questionable/results from pkg/stew/results as stewResults import get, isErr import pkg/upraises +import ./backend import ./datastore export datastore @@ -16,15 +17,15 @@ push: {.upraises: [].} type FSDatastore* = ref object of Datastore - root*: string + root*: DataBuffer ignoreProtected: bool depth: int proc validDepth*(self: FSDatastore, key: Key): bool = key.len <= self.depth -proc isRootSubdir*(self: FSDatastore, path: string): bool = - path.startsWith(self.root) +proc isRootSubdir*(root, path: string): bool = + path.startsWith(root) proc path*(self: FSDatastore, key: Key): ?!string = ## Return filename corresponding to the key @@ -53,21 +54,22 @@ proc path*(self: FSDatastore, key: Key): ?!string = segments.add(ns.field / ns.value) let - fullname = (self.root / segments.joinPath()) + root = $self.root + fullname = (root / segments.joinPath()) .absolutePath() .catch() .get() .addFileExt(FileExt) - if not self.isRootSubdir(fullname): + if not root.isRootSubdir(fullname): return failure "Path is outside of `root` directory!" return success fullname -method has*(self: FSDatastore, key: Key): Future[?!bool] {.async.} = +proc has*(self: FSDatastore, key: Key): Future[?!bool] {.async.} = return self.path(key).?fileExists() -method delete*(self: FSDatastore, key: Key): Future[?!void] {.async.} = +proc delete*(self: FSDatastore, key: Key): Future[?!void] {.async.} = without path =? self.path(key), error: return failure error @@ -81,7 +83,7 @@ method delete*(self: FSDatastore, key: Key): Future[?!void] {.async.} = return success() -method delete*(self: FSDatastore, keys: seq[Key]): Future[?!void] {.async.} = +proc delete*(self: FSDatastore, keys: seq[Key]): Future[?!void] {.async.} = for key in keys: if err =? (await self.delete(key)).errorOption: return failure err @@ -118,7 +120,7 @@ proc readFile*(self: FSDatastore, path: string): ?!seq[byte] = except CatchableError as e: return failure e -method get*(self: FSDatastore, key: Key): Future[?!seq[byte]] {.async.} = +proc get*(self: FSDatastore, key: Key): Future[?!seq[byte]] {.async.} = without path =? self.path(key), error: return failure error @@ -128,7 +130,7 @@ method get*(self: FSDatastore, key: Key): Future[?!seq[byte]] {.async.} = return self.readFile(path) -method put*( +proc put*( self: FSDatastore, key: Key, data: seq[byte]): Future[?!void] {.async.} = @@ -144,7 +146,7 @@ method put*( return success() -method put*( +proc put*( self: FSDatastore, batch: seq[BatchEntry]): Future[?!void] {.async.} = @@ -165,10 +167,10 @@ proc dirWalker(path: string): iterator: string {.gcsafe.} = except CatchableError as exc: raise newException(Defect, exc.msg) -method close*(self: FSDatastore): Future[?!void] {.async.} = +proc close*(self: FSDatastore): Future[?!void] {.async.} = return success() -method query*( +proc query*( self: FSDatastore, query: Query): Future[?!QueryIter] {.async.} = @@ -201,6 +203,7 @@ method query*( return failure (ref DatastoreError)(msg: "Should always await query features") let + root = $self.root path = walker() if iter.finished: @@ -215,7 +218,7 @@ method query*( var keyPath = basePath - keyPath.removePrefix(self.root) + keyPath.removePrefix(root) keyPath = keyPath / path.changeFileExt("") keyPath = keyPath.replace("\\", "/") From 24ac1a870807afc090d2efd64e8b8262ccba328b Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Tue, 26 Sep 2023 21:15:41 -0700 Subject: [PATCH 02/74] initial take at making fsds synchronous --- datastore/fsds.nim | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index ffb3575d..d16fc704 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -2,7 +2,6 @@ import std/os import std/options import std/strutils -import pkg/chronos import pkg/questionable import pkg/questionable/results from pkg/stew/results as stewResults import get, isErr @@ -16,7 +15,7 @@ export datastore push: {.upraises: [].} type - FSDatastore* = ref object of Datastore + FSDatastore* = object root*: DataBuffer ignoreProtected: bool depth: int @@ -66,10 +65,13 @@ proc path*(self: FSDatastore, key: Key): ?!string = return success fullname -proc has*(self: FSDatastore, key: Key): Future[?!bool] {.async.} = +proc has*(self: FSDatastore, key: KeyId): ?!bool = + let key = key.toKey() return self.path(key).?fileExists() -proc delete*(self: FSDatastore, key: Key): Future[?!void] {.async.} = +proc delete*(self: FSDatastore, key: KeyId): ?!void = + let key = key.toKey() + without path =? self.path(key), error: return failure error @@ -83,9 +85,9 @@ proc delete*(self: FSDatastore, key: Key): Future[?!void] {.async.} = return success() -proc delete*(self: FSDatastore, keys: seq[Key]): Future[?!void] {.async.} = +proc delete*(self: FSDatastore, keys: openArray[KeyId]): ?!void = for key in keys: - if err =? (await self.delete(key)).errorOption: + if err =? self.delete(key).errorOption: return failure err return success() @@ -120,7 +122,7 @@ proc readFile*(self: FSDatastore, path: string): ?!seq[byte] = except CatchableError as e: return failure e -proc get*(self: FSDatastore, key: Key): Future[?!seq[byte]] {.async.} = +proc get*(self: FSDatastore, key: Key): ?!seq[byte] = without path =? self.path(key), error: return failure error @@ -133,7 +135,7 @@ proc get*(self: FSDatastore, key: Key): Future[?!seq[byte]] {.async.} = proc put*( self: FSDatastore, key: Key, - data: seq[byte]): Future[?!void] {.async.} = + data: seq[byte]): ?!void = without path =? self.path(key), error: return failure error @@ -148,10 +150,10 @@ proc put*( proc put*( self: FSDatastore, - batch: seq[BatchEntry]): Future[?!void] {.async.} = + batch: seq[BatchEntry]): ?!void = for entry in batch: - if err =? (await self.put(entry.key, entry.data)).errorOption: + if err =? self.put(entry.key, entry.data).errorOption: return failure err return success() @@ -167,12 +169,12 @@ proc dirWalker(path: string): iterator: string {.gcsafe.} = except CatchableError as exc: raise newException(Defect, exc.msg) -proc close*(self: FSDatastore): Future[?!void] {.async.} = +proc close*(self: FSDatastore): ?!void = return success() proc query*( self: FSDatastore, - query: Query): Future[?!QueryIter] {.async.} = + query: Query): ?!QueryIter = without path =? self.path(query.key), error: return failure error @@ -193,14 +195,14 @@ proc query*( var iter = QueryIter.new() - var lock = newAsyncLock() # serialize querying under threads - proc next(): Future[?!QueryResponse] {.async.} = - defer: - if lock.locked: - lock.release() + # var lock = newAsyncLock() # serialize querying under threads + proc next(): ?!QueryResponse = + # defer: + # if lock.locked: + # lock.release() - if lock.locked: - return failure (ref DatastoreError)(msg: "Should always await query features") + # if lock.locked: + # return failure (ref DatastoreError)(msg: "Should always await query features") let root = $self.root @@ -209,7 +211,7 @@ proc query*( if iter.finished: return failure "iterator is finished" - await lock.acquire() + # await lock.acquire() if finished(walker): iter.finished = true From 653ec99b5341e3ea90ed2bdddf9719c5025c3edd Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Tue, 26 Sep 2023 21:27:46 -0700 Subject: [PATCH 03/74] initial take at making fsds synchronous --- datastore/fsds.nim | 26 ++++++++++++++++---------- datastore/threads/databuffer.nim | 9 ++++++--- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index d16fc704..2e9ad406 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -92,7 +92,7 @@ proc delete*(self: FSDatastore, keys: openArray[KeyId]): ?!void = return success() -proc readFile*(self: FSDatastore, path: string): ?!seq[byte] = +proc readFile*[V](self: FSDatastore, path: string): ?!V = var file: File @@ -104,14 +104,19 @@ proc readFile*(self: FSDatastore, path: string): ?!seq[byte] = try: let - size = file.getFileSize + size = file.getFileSize().int + when V is seq[byte]: + var bytes = newSeq[byte](size) + elif V is DataBuffer: + var bytes = DataBuffer.new(capacity=size) + else: + {.error: "unhandled result type".} var - bytes = newSeq[byte](size) read = 0 while read < size: - read += file.readBytes(bytes, read, size) + read += file.readBytes(bytes.toOpenArray(), read, size) if read < size: return failure $read & " bytes were read from " & path & @@ -122,7 +127,8 @@ proc readFile*(self: FSDatastore, path: string): ?!seq[byte] = except CatchableError as e: return failure e -proc get*(self: FSDatastore, key: Key): ?!seq[byte] = +proc get*(self: FSDatastore, key: KeyId): ?!DataBuffer = + let key = key.toKey() without path =? self.path(key), error: return failure error @@ -130,12 +136,12 @@ proc get*(self: FSDatastore, key: Key): ?!seq[byte] = return failure( newException(DatastoreKeyNotFound, "Key doesn't exist")) - return self.readFile(path) + return readFile[DataBuffer](self, path) proc put*( self: FSDatastore, - key: Key, - data: seq[byte]): ?!void = + key: KeyId, + data: DataBuffer): ?!void = without path =? self.path(key), error: return failure error @@ -174,7 +180,7 @@ proc close*(self: FSDatastore): ?!void = proc query*( self: FSDatastore, - query: Query): ?!QueryIter = + query: DbQuery[KeyId, DataBuffer]): ?!QueryIter = without path =? self.path(query.key), error: return failure error @@ -228,7 +234,7 @@ proc query*( key = Key.init(keyPath).expect("should not fail") data = if query.value: - self.readFile((basePath / path).absolutePath) + self.readFile[DataBuffer]((basePath / path).absolutePath) .expect("Should read file") else: @[] diff --git a/datastore/threads/databuffer.nim b/datastore/threads/databuffer.nim index 5ea7b4be..1b5e6d25 100644 --- a/datastore/threads/databuffer.nim +++ b/datastore/threads/databuffer.nim @@ -47,7 +47,7 @@ template `==`*[T: char | byte](a: DataBuffer, b: openArray[T]): bool = elif a[].size != b.len: false else: a.hash() == b.hash() -proc new(tp: type DataBuffer, capacity: int = 0): DataBuffer = +proc new*(tp: type DataBuffer, capacity: int = 0): DataBuffer = ## allocate new buffer with given capacity ## @@ -130,9 +130,12 @@ converter toBuffer*(err: ref CatchableError): DataBuffer = return DataBuffer.new(err.msg) -template toOpenArray*[T: byte | char](data: DataBuffer, t: typedesc[T]): openArray[T] = +template toOpenArray*[T: byte | char](data: var DataBuffer, t: typedesc[T]): var openArray[T] = ## get openArray from DataBuffer as char ## ## this is explicit since sqlite treats string differently from openArray[byte] - let bf = cast[ptr UncheckedArray[T]](data[].buf) + var bf = cast[ptr UncheckedArray[T]](data[].buf) bf.toOpenArray(0, data[].size-1) + +template toOpenArray*(data: var DataBuffer): var openArray[byte] = + toOpenArray(data, byte) From 5242b85b3693131764d7bdd0552177c3561ad00d Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Tue, 26 Sep 2023 21:28:48 -0700 Subject: [PATCH 04/74] initial take at making fsds synchronous --- datastore/fsds.nim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index 2e9ad406..24b3f06d 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -142,13 +142,15 @@ proc put*( self: FSDatastore, key: KeyId, data: DataBuffer): ?!void = + let key = key.toKey() without path =? self.path(key), error: return failure error try: + var data = data createDir(parentDir(path)) - writeFile(path, data) + writeFile(path, data.toOpenArray()) except CatchableError as e: return failure e From bb9e343e9ffe59511f5fd8afe188a90692b793ec Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Tue, 26 Sep 2023 21:29:12 -0700 Subject: [PATCH 05/74] initial take at making fsds synchronous --- datastore/fsds.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index 24b3f06d..35585bd3 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -158,7 +158,7 @@ proc put*( proc put*( self: FSDatastore, - batch: seq[BatchEntry]): ?!void = + batch: seq[DbBatchEntry[KeyId, DataBuffer]]): ?!void = for entry in batch: if err =? self.put(entry.key, entry.data).errorOption: @@ -182,7 +182,7 @@ proc close*(self: FSDatastore): ?!void = proc query*( self: FSDatastore, - query: DbQuery[KeyId, DataBuffer]): ?!QueryIter = + query: DbQuery[KeyId]): ?!QueryIter = without path =? self.path(query.key), error: return failure error From 235232a84f5ca5c2f8198edafa34ce4e35df0851 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Tue, 26 Sep 2023 21:30:59 -0700 Subject: [PATCH 06/74] initial take at making fsds synchronous --- datastore/fsds.nim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index 35585bd3..b3162d82 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -184,7 +184,8 @@ proc query*( self: FSDatastore, query: DbQuery[KeyId]): ?!QueryIter = - without path =? self.path(query.key), error: + let key = query.key.toKey() + without path =? self.path(key), error: return failure error let basePath = @@ -236,7 +237,7 @@ proc query*( key = Key.init(keyPath).expect("should not fail") data = if query.value: - self.readFile[DataBuffer]((basePath / path).absolutePath) + readFile[DataBuffer](self, (basePath / path).absolutePath) .expect("Should read file") else: @[] From 5e424262c3cb39863d259fc8bcf7c683144d2158 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 11:51:55 -0700 Subject: [PATCH 07/74] rework tuple types --- datastore/threads/threadproxyds.nim | 96 ++++++++++++----------------- 1 file changed, 41 insertions(+), 55 deletions(-) diff --git a/datastore/threads/threadproxyds.nim b/datastore/threads/threadproxyds.nim index 772dc721..f9d11894 100644 --- a/datastore/threads/threadproxyds.nim +++ b/datastore/threads/threadproxyds.nim @@ -36,16 +36,6 @@ logScope: type - ThreadBackendKinds* = enum - Sqlite - # Filesystem - - ThreadBackend* = object - ## backend case type to avoid needing to make ThreadDatastore generic - case kind*: ThreadBackendKinds - of Sqlite: - sql*: SQLiteBackend[KeyId,DataBuffer] - TaskCtxObj*[T: ThreadTypes] = object res: ThreadResult[T] signal: ThreadSignalPtr @@ -56,15 +46,18 @@ type ## Task context object. ## This is a SharedPtr to make the query iter simpler - ThreadDatastore* = ref object of Datastore + ThreadDatastore*[BT] = ref object of Datastore tp: Taskpool - backend: ThreadBackend + backend: BT semaphore: AsyncSemaphore # semaphore is used for backpressure \ # to avoid exhausting file descriptors var ctxLock: Lock ctxLock.initLock() +proc newTaskCtx*[T](signal: ThreadSignalPtr): TaskCtx[T] = + newSharedPtr(TaskCtxObj[T](signal: signal)) + proc setCancelled[T](ctx: TaskCtx[T]) = # withLock(ctxLock): ctx[].cancelled = true @@ -113,28 +106,26 @@ template executeTask[T](ctx: TaskCtx[T], blk: untyped) = ctx.setDone() discard ctx[].signal.fireSync() -template dispatchTaskWrap[T](self: ThreadDatastore, +template dispatchTaskWrap[BT](self: ThreadDatastore[BT], + signal: ThreadSignalPtr, + blk: untyped + ): auto = + var ds {.used, inject.} = self.backend + proc runTask() = + `blk` + runTask() + await wait(ctx[].signal) + +template dispatchTask[BT](self: ThreadDatastore[BT], signal: ThreadSignalPtr, blk: untyped - ): auto = - case self.backend.kind: - of Sqlite: - var ds {.used, inject.} = self.backend.sql - proc runTask() = - `blk` - runTask() - await wait(ctx[].signal) - -template dispatchTask[T](self: ThreadDatastore, - signal: ThreadSignalPtr, - blk: untyped - ): auto = + ): auto = ## handles dispatching a task from an async context ## `blk` is the actions, it has `ctx` and `ds` variables in scope. ## note that `ds` is a generic - let ctx {.inject.} = newSharedPtr(TaskCtxObj[T](signal: signal)) + # let ctx {.inject.} = newSharedPtr(TaskCtxObj[T](signal: signal)) try: - dispatchTaskWrap[T](self, signal, blk) + dispatchTaskWrap[BT](self, signal, blk) except CancelledError as exc: trace "Cancelling thread future!", exc = exc.msg ctx.setCancelled() @@ -149,38 +140,39 @@ proc hasTask[T, DB](ctx: TaskCtx[T], ds: DB, key: KeyId) {.gcsafe.} = executeTask(ctx): has(ds, key) -method has*(self: ThreadDatastore, +method has*[BT](self: ThreadDatastore[BT], key: Key): Future[?!bool] {.async.} = await self.semaphore.acquire() without signal =? acquireSignal(), err: return failure err - let key = KeyId.new key.id() - dispatchTask[bool](self, signal): + let ctx = newTaskCtx(bool, signal: signal) + dispatchTask(self, signal): + let key = KeyId.new key.id() self.tp.spawn hasTask(ctx, ds, key) return ctx[].res.toRes(v => v) - proc deleteTask[T, DB](ctx: TaskCtx[T], ds: DB; key: KeyId) {.gcsafe.} = ## run backend command executeTask(ctx): delete(ds, key) -method delete*(self: ThreadDatastore, +method delete*[BT](self: ThreadDatastore[BT], key: Key): Future[?!void] {.async.} = ## delete key await self.semaphore.acquire() without signal =? acquireSignal(), err: return failure err - let key = KeyId.new key.id() - dispatchTask[void](self, signal): + let ctx = newTaskCtx[void](signal: signal) + dispatchTask(self, signal): + let key = KeyId.new key.id() self.tp.spawn deleteTask(ctx, ds, key) return ctx[].res.toRes() -method delete*(self: ThreadDatastore, +method delete*[BT](self: ThreadDatastore[BT], keys: seq[Key]): Future[?!void] {.async.} = ## delete batch for key in keys: @@ -196,7 +188,7 @@ proc putTask[T, DB](ctx: TaskCtx[T], ds: DB; executeTask(ctx): put(ds, key, data) -method put*(self: ThreadDatastore, +method put*[BT](self: ThreadDatastore[BT], key: Key, data: seq[byte]): Future[?!void] {.async.} = ## put key with data @@ -204,7 +196,8 @@ method put*(self: ThreadDatastore, without signal =? acquireSignal(), err: return failure err - dispatchTask[void](self, signal): + let ctx = newTaskCtx[void](signal: signal) + dispatchTask(self, signal): let key = KeyId.new key.id() let data = DataBuffer.new data self.tp.spawn putTask(ctx, ds, key, data) @@ -229,24 +222,22 @@ proc getTask[DB](ctx: TaskCtx[DataBuffer], ds: DB; let res = get(ds, key) res -method get*(self: ThreadDatastore, - key: Key, - ): Future[?!seq[byte]] {.async.} = +method get*[BT](self: ThreadDatastore[BT], + key: Key, + ): Future[?!seq[byte]] {.async.} = await self.semaphore.acquire() without signal =? acquireSignal(), err: return failure err - let key = KeyId.new key.id() + let ctx = newTaskCtx[void](signal: signal) dispatchTask[DataBuffer](self, signal): self.tp.spawn getTask(ctx, ds, key) return ctx[].res.toRes(v => v.toSeq()) -method close*(self: ThreadDatastore): Future[?!void] {.async.} = +method close*[BT](self: ThreadDatastore[BT]): Future[?!void] {.async.} = await self.semaphore.closeAll() - case self.backend.kind: - of Sqlite: - self.backend.sql.close() + self.backend.close() type QResult = DbQueryResponse[KeyId, DataBuffer] @@ -293,7 +284,7 @@ proc queryTask[DB]( # set final result (?!QResult).ok((KeyId.none, DataBuffer())) -method query*(self: ThreadDatastore, +method query*[BT](self: ThreadDatastore[BT], q: Query ): Future[?!QueryIter] {.async.} = ## performs async query @@ -311,8 +302,8 @@ method query*(self: ThreadDatastore, value=q.value, limit=q.limit, offset=q.offset, sort=q.sort) # setup initial queryTask - let ctx {.inject.} = newSharedPtr(TaskCtxObj[QResult](signal: signal)) - dispatchTaskWrap[DbQueryResponse[KeyId, DataBuffer]](self, signal): + let ctx = newTaskCtx[QResult](signal: signal) + dispatchTaskWrap[DbQueryResponse[KeyId, DataBuffer], BT](self, signal): self.tp.spawn queryTask(ctx, ds, query, nextSignal) await nextSignal.fire() @@ -368,14 +359,9 @@ proc new*[DB](self: type ThreadDatastore, ): ?!ThreadDatastore = doAssert tp.numThreads > 1, "ThreadDatastore requires at least 2 threads" - when DB is SQLiteBackend[KeyId,DataBuffer]: - let backend = ThreadBackend(kind: Sqlite, sql: db) - else: - {.error: "unsupported backend: " & $typeof(db).} - success ThreadDatastore( tp: tp, - backend: backend, + backend: db, # TODO: are these needed anymore?? # withLocks: withLocks, # queryLock: newAsyncLock(), From 49d846f468cf846b81da3d264c7e0a66fe0fb04f Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 12:02:04 -0700 Subject: [PATCH 08/74] make Threadproxyds generic --- datastore/threads/threadproxyds.nim | 24 +++++++++++------------ tests/datastore/testthreadproxyds.nim | 28 +++++++-------------------- 2 files changed, 19 insertions(+), 33 deletions(-) diff --git a/datastore/threads/threadproxyds.nim b/datastore/threads/threadproxyds.nim index f9d11894..c8e98ad5 100644 --- a/datastore/threads/threadproxyds.nim +++ b/datastore/threads/threadproxyds.nim @@ -55,7 +55,7 @@ type var ctxLock: Lock ctxLock.initLock() -proc newTaskCtx*[T](signal: ThreadSignalPtr): TaskCtx[T] = +proc newTaskCtx*[T](tp: typedesc[T], signal: ThreadSignalPtr): TaskCtx[T] = newSharedPtr(TaskCtxObj[T](signal: signal)) proc setCancelled[T](ctx: TaskCtx[T]) = @@ -123,7 +123,6 @@ template dispatchTask[BT](self: ThreadDatastore[BT], ## handles dispatching a task from an async context ## `blk` is the actions, it has `ctx` and `ds` variables in scope. ## note that `ds` is a generic - # let ctx {.inject.} = newSharedPtr(TaskCtxObj[T](signal: signal)) try: dispatchTaskWrap[BT](self, signal, blk) except CancelledError as exc: @@ -146,13 +145,13 @@ method has*[BT](self: ThreadDatastore[BT], without signal =? acquireSignal(), err: return failure err - let ctx = newTaskCtx(bool, signal: signal) + let ctx = newTaskCtx(bool, signal=signal) dispatchTask(self, signal): let key = KeyId.new key.id() self.tp.spawn hasTask(ctx, ds, key) return ctx[].res.toRes(v => v) -proc deleteTask[T, DB](ctx: TaskCtx[T], ds: DB; +method deleteTask[T, DB](ctx: TaskCtx[T], ds: DB; key: KeyId) {.gcsafe.} = ## run backend command executeTask(ctx): @@ -165,7 +164,7 @@ method delete*[BT](self: ThreadDatastore[BT], without signal =? acquireSignal(), err: return failure err - let ctx = newTaskCtx[void](signal: signal) + let ctx = newTaskCtx(void, signal=signal) dispatchTask(self, signal): let key = KeyId.new key.id() self.tp.spawn deleteTask(ctx, ds, key) @@ -196,7 +195,7 @@ method put*[BT](self: ThreadDatastore[BT], without signal =? acquireSignal(), err: return failure err - let ctx = newTaskCtx[void](signal: signal) + let ctx = newTaskCtx(void, signal=signal) dispatchTask(self, signal): let key = KeyId.new key.id() let data = DataBuffer.new data @@ -229,8 +228,9 @@ method get*[BT](self: ThreadDatastore[BT], without signal =? acquireSignal(), err: return failure err - let ctx = newTaskCtx[void](signal: signal) - dispatchTask[DataBuffer](self, signal): + let ctx = newTaskCtx(DataBuffer, signal=signal) + dispatchTask(self, signal): + let key = KeyId.new key.id() self.tp.spawn getTask(ctx, ds, key) return ctx[].res.toRes(v => v.toSeq()) @@ -302,8 +302,8 @@ method query*[BT](self: ThreadDatastore[BT], value=q.value, limit=q.limit, offset=q.offset, sort=q.sort) # setup initial queryTask - let ctx = newTaskCtx[QResult](signal: signal) - dispatchTaskWrap[DbQueryResponse[KeyId, DataBuffer], BT](self, signal): + let ctx = newTaskCtx(QResult, signal=signal) + dispatchTaskWrap(self, signal): self.tp.spawn queryTask(ctx, ds, query, nextSignal) await nextSignal.fire() @@ -356,10 +356,10 @@ proc new*[DB](self: type ThreadDatastore, db: DB, withLocks = static false, tp: Taskpool - ): ?!ThreadDatastore = + ): ?!ThreadDatastore[DB] = doAssert tp.numThreads > 1, "ThreadDatastore requires at least 2 threads" - success ThreadDatastore( + success ThreadDatastore[DB]( tp: tp, backend: db, # TODO: are these needed anymore?? diff --git a/tests/datastore/testthreadproxyds.nim b/tests/datastore/testthreadproxyds.nim index 14a41b69..72758b04 100644 --- a/tests/datastore/testthreadproxyds.nim +++ b/tests/datastore/testthreadproxyds.nim @@ -25,14 +25,10 @@ import ./querycommontests const NumThreads = 20 # IO threads aren't attached to CPU count suite "Test Basic ThreadProxyDatastore": + var - sqlStore: SQLiteBackend[KeyId,DataBuffer] - ds: ThreadDatastore - taskPool: Taskpool key = Key.init("/a").tryGet() data = "some bytes".toBytes - - setupAll: sqlStore = newSQLiteBackend[KeyId, DataBuffer](Memory).tryGet() taskPool = Taskpool.new(NumThreads) ds = ThreadDatastore.new(sqlStore, tp = taskPool).tryGet() @@ -61,17 +57,12 @@ suite "Test Basic ThreadProxyDatastore": suite "Test Basic ThreadDatastore with SQLite": var - sqlStore: SQLiteBackend[KeyId,DataBuffer] - ds: ThreadDatastore - taskPool: Taskpool - key = Key.init("/a/b").tryGet() - bytes = "some bytes".toBytes - otherBytes = "some other bytes".toBytes - - setupAll: sqlStore = newSQLiteBackend[KeyId, DataBuffer](Memory).tryGet() taskPool = Taskpool.new(NumThreads) ds = ThreadDatastore.new(sqlStore, tp = taskPool).tryGet() + key = Key.init("/a/b").tryGet() + bytes = "some bytes".toBytes + otherBytes = "some other bytes".toBytes teardown: GC_fullCollect() @@ -85,17 +76,12 @@ suite "Test Basic ThreadDatastore with SQLite": suite "Test Query ThreadDatastore with SQLite": var - sqlStore: SQLiteBackend[KeyId,DataBuffer] - ds: ThreadDatastore - taskPool: Taskpool - key = Key.init("/a/b").tryGet() - bytes = "some bytes".toBytes - otherBytes = "some other bytes".toBytes - - setup: sqlStore = newSQLiteBackend[KeyId, DataBuffer](Memory).tryGet() taskPool = Taskpool.new(NumThreads) ds = ThreadDatastore.new(sqlStore, tp = taskPool).tryGet() + key = Key.init("/a/b").tryGet() + bytes = "some bytes".toBytes + otherBytes = "some other bytes".toBytes teardown: GC_fullCollect() From b4b534bf67d8ac1928492a70fece524759148ef2 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 12:54:22 -0700 Subject: [PATCH 09/74] loop tests --- datastore/threads/threadproxyds.nim | 2 + tests/datastore/testthreadproxyds.nim | 136 ++++++++++---------------- 2 files changed, 56 insertions(+), 82 deletions(-) diff --git a/datastore/threads/threadproxyds.nim b/datastore/threads/threadproxyds.nim index dcc16800..432eb6f8 100644 --- a/datastore/threads/threadproxyds.nim +++ b/datastore/threads/threadproxyds.nim @@ -348,6 +348,7 @@ method query*(self: ThreadDatastore, trace "Cancelling thread future!", exc = exc.msg ctx.setCancelled() discard ctx[].signal.close() + echo "nextSignal:CLOSE!" discard nextSignal.close() self.semaphore.release() raise exc @@ -357,6 +358,7 @@ method query*(self: ThreadDatastore, except CancelledError as exc: trace "Cancelling thread future!", exc = exc.msg discard signal.close() + echo "nextSignal:CLOSE!" discard nextSignal.close() self.semaphore.release() raise exc diff --git a/tests/datastore/testthreadproxyds.nim b/tests/datastore/testthreadproxyds.nim index 14a41b69..27c8e829 100644 --- a/tests/datastore/testthreadproxyds.nim +++ b/tests/datastore/testthreadproxyds.nim @@ -22,88 +22,60 @@ import pkg/datastore/threads/threadproxyds {.all.} import ./dscommontests import ./querycommontests -const NumThreads = 20 # IO threads aren't attached to CPU count - -suite "Test Basic ThreadProxyDatastore": - var - sqlStore: SQLiteBackend[KeyId,DataBuffer] - ds: ThreadDatastore - taskPool: Taskpool - key = Key.init("/a").tryGet() - data = "some bytes".toBytes - - setupAll: - sqlStore = newSQLiteBackend[KeyId, DataBuffer](Memory).tryGet() - taskPool = Taskpool.new(NumThreads) - ds = ThreadDatastore.new(sqlStore, tp = taskPool).tryGet() - - teardownAll: - echo "teardown done" - - test "check put": - echo "\n\n=== put ===" - let res1 = await ds.put(key, data) - echo "res1: ", res1.repr - check res1.isOk - - test "check get": - echo "\n\n=== get ===" - echo "get send key: ", key.repr - let res2 = await ds.get(key) - echo "get key post: ", key.repr - echo "get res2: ", res2.repr - echo res2.get() == data - var val = "" - for c in res2.get(): - val &= char(c) - echo "get res2: ", $val - -suite "Test Basic ThreadDatastore with SQLite": - - var - sqlStore: SQLiteBackend[KeyId,DataBuffer] - ds: ThreadDatastore - taskPool: Taskpool - key = Key.init("/a/b").tryGet() - bytes = "some bytes".toBytes - otherBytes = "some other bytes".toBytes - - setupAll: - sqlStore = newSQLiteBackend[KeyId, DataBuffer](Memory).tryGet() - taskPool = Taskpool.new(NumThreads) - ds = ThreadDatastore.new(sqlStore, tp = taskPool).tryGet() - - teardown: - GC_fullCollect() - - teardownAll: - (await ds.close()).tryGet() - taskPool.shutdown() - - basicStoreTests(ds, key, bytes, otherBytes) - -suite "Test Query ThreadDatastore with SQLite": - - var - sqlStore: SQLiteBackend[KeyId,DataBuffer] - ds: ThreadDatastore - taskPool: Taskpool - key = Key.init("/a/b").tryGet() - bytes = "some bytes".toBytes - otherBytes = "some other bytes".toBytes - - setup: - sqlStore = newSQLiteBackend[KeyId, DataBuffer](Memory).tryGet() - taskPool = Taskpool.new(NumThreads) - ds = ThreadDatastore.new(sqlStore, tp = taskPool).tryGet() - - teardown: - GC_fullCollect() - - (await ds.close()).tryGet() - taskPool.shutdown() - - queryTests(ds, true) +const + NumThreads = 20 # IO threads aren't attached to CPU count + N = 100 + +for i in 1..N: + suite "Test Basic ThreadDatastore with SQLite": + + var + sqlStore: SQLiteBackend[KeyId,DataBuffer] + ds: ThreadDatastore + taskPool: Taskpool + key = Key.init("/a/b").tryGet() + bytes = "some bytes".toBytes + otherBytes = "some other bytes".toBytes + + setupAll: + sqlStore = newSQLiteBackend[KeyId, DataBuffer](Memory).tryGet() + taskPool = Taskpool.new(NumThreads) + ds = ThreadDatastore.new(sqlStore, tp = taskPool).tryGet() + + teardown: + GC_fullCollect() + + teardownAll: + (await ds.close()).tryGet() + taskPool.shutdown() + + basicStoreTests(ds, key, bytes, otherBytes) + GC_fullCollect() + +for i in 1..N: + suite "Test Query ThreadDatastore with SQLite": + + var + sqlStore: SQLiteBackend[KeyId,DataBuffer] + ds: ThreadDatastore + taskPool: Taskpool + key = Key.init("/a/b").tryGet() + bytes = "some bytes".toBytes + otherBytes = "some other bytes".toBytes + + setup: + sqlStore = newSQLiteBackend[KeyId, DataBuffer](Memory).tryGet() + taskPool = Taskpool.new(NumThreads) + ds = ThreadDatastore.new(sqlStore, tp = taskPool).tryGet() + + teardown: + GC_fullCollect() + + (await ds.close()).tryGet() + taskPool.shutdown() + + queryTests(ds, true) + GC_fullCollect() # suite "Test Basic ThreadDatastore with fsds": # let From 030084186ef68472f4e0c7f60d962f95aee9ad93 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 12:55:48 -0700 Subject: [PATCH 10/74] loop tests --- tests/datastore/testthreadproxyds.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/datastore/testthreadproxyds.nim b/tests/datastore/testthreadproxyds.nim index 27c8e829..b42cbb0a 100644 --- a/tests/datastore/testthreadproxyds.nim +++ b/tests/datastore/testthreadproxyds.nim @@ -24,7 +24,8 @@ import ./querycommontests const NumThreads = 20 # IO threads aren't attached to CPU count - N = 100 + ThreadTestLoops {.intdefine.} = 10 + N = ThreadTestLoops for i in 1..N: suite "Test Basic ThreadDatastore with SQLite": From 1fd80c6f2be318b9da26a5ffea34326cc3f236fb Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 13:12:32 -0700 Subject: [PATCH 11/74] fix merge --- datastore/threads/threadproxyds.nim | 2 +- tests/datastore/testthreadproxyds.nim | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/datastore/threads/threadproxyds.nim b/datastore/threads/threadproxyds.nim index b573bf1e..422c548e 100644 --- a/datastore/threads/threadproxyds.nim +++ b/datastore/threads/threadproxyds.nim @@ -204,7 +204,7 @@ method put*[BT](self: ThreadDatastore[BT], return ctx[].res.toRes() method put*[DB]( - self: ThreadDatastore, + self: ThreadDatastore[DB], batch: seq[BatchEntry]): Future[?!void] {.async.} = ## put batch data for entry in batch: diff --git a/tests/datastore/testthreadproxyds.nim b/tests/datastore/testthreadproxyds.nim index f415ba61..000c52d7 100644 --- a/tests/datastore/testthreadproxyds.nim +++ b/tests/datastore/testthreadproxyds.nim @@ -63,7 +63,6 @@ for i in 1..N: key = Key.init("/a/b").tryGet() bytes = "some bytes".toBytes otherBytes = "some other bytes".toBytes - setup: sqlStore = newSQLiteBackend[KeyId, DataBuffer](Memory).tryGet() From c51d354f72a40ea7c0b9b09c126ab80e2294b6a9 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 13:16:30 -0700 Subject: [PATCH 12/74] add nextSignal using mutex --- datastore/threads/threadproxyds.nim | 8 ++++++++ tests/datastore/testthreadproxyds.nim | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/datastore/threads/threadproxyds.nim b/datastore/threads/threadproxyds.nim index 422c548e..e273e062 100644 --- a/datastore/threads/threadproxyds.nim +++ b/datastore/threads/threadproxyds.nim @@ -41,6 +41,7 @@ type signal: ThreadSignalPtr running: bool ## used to mark when a task worker is running cancelled: bool ## used to cancel a task before it's started + nextSignal: (Lock, Cond) TaskCtx*[T] = SharedPtr[TaskCtxObj[T]] ## Task context object. @@ -72,6 +73,13 @@ proc setDone[T](ctx: TaskCtx[T]) = # withLock(ctxLock): ctx[].running = false +proc waitSync*(sig: var (Lock, Cond)) = + withLock(sig[0]): + wait(sig[1], sig[0]) +proc fireSync*(sig: var (Lock, Cond)) = + withLock(sig[0]): + signal(sig[1]) + proc acquireSignal(): ?!ThreadSignalPtr = let signal = ThreadSignalPtr.new() if signal.isErr(): diff --git a/tests/datastore/testthreadproxyds.nim b/tests/datastore/testthreadproxyds.nim index 000c52d7..57fea886 100644 --- a/tests/datastore/testthreadproxyds.nim +++ b/tests/datastore/testthreadproxyds.nim @@ -24,7 +24,7 @@ import ./querycommontests const NumThreads = 20 # IO threads aren't attached to CPU count - ThreadTestLoops {.intdefine.} = 10 + ThreadTestLoops {.intdefine.} = 100 N = ThreadTestLoops for i in 1..N: From 30728c1d0f0ae85068bd774f8e0ca420eaee293c Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 13:24:32 -0700 Subject: [PATCH 13/74] add nextSignal using mutex --- datastore/threads/threadproxyds.nim | 17 ++++------------- datastore/threads/threadresult.nim | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/datastore/threads/threadproxyds.nim b/datastore/threads/threadproxyds.nim index e273e062..1c5528ba 100644 --- a/datastore/threads/threadproxyds.nim +++ b/datastore/threads/threadproxyds.nim @@ -41,7 +41,7 @@ type signal: ThreadSignalPtr running: bool ## used to mark when a task worker is running cancelled: bool ## used to cancel a task before it's started - nextSignal: (Lock, Cond) + nextSignal: MutexSignal TaskCtx*[T] = SharedPtr[TaskCtxObj[T]] ## Task context object. @@ -73,13 +73,6 @@ proc setDone[T](ctx: TaskCtx[T]) = # withLock(ctxLock): ctx[].running = false -proc waitSync*(sig: var (Lock, Cond)) = - withLock(sig[0]): - wait(sig[1], sig[0]) -proc fireSync*(sig: var (Lock, Cond)) = - withLock(sig[0]): - signal(sig[1]) - proc acquireSignal(): ?!ThreadSignalPtr = let signal = ThreadSignalPtr.new() if signal.isErr(): @@ -254,7 +247,6 @@ method queryTask[DB]( ctx: TaskCtx[QResult], ds: DB, query: DbQuery[KeyId], - nextSignal: ThreadSignalPtr ) {.gcsafe, nimcall.} = ## run query command executeTask(ctx): @@ -299,8 +291,8 @@ method query*[BT](self: ThreadDatastore[BT], await self.semaphore.acquire() without signal =? acquireSignal(), err: return failure err - without nextSignal =? acquireSignal(), err: - return failure err + let ctx = newTaskCtx(QResult, signal=signal) + ctx[].nextSignal.init() try: let query = dbQuery( @@ -308,9 +300,8 @@ method query*[BT](self: ThreadDatastore[BT], value=q.value, limit=q.limit, offset=q.offset, sort=q.sort) # setup initial queryTask - let ctx = newTaskCtx(QResult, signal=signal) dispatchTaskWrap(self, signal): - self.tp.spawn queryTask(ctx, ds, query, nextSignal) + self.tp.spawn queryTask(ctx, ds, query) await nextSignal.fire() var diff --git a/datastore/threads/threadresult.nim b/datastore/threads/threadresult.nim index da0df6dc..9caa70c9 100644 --- a/datastore/threads/threadresult.nim +++ b/datastore/threads/threadresult.nim @@ -1,5 +1,6 @@ import std/atomics import std/options +import std/locks import pkg/questionable/results import pkg/results @@ -51,3 +52,24 @@ proc toRes*[T,S](res: ThreadResult[T], result.err res.error().toExc() else: result.ok m(res.get()) + +type + MutexSignal* = tuple[lock: Lock, cond: Cond, open: bool] + +proc open*(sig: var MutexSignal) = + sig.lock.initLock() + sig.cond.initCond() + sig.open = true + +proc waitSync*(sig: var MutexSignal) = + withLock(sig.lock): + wait(sig.cond, sig.lock) + +proc fireSync*(sig: var MutexSignal) = + withLock(sig.lock): + signal(sig.cond) + +proc close*(sig: var MutexSignal) = + if sig.open: + sig.lock.deinitLock() + sig.cond.deinitCond() From e571d2116a986fb7f2233e084170a182afee9b05 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 13:28:58 -0700 Subject: [PATCH 14/74] add nextSignal using mutex --- datastore/threads/threadproxyds.nim | 14 ++++++-------- datastore/threads/threadresult.nim | 6 +++--- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/datastore/threads/threadproxyds.nim b/datastore/threads/threadproxyds.nim index 1c5528ba..8a543b81 100644 --- a/datastore/threads/threadproxyds.nim +++ b/datastore/threads/threadproxyds.nim @@ -260,8 +260,7 @@ method queryTask[DB]( # otherwise manually an set empty ok result ctx[].res.ok (KeyId.none, DataBuffer(), ) discard ctx[].signal.fireSync() - if not nextSignal.waitSync(10.seconds).get(): - raise newException(DeadThreadDefect, "query task timeout; possible deadlock!") + ctx[].nextSignal.wait() var handle = handleRes.get() for item in handle.iter(): @@ -276,8 +275,7 @@ method queryTask[DB]( exc discard ctx[].signal.fireSync() - - discard nextSignal.waitSync().get() + ctx[].nextSignal.wait() # set final result (?!QResult).ok((KeyId.none, DataBuffer())) @@ -302,7 +300,7 @@ method query*[BT](self: ThreadDatastore[BT], # setup initial queryTask dispatchTaskWrap(self, signal): self.tp.spawn queryTask(ctx, ds, query) - await nextSignal.fire() + ctx[].nextSignal.fire() var lock = newAsyncLock() # serialize querying under threads @@ -323,7 +321,7 @@ method query*[BT](self: ThreadDatastore[BT], iter.finished = true defer: - await nextSignal.fire() + ctx[].nextSignal.fire() if ctx[].res.isErr(): return err(ctx[].res.error()) @@ -337,7 +335,7 @@ method query*[BT](self: ThreadDatastore[BT], ctx.setCancelled() discard ctx[].signal.close() echo "nextSignal:CLOSE!" - discard nextSignal.close() + ctx[].nextSignal.close() self.semaphore.release() raise exc @@ -347,7 +345,7 @@ method query*[BT](self: ThreadDatastore[BT], trace "Cancelling thread future!", exc = exc.msg discard signal.close() echo "nextSignal:CLOSE!" - discard nextSignal.close() + ctx[].nextSignal.close() self.semaphore.release() raise exc diff --git a/datastore/threads/threadresult.nim b/datastore/threads/threadresult.nim index 9caa70c9..3508e9ee 100644 --- a/datastore/threads/threadresult.nim +++ b/datastore/threads/threadresult.nim @@ -56,16 +56,16 @@ proc toRes*[T,S](res: ThreadResult[T], type MutexSignal* = tuple[lock: Lock, cond: Cond, open: bool] -proc open*(sig: var MutexSignal) = +proc init*(sig: var MutexSignal) = sig.lock.initLock() sig.cond.initCond() sig.open = true -proc waitSync*(sig: var MutexSignal) = +proc wait*(sig: var MutexSignal) = withLock(sig.lock): wait(sig.cond, sig.lock) -proc fireSync*(sig: var MutexSignal) = +proc fire*(sig: var MutexSignal) = withLock(sig.lock): signal(sig.cond) From 3cc21b3a59279cc01b971b12e6258c5e0dcaa56c Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 13:30:14 -0700 Subject: [PATCH 15/74] add nextSignal using mutex --- tests/datastore/testthreadproxyds.nim | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/datastore/testthreadproxyds.nim b/tests/datastore/testthreadproxyds.nim index 57fea886..c5b6ab0f 100644 --- a/tests/datastore/testthreadproxyds.nim +++ b/tests/datastore/testthreadproxyds.nim @@ -24,7 +24,7 @@ import ./querycommontests const NumThreads = 20 # IO threads aren't attached to CPU count - ThreadTestLoops {.intdefine.} = 100 + ThreadTestLoops {.intdefine.} = 1000 N = ThreadTestLoops for i in 1..N: @@ -60,9 +60,6 @@ for i in 1..N: sqlStore = newSQLiteBackend[KeyId, DataBuffer](Memory).tryGet() taskPool = Taskpool.new(NumThreads) ds = ThreadDatastore.new(sqlStore, tp = taskPool).tryGet() - key = Key.init("/a/b").tryGet() - bytes = "some bytes".toBytes - otherBytes = "some other bytes".toBytes setup: sqlStore = newSQLiteBackend[KeyId, DataBuffer](Memory).tryGet() From 1da59ba730c03e1dae30bd2607b2c29ab0991782 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 13:35:14 -0700 Subject: [PATCH 16/74] add nextSignal using mutex --- datastore/threads/threadproxyds.nim | 3 +++ tests/datastore/testthreadproxyds.nim | 10 +++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/datastore/threads/threadproxyds.nim b/datastore/threads/threadproxyds.nim index 8a543b81..ae9f64c7 100644 --- a/datastore/threads/threadproxyds.nim +++ b/datastore/threads/threadproxyds.nim @@ -131,6 +131,7 @@ template dispatchTask[BT](self: ThreadDatastore[BT], ctx.setCancelled() raise exc finally: + echo "signal:CLOSE!" discard ctx[].signal.close() self.semaphore.release() @@ -333,6 +334,7 @@ method query*[BT](self: ThreadDatastore[BT], except CancelledError as exc: trace "Cancelling thread future!", exc = exc.msg ctx.setCancelled() + echo "signal:CLOSE!" discard ctx[].signal.close() echo "nextSignal:CLOSE!" ctx[].nextSignal.close() @@ -343,6 +345,7 @@ method query*[BT](self: ThreadDatastore[BT], return success iter except CancelledError as exc: trace "Cancelling thread future!", exc = exc.msg + echo "signal:CLOSE!" discard signal.close() echo "nextSignal:CLOSE!" ctx[].nextSignal.close() diff --git a/tests/datastore/testthreadproxyds.nim b/tests/datastore/testthreadproxyds.nim index c5b6ab0f..e0cb8229 100644 --- a/tests/datastore/testthreadproxyds.nim +++ b/tests/datastore/testthreadproxyds.nim @@ -24,8 +24,10 @@ import ./querycommontests const NumThreads = 20 # IO threads aren't attached to CPU count - ThreadTestLoops {.intdefine.} = 1000 + ThreadTestLoops {.intdefine.} = 1 N = ThreadTestLoops + ThreadTestInnerLoops {.intdefine.} = 1 + M = ThreadTestInnerLoops for i in 1..N: suite "Test Basic ThreadDatastore with SQLite": @@ -50,7 +52,8 @@ for i in 1..N: (await ds.close()).tryGet() taskPool.shutdown() - basicStoreTests(ds, key, bytes, otherBytes) + for i in 1..M: + basicStoreTests(ds, key, bytes, otherBytes) GC_fullCollect() for i in 1..N: @@ -72,7 +75,8 @@ for i in 1..N: (await ds.close()).tryGet() taskPool.shutdown() - queryTests(ds, true) + for i in 1..M: + queryTests(ds, true) GC_fullCollect() # suite "Test Basic ThreadDatastore with fsds": From ac77917146c3bdeed72dc2bc9e4d7e75f61f6da6 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 13:36:16 -0700 Subject: [PATCH 17/74] remove ctx locks --- datastore/threads/threadproxyds.nim | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/datastore/threads/threadproxyds.nim b/datastore/threads/threadproxyds.nim index ae9f64c7..8c84fc72 100644 --- a/datastore/threads/threadproxyds.nim +++ b/datastore/threads/threadproxyds.nim @@ -53,27 +53,22 @@ type semaphore: AsyncSemaphore # semaphore is used for backpressure \ # to avoid exhausting file descriptors -var ctxLock: Lock -ctxLock.initLock() - proc newTaskCtx*[T](tp: typedesc[T], signal: ThreadSignalPtr): TaskCtx[T] = newSharedPtr(TaskCtxObj[T](signal: signal)) proc setCancelled[T](ctx: TaskCtx[T]) = - # withLock(ctxLock): ctx[].cancelled = true proc setRunning[T](ctx: TaskCtx[T]): bool = - # withLock(ctxLock): if ctx[].cancelled: return false ctx[].running = true return true proc setDone[T](ctx: TaskCtx[T]) = - # withLock(ctxLock): ctx[].running = false proc acquireSignal(): ?!ThreadSignalPtr = + echo "signal:OPEN!" let signal = ThreadSignalPtr.new() if signal.isErr(): failure (ref CatchableError)(msg: "failed to aquire ThreadSignalPtr: " & signal.error()) From ca5695f2c8fc87dd2d5e8803ddc35184e1d34111 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 13:37:12 -0700 Subject: [PATCH 18/74] remove ctx locks --- datastore/threads/threadproxyds.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/datastore/threads/threadproxyds.nim b/datastore/threads/threadproxyds.nim index 8c84fc72..dbe25541 100644 --- a/datastore/threads/threadproxyds.nim +++ b/datastore/threads/threadproxyds.nim @@ -286,6 +286,7 @@ method query*[BT](self: ThreadDatastore[BT], without signal =? acquireSignal(), err: return failure err let ctx = newTaskCtx(QResult, signal=signal) + echo "nextSignal:OPEN!" ctx[].nextSignal.init() try: From fbc00613c477b9c603316f43c2116094843f5d11 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 13:50:21 -0700 Subject: [PATCH 19/74] remove ctx locks --- datastore/threads/threadproxyds.nim | 31 +++++++++++++-------------- tests/datastore/testthreadproxyds.nim | 4 ++-- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/datastore/threads/threadproxyds.nim b/datastore/threads/threadproxyds.nim index dbe25541..4a6e5146 100644 --- a/datastore/threads/threadproxyds.nim +++ b/datastore/threads/threadproxyds.nim @@ -289,6 +289,13 @@ method query*[BT](self: ThreadDatastore[BT], echo "nextSignal:OPEN!" ctx[].nextSignal.init() + proc iterDispose() = + echo "signal:CLOSE!" + discard signal.close() + echo "nextSignal:CLOSE!" + ctx[].nextSignal.close() + self.semaphore.release() + try: let query = dbQuery( key= KeyId.new q.key.id(), @@ -299,11 +306,13 @@ method query*[BT](self: ThreadDatastore[BT], self.tp.spawn queryTask(ctx, ds, query) ctx[].nextSignal.fire() - var - lock = newAsyncLock() # serialize querying under threads - iter = QueryIter.new() + var lock = newAsyncLock() # serialize querying under threads + var iter = QueryIter.new() + iter.dispose = proc (): Future[?!void] {.async.} = + iterDispose() + success() - proc next(): Future[?!QueryResponse] {.async.} = + iter.next = proc(): Future[?!QueryResponse] {.async.} = let ctx = ctx try: trace "About to query" @@ -313,7 +322,6 @@ method query*[BT](self: ThreadDatastore[BT], return failure (ref QueryEndedError)(msg: "Calling next on a finished query!") await wait(ctx[].signal) - if not ctx[].running: iter.finished = true @@ -330,22 +338,13 @@ method query*[BT](self: ThreadDatastore[BT], except CancelledError as exc: trace "Cancelling thread future!", exc = exc.msg ctx.setCancelled() - echo "signal:CLOSE!" - discard ctx[].signal.close() - echo "nextSignal:CLOSE!" - ctx[].nextSignal.close() - self.semaphore.release() + iterDispose() raise exc - iter.next = next return success iter except CancelledError as exc: trace "Cancelling thread future!", exc = exc.msg - echo "signal:CLOSE!" - discard signal.close() - echo "nextSignal:CLOSE!" - ctx[].nextSignal.close() - self.semaphore.release() + iterDispose() raise exc proc new*[DB](self: type ThreadDatastore, diff --git a/tests/datastore/testthreadproxyds.nim b/tests/datastore/testthreadproxyds.nim index e0cb8229..0926f07e 100644 --- a/tests/datastore/testthreadproxyds.nim +++ b/tests/datastore/testthreadproxyds.nim @@ -52,8 +52,8 @@ for i in 1..N: (await ds.close()).tryGet() taskPool.shutdown() - for i in 1..M: - basicStoreTests(ds, key, bytes, otherBytes) + # for i in 1..M: + # basicStoreTests(ds, key, bytes, otherBytes) GC_fullCollect() for i in 1..N: From efd2e1d19d9f792fe7b5e4642e737634c2d34191 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 14:01:21 -0700 Subject: [PATCH 20/74] running 1000+ outer loops --- datastore/threads/threadproxyds.nim | 10 +++++----- tests/datastore/testthreadproxyds.nim | 16 ++++++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/datastore/threads/threadproxyds.nim b/datastore/threads/threadproxyds.nim index 4a6e5146..3085d6c6 100644 --- a/datastore/threads/threadproxyds.nim +++ b/datastore/threads/threadproxyds.nim @@ -68,7 +68,7 @@ proc setDone[T](ctx: TaskCtx[T]) = ctx[].running = false proc acquireSignal(): ?!ThreadSignalPtr = - echo "signal:OPEN!" + # echo "signal:OPEN!" let signal = ThreadSignalPtr.new() if signal.isErr(): failure (ref CatchableError)(msg: "failed to aquire ThreadSignalPtr: " & signal.error()) @@ -126,7 +126,7 @@ template dispatchTask[BT](self: ThreadDatastore[BT], ctx.setCancelled() raise exc finally: - echo "signal:CLOSE!" + # echo "signal:CLOSE!" discard ctx[].signal.close() self.semaphore.release() @@ -286,13 +286,13 @@ method query*[BT](self: ThreadDatastore[BT], without signal =? acquireSignal(), err: return failure err let ctx = newTaskCtx(QResult, signal=signal) - echo "nextSignal:OPEN!" + # echo "nextSignal:OPEN!" ctx[].nextSignal.init() proc iterDispose() = - echo "signal:CLOSE!" + # echo "signal:CLOSE!" discard signal.close() - echo "nextSignal:CLOSE!" + # echo "nextSignal:CLOSE!" ctx[].nextSignal.close() self.semaphore.release() diff --git a/tests/datastore/testthreadproxyds.nim b/tests/datastore/testthreadproxyds.nim index 0926f07e..8ab53399 100644 --- a/tests/datastore/testthreadproxyds.nim +++ b/tests/datastore/testthreadproxyds.nim @@ -33,9 +33,9 @@ for i in 1..N: suite "Test Basic ThreadDatastore with SQLite": var - sqlStore = newSQLiteBackend[KeyId, DataBuffer](Memory).tryGet() - taskPool = Taskpool.new(NumThreads) - ds = ThreadDatastore.new(sqlStore, tp = taskPool).tryGet() + sqlStore: SQLiteBackend[KeyId, DataBuffer] + taskPool: Taskpool + ds: ThreadDatastore[SQLiteBackend[KeyId, DataBuffer]] key = Key.init("/a/b").tryGet() bytes = "some bytes".toBytes otherBytes = "some other bytes".toBytes @@ -52,17 +52,17 @@ for i in 1..N: (await ds.close()).tryGet() taskPool.shutdown() - # for i in 1..M: - # basicStoreTests(ds, key, bytes, otherBytes) + for i in 1..M: + basicStoreTests(ds, key, bytes, otherBytes) GC_fullCollect() for i in 1..N: suite "Test Query ThreadDatastore with SQLite": var - sqlStore = newSQLiteBackend[KeyId, DataBuffer](Memory).tryGet() - taskPool = Taskpool.new(NumThreads) - ds = ThreadDatastore.new(sqlStore, tp = taskPool).tryGet() + sqlStore: SQLiteBackend[KeyId, DataBuffer] + taskPool: Taskpool + ds: ThreadDatastore[SQLiteBackend[KeyId, DataBuffer]] setup: sqlStore = newSQLiteBackend[KeyId, DataBuffer](Memory).tryGet() From a4748ef4c6585164734e56643fe284522c832f21 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 15:30:21 -0700 Subject: [PATCH 21/74] running 1000+ outer loops --- tests/datastore/testthreadproxyds.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/datastore/testthreadproxyds.nim b/tests/datastore/testthreadproxyds.nim index 8ab53399..841dfcdf 100644 --- a/tests/datastore/testthreadproxyds.nim +++ b/tests/datastore/testthreadproxyds.nim @@ -30,7 +30,7 @@ const M = ThreadTestInnerLoops for i in 1..N: - suite "Test Basic ThreadDatastore with SQLite": + suite "Test Basic ThreadDatastore with SQLite " & $i: var sqlStore: SQLiteBackend[KeyId, DataBuffer] @@ -57,7 +57,7 @@ for i in 1..N: GC_fullCollect() for i in 1..N: - suite "Test Query ThreadDatastore with SQLite": + suite "Test Query ThreadDatastore with SQLite " & $i: var sqlStore: SQLiteBackend[KeyId, DataBuffer] From 78ea3b117e9916904e513425b1fb77c42480649f Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 16:01:40 -0700 Subject: [PATCH 22/74] global taskpool --- datastore/threads/threadproxyds.nim | 2 ++ tests/datastore/testthreadproxyds.nim | 17 ++++++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/datastore/threads/threadproxyds.nim b/datastore/threads/threadproxyds.nim index 3085d6c6..c192b063 100644 --- a/datastore/threads/threadproxyds.nim +++ b/datastore/threads/threadproxyds.nim @@ -291,6 +291,8 @@ method query*[BT](self: ThreadDatastore[BT], proc iterDispose() = # echo "signal:CLOSE!" + ctx.setCancelled() + ctx[].nextSignal.fire() discard signal.close() # echo "nextSignal:CLOSE!" ctx[].nextSignal.close() diff --git a/tests/datastore/testthreadproxyds.nim b/tests/datastore/testthreadproxyds.nim index 841dfcdf..9bb4849b 100644 --- a/tests/datastore/testthreadproxyds.nim +++ b/tests/datastore/testthreadproxyds.nim @@ -29,12 +29,14 @@ const ThreadTestInnerLoops {.intdefine.} = 1 M = ThreadTestInnerLoops -for i in 1..N: +var + taskPool: Taskpool = Taskpool.new(NumThreads) + +for i in 1..1: suite "Test Basic ThreadDatastore with SQLite " & $i: var sqlStore: SQLiteBackend[KeyId, DataBuffer] - taskPool: Taskpool ds: ThreadDatastore[SQLiteBackend[KeyId, DataBuffer]] key = Key.init("/a/b").tryGet() bytes = "some bytes".toBytes @@ -42,7 +44,7 @@ for i in 1..N: setupAll: sqlStore = newSQLiteBackend[KeyId, DataBuffer](Memory).tryGet() - taskPool = Taskpool.new(NumThreads) + # taskPool = Taskpool.new(NumThreads) ds = ThreadDatastore.new(sqlStore, tp = taskPool).tryGet() teardown: @@ -50,30 +52,31 @@ for i in 1..N: teardownAll: (await ds.close()).tryGet() - taskPool.shutdown() + # taskPool.shutdown() for i in 1..M: basicStoreTests(ds, key, bytes, otherBytes) GC_fullCollect() + for i in 1..N: suite "Test Query ThreadDatastore with SQLite " & $i: var sqlStore: SQLiteBackend[KeyId, DataBuffer] - taskPool: Taskpool + # taskPool: Taskpool ds: ThreadDatastore[SQLiteBackend[KeyId, DataBuffer]] setup: sqlStore = newSQLiteBackend[KeyId, DataBuffer](Memory).tryGet() - taskPool = Taskpool.new(NumThreads) + # taskPool = Taskpool.new(NumThreads) ds = ThreadDatastore.new(sqlStore, tp = taskPool).tryGet() teardown: GC_fullCollect() (await ds.close()).tryGet() - taskPool.shutdown() + # taskPool.shutdown() for i in 1..M: queryTests(ds, true) From 5afec2b3d8cfefb74216ee8ac3c9cad21ed3b011 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 16:16:18 -0700 Subject: [PATCH 23/74] change nextSignal back to ThreadSignalPtr for timeouts --- datastore/threads/threadproxyds.nim | 38 ++++++++++++++++------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/datastore/threads/threadproxyds.nim b/datastore/threads/threadproxyds.nim index c192b063..8be8a8a5 100644 --- a/datastore/threads/threadproxyds.nim +++ b/datastore/threads/threadproxyds.nim @@ -41,7 +41,7 @@ type signal: ThreadSignalPtr running: bool ## used to mark when a task worker is running cancelled: bool ## used to cancel a task before it's started - nextSignal: MutexSignal + nextSignal: ThreadSignalPtr TaskCtx*[T] = SharedPtr[TaskCtxObj[T]] ## Task context object. @@ -53,8 +53,10 @@ type semaphore: AsyncSemaphore # semaphore is used for backpressure \ # to avoid exhausting file descriptors -proc newTaskCtx*[T](tp: typedesc[T], signal: ThreadSignalPtr): TaskCtx[T] = - newSharedPtr(TaskCtxObj[T](signal: signal)) +proc newTaskCtx*[T](tp: typedesc[T], + signal: ThreadSignalPtr, + nextSignal: ThreadSignalPtr = nil): TaskCtx[T] = + newSharedPtr(TaskCtxObj[T](signal: signal, nextSignal: nextSignal)) proc setCancelled[T](ctx: TaskCtx[T]) = ctx[].cancelled = true @@ -137,7 +139,7 @@ proc hasTask[T, DB](ctx: TaskCtx[T], ds: DB, key: KeyId) {.gcsafe.} = has(ds, key) method has*[BT](self: ThreadDatastore[BT], - key: Key): Future[?!bool] {.async.} = + key: Key): Future[?!bool] {.async.} = await self.semaphore.acquire() without signal =? acquireSignal(), err: return failure err @@ -256,7 +258,8 @@ method queryTask[DB]( # otherwise manually an set empty ok result ctx[].res.ok (KeyId.none, DataBuffer(), ) discard ctx[].signal.fireSync() - ctx[].nextSignal.wait() + if not ctx[].nextSignal.waitSync(10.seconds).get(): + raise newException(DeadThreadDefect, "queryTask timed out") var handle = handleRes.get() for item in handle.iter(): @@ -271,7 +274,8 @@ method queryTask[DB]( exc discard ctx[].signal.fireSync() - ctx[].nextSignal.wait() + if not ctx[].nextSignal.waitSync(10.seconds).get(): + raise newException(DeadThreadDefect, "queryTask timed out") # set final result (?!QResult).ok((KeyId.none, DataBuffer())) @@ -285,17 +289,17 @@ method query*[BT](self: ThreadDatastore[BT], await self.semaphore.acquire() without signal =? acquireSignal(), err: return failure err - let ctx = newTaskCtx(QResult, signal=signal) - # echo "nextSignal:OPEN!" - ctx[].nextSignal.init() + without nextSignal =? acquireSignal(), err: + return failure err + let ctx = newTaskCtx(QResult, signal=signal, nextSignal=nextSignal) - proc iterDispose() = + proc iterDispose() {.async.} = # echo "signal:CLOSE!" ctx.setCancelled() - ctx[].nextSignal.fire() - discard signal.close() + await ctx[].nextSignal.fire() + discard ctx[].signal.close() # echo "nextSignal:CLOSE!" - ctx[].nextSignal.close() + discard ctx[].nextSignal.close() self.semaphore.release() try: @@ -306,7 +310,7 @@ method query*[BT](self: ThreadDatastore[BT], # setup initial queryTask dispatchTaskWrap(self, signal): self.tp.spawn queryTask(ctx, ds, query) - ctx[].nextSignal.fire() + await ctx[].nextSignal.fire() var lock = newAsyncLock() # serialize querying under threads var iter = QueryIter.new() @@ -328,7 +332,7 @@ method query*[BT](self: ThreadDatastore[BT], iter.finished = true defer: - ctx[].nextSignal.fire() + await ctx[].nextSignal.fire() if ctx[].res.isErr(): return err(ctx[].res.error()) @@ -340,13 +344,13 @@ method query*[BT](self: ThreadDatastore[BT], except CancelledError as exc: trace "Cancelling thread future!", exc = exc.msg ctx.setCancelled() - iterDispose() + await iterDispose() # todo: is this valid? raise exc return success iter except CancelledError as exc: trace "Cancelling thread future!", exc = exc.msg - iterDispose() + await iterDispose() raise exc proc new*[DB](self: type ThreadDatastore, From 69529876268f305651f0e65eca300395e7bfa66b Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 16:17:02 -0700 Subject: [PATCH 24/74] running 1000+ outer loops --- tests/datastore/testthreadproxyds.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/datastore/testthreadproxyds.nim b/tests/datastore/testthreadproxyds.nim index 9bb4849b..d59992cc 100644 --- a/tests/datastore/testthreadproxyds.nim +++ b/tests/datastore/testthreadproxyds.nim @@ -32,7 +32,7 @@ const var taskPool: Taskpool = Taskpool.new(NumThreads) -for i in 1..1: +for i in 1..N: suite "Test Basic ThreadDatastore with SQLite " & $i: var From 74953e175b58dc5a7e8d9d09661c2e1a10ece2c4 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 16:30:47 -0700 Subject: [PATCH 25/74] try newer questionable? --- datastore.nimble | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datastore.nimble b/datastore.nimble index 3b5b28ef..bcd31398 100644 --- a/datastore.nimble +++ b/datastore.nimble @@ -9,7 +9,7 @@ license = "Apache License 2.0 or MIT" requires "nim >= 1.6.14", "asynctest >= 0.3.1 & < 0.4.0", "chronos#0277b65be2c7a365ac13df002fba6e172be55537", - "questionable >= 0.10.3 & < 0.11.0", + "questionable", "sqlite3_abi", "stew", "unittest2", From 9fdba84aac85090c00bb27a92c9da3690f12e270 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 17:07:50 -0700 Subject: [PATCH 26/74] try newer questionable? --- datastore/fsds.nim | 54 +++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index b3162d82..d4c9ceff 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -20,18 +20,18 @@ type ignoreProtected: bool depth: int -proc validDepth*(self: FSDatastore, key: Key): bool = - key.len <= self.depth - proc isRootSubdir*(root, path: string): bool = path.startsWith(root) -proc path*(self: FSDatastore, key: Key): ?!string = +proc path*(root: string, depth: int, key: Key): ?!string = ## Return filename corresponding to the key ## or failure if the key doesn't correspond to a valid filename ## - if not self.validDepth(key): + proc validDepth(depth: int, key: Key): bool = + key.len <= depth + + if not validDepth(depth, key): return failure "Path has invalid depth!" var @@ -53,7 +53,7 @@ proc path*(self: FSDatastore, key: Key): ?!string = segments.add(ns.field / ns.value) let - root = $self.root + root = root fullname = (root / segments.joinPath()) .absolutePath() .catch() @@ -65,6 +65,9 @@ proc path*(self: FSDatastore, key: Key): ?!string = return success fullname +proc path*(self: FSDatastore, key: Key): ?!string = + path($self.root, self.depth, key) + proc has*(self: FSDatastore, key: KeyId): ?!bool = let key = key.toKey() return self.path(key).?fileExists() @@ -166,23 +169,23 @@ proc put*( return success() -proc dirWalker(path: string): iterator: string {.gcsafe.} = - var localPath {.threadvar.}: string - - localPath = path - return iterator(): string = - try: - for p in path.walkDirRec(yieldFilter = {pcFile}, relative = true): - yield p - except CatchableError as exc: - raise newException(Defect, exc.msg) +iterator dirIter(path: string): string {.gcsafe.} = + try: + for p in path.walkDirRec(yieldFilter = {pcFile}, relative = true): + yield p + except CatchableError as exc: + raise newException(Defect, exc.msg) proc close*(self: FSDatastore): ?!void = return success() +type + FsQueryEnv* = tuple[path: DataBuffer, root: DataBuffer] + proc query*( self: FSDatastore, - query: DbQuery[KeyId]): ?!QueryIter = + query: DbQuery[KeyId], +): Result[DbQueryHandle[KeyId, DataBuffer, KeyId], ref CatchableError] = let key = query.key.toKey() without path =? self.path(key), error: @@ -198,20 +201,13 @@ proc query*( else: path.changeFileExt("") - let - walker = dirWalker(basePath) - var - iter = QueryIter.new() - # var lock = newAsyncLock() # serialize querying under threads - proc next(): ?!QueryResponse = - # defer: - # if lock.locked: - # lock.release() +iterator iter*[K, V](handle: var DbQueryHandle[K, V, DataBuffer]): ?!DbQueryResponse[K, V] = - # if lock.locked: - # return failure (ref DatastoreError)(msg: "Should always await query features") + let root = $(handle.env) + + for path in root.dirIter(): let root = $self.root @@ -240,7 +236,7 @@ proc query*( readFile[DataBuffer](self, (basePath / path).absolutePath) .expect("Should read file") else: - @[] + DataBuffer.new(0) return success (key.some, data) From 10bad7e70d3a05857ac8e30982174a894fabaf48 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 17:09:38 -0700 Subject: [PATCH 27/74] try newer questionable? --- datastore/fsds.nim | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index d4c9ceff..5b221b0e 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -23,7 +23,7 @@ type proc isRootSubdir*(root, path: string): bool = path.startsWith(root) -proc path*(root: string, depth: int, key: Key): ?!string = +proc findPath*(root: string, depth: int, key: Key): ?!string = ## Return filename corresponding to the key ## or failure if the key doesn't correspond to a valid filename ## @@ -65,17 +65,17 @@ proc path*(root: string, depth: int, key: Key): ?!string = return success fullname -proc path*(self: FSDatastore, key: Key): ?!string = - path($self.root, self.depth, key) +proc findPath*(self: FSDatastore, key: Key): ?!string = + findPath($self.root, self.depth, key) proc has*(self: FSDatastore, key: KeyId): ?!bool = let key = key.toKey() - return self.path(key).?fileExists() + return self.findPath(key).?fileExists() proc delete*(self: FSDatastore, key: KeyId): ?!void = let key = key.toKey() - without path =? self.path(key), error: + without path =? self.findPath(key), error: return failure error if not path.fileExists(): @@ -132,7 +132,7 @@ proc readFile*[V](self: FSDatastore, path: string): ?!V = proc get*(self: FSDatastore, key: KeyId): ?!DataBuffer = let key = key.toKey() - without path =? self.path(key), error: + without path =? self.findPath(key), error: return failure error if not path.fileExists(): @@ -147,7 +147,7 @@ proc put*( data: DataBuffer): ?!void = let key = key.toKey() - without path =? self.path(key), error: + without path =? self.findPath(key), error: return failure error try: @@ -188,7 +188,7 @@ proc query*( ): Result[DbQueryHandle[KeyId, DataBuffer, KeyId], ref CatchableError] = let key = query.key.toKey() - without path =? self.path(key), error: + without path =? self.findPath(key), error: return failure error let basePath = From 2eb0ee6e4ec8044404b31958f99bf0b915e02fe3 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 17:18:29 -0700 Subject: [PATCH 28/74] update fsds --- datastore/fsds.nim | 50 +++++++++++++--------------------------------- 1 file changed, 14 insertions(+), 36 deletions(-) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index 5b221b0e..3e4891fd 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -23,15 +23,15 @@ type proc isRootSubdir*(root, path: string): bool = path.startsWith(root) -proc findPath*(root: string, depth: int, key: Key): ?!string = +proc validDepth(self: FSDatastore, key: Key): bool = + key.len <= self.depth + +proc findPath*(self: FSDatastore, key: Key): ?!string = ## Return filename corresponding to the key ## or failure if the key doesn't correspond to a valid filename ## - - proc validDepth(depth: int, key: Key): bool = - key.len <= depth - - if not validDepth(depth, key): + let root = $self.root + if not self.validDepth(key): return failure "Path has invalid depth!" var @@ -53,7 +53,6 @@ proc findPath*(root: string, depth: int, key: Key): ?!string = segments.add(ns.field / ns.value) let - root = root fullname = (root / segments.joinPath()) .absolutePath() .catch() @@ -65,9 +64,6 @@ proc findPath*(root: string, depth: int, key: Key): ?!string = return success fullname -proc findPath*(self: FSDatastore, key: Key): ?!string = - findPath($self.root, self.depth, key) - proc has*(self: FSDatastore, key: KeyId): ?!bool = let key = key.toKey() return self.findPath(key).?fileExists() @@ -95,7 +91,7 @@ proc delete*(self: FSDatastore, keys: openArray[KeyId]): ?!void = return success() -proc readFile*[V](self: FSDatastore, path: string): ?!V = +proc readFile[V](self: FSDatastore, path: string): ?!V = var file: File @@ -180,7 +176,7 @@ proc close*(self: FSDatastore): ?!void = return success() type - FsQueryEnv* = tuple[path: DataBuffer, root: DataBuffer] + FsQueryEnv* = tuple[basePath: DataBuffer, self: FSDatastore] proc query*( self: FSDatastore, @@ -201,29 +197,14 @@ proc query*( else: path.changeFileExt("") - - iterator iter*[K, V](handle: var DbQueryHandle[K, V, DataBuffer]): ?!DbQueryResponse[K, V] = - let root = $(handle.env) - - for path in root.dirIter(): - - let - root = $self.root - path = walker() - - if iter.finished: - return failure "iterator is finished" - # await lock.acquire() - - if finished(walker): - iter.finished = true - return success (Key.none, EmptyBytes) + for path in root.dirIter(): + if handle.cancel: + return - var - keyPath = basePath + var keyPath = handle.basePath keyPath.removePrefix(root) keyPath = keyPath / path.changeFileExt("") @@ -233,16 +214,13 @@ iterator iter*[K, V](handle: var DbQueryHandle[K, V, DataBuffer]): ?!DbQueryResp key = Key.init(keyPath).expect("should not fail") data = if query.value: - readFile[DataBuffer](self, (basePath / path).absolutePath) - .expect("Should read file") + let fl = (handle.env.basePath / path).absolutePath() + readFile[DataBuffer](handle.env.self, fl).expect("Should read file") else: DataBuffer.new(0) return success (key.some, data) - iter.next = next - return success iter - proc new*( T: type FSDatastore, root: string, From 131712071fddc98ead38d833cc71fb05c3e087ad Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 17:26:23 -0700 Subject: [PATCH 29/74] update fsds --- datastore/fsds.nim | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index 3e4891fd..8f308c9e 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -211,15 +211,18 @@ iterator iter*[K, V](handle: var DbQueryHandle[K, V, DataBuffer]): ?!DbQueryResp keyPath = keyPath.replace("\\", "/") let + fl = (handle.env.basePath / path).absolutePath() key = Key.init(keyPath).expect("should not fail") data = if query.value: - let fl = (handle.env.basePath / path).absolutePath() - readFile[DataBuffer](handle.env.self, fl).expect("Should read file") + let res = readFile[DataBuffer](handle.env.self, fl) + if res.isErr(): + yield failure res.error() + res.get() else: - DataBuffer.new(0) + DataBuffer.new() - return success (key.some, data) + yield success (key.some, data) proc new*( T: type FSDatastore, From bda61df77e10dc40aa8350dd3fb6b0df65098d02 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 17:31:22 -0700 Subject: [PATCH 30/74] update fsds --- datastore/fsds.nim | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index 8f308c9e..13181581 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -176,12 +176,12 @@ proc close*(self: FSDatastore): ?!void = return success() type - FsQueryEnv* = tuple[basePath: DataBuffer, self: FSDatastore] + FsQueryEnv* = tuple[self: FSDatastore, basePath: DataBuffer] proc query*( self: FSDatastore, query: DbQuery[KeyId], -): Result[DbQueryHandle[KeyId, DataBuffer, KeyId], ref CatchableError] = +): Result[DbQueryHandle[KeyId, DataBuffer, FsQueryEnv], ref CatchableError] = let key = query.key.toKey() without path =? self.findPath(key), error: @@ -196,6 +196,9 @@ proc query*( path.parentDir else: path.changeFileExt("") + + let env: FsQueryEnv = (self: self, basePath: DataBuffer.new(basePath)) + success DbQueryHandle[KeyId, DataBuffer, FsQueryEnv](env: env) iterator iter*[K, V](handle: var DbQueryHandle[K, V, DataBuffer]): ?!DbQueryResponse[K, V] = let root = $(handle.env) From 36ec858dcca9225a27c200f516d19f65c33eb96b Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 18:06:23 -0700 Subject: [PATCH 31/74] refactor tests --- tests/datastore/backendCommonTests.nim | 301 +++++++++++++++++++++++++ tests/datastore/querycommontests.nim | 9 +- tests/datastore/sql/testsqliteds.nim | 60 +---- 3 files changed, 305 insertions(+), 65 deletions(-) create mode 100644 tests/datastore/backendCommonTests.nim diff --git a/tests/datastore/backendCommonTests.nim b/tests/datastore/backendCommonTests.nim new file mode 100644 index 00000000..d3273e03 --- /dev/null +++ b/tests/datastore/backendCommonTests.nim @@ -0,0 +1,301 @@ + +template testBasicBackend*[K, V, DB]( + ds: DB, + key: K, + bytes: V, + otherBytes: V, + batch: untyped, + extended = true +): untyped = + + test "put": + ds.put(key, bytes).tryGet() + + test "get": + check: + ds.get(key).tryGet() == bytes + + test "put update": + ds.put(key, otherBytes).tryGet() + + test "get updated": + check: + ds.get(key).tryGet() == otherBytes + + test "delete": + ds.delete(key).tryGet() + + test "contains": + check key notin ds + + test "put batch": + + ds.put(batch).tryGet + + for (k, v) in batch: + check: ds.has(k).tryGet + + test "delete batch": + var keys: seq[K] + for (k, v) in batch: + keys.add(k) + + ds.delete(keys).tryGet + + for (k, v) in batch: + check: not ds.has(k).tryGet + + test "handle missing key": + when K is KeyId: + let key = KeyId.new Key.init("/missing/key").tryGet().id() + elif K is string: + let key = $KeyId.new Key.init("/missing/key").tryGet().id() + + expect(DatastoreKeyNotFound): + discard ds.get(key).tryGet() # non existing key + +template queryTests*[K, V, DB]( + ds: DB, +) = + + setup: + let + key1 = KeyId.new "/a" + key2 = KeyId.new "/a/b" + key3 = KeyId.new "/a/b/c" + val1 = DataBuffer.new "value for 1" + val2 = DataBuffer.new "value for 2" + val3 = DataBuffer.new "value for 3" + + test "Key should query all keys and all it's children": + let + q = dbQuery(key=key1, value=true) + + ds.put(key1, val1).tryGet + ds.put(key2, val2).tryGet + ds.put(key3, val3).tryGet + + var + handle = ds.query(q).tryGet + res = handle.iter().toSeq().mapIt(it.tryGet()) + + check: + res.len == 3 + res[0].key.get == key1 + res[0].data == val1 + + res[1].key.get == key2 + res[1].data == val2 + + res[2].key.get == key3 + res[2].data == val3 + + test "query should cancel": + let + q = dbQuery(key= key1, value= true) + + ds.put(key1, val1).tryGet + ds.put(key2, val2).tryGet + ds.put(key3, val3).tryGet + + var + handle = ds.query(q).tryGet + + var res: seq[DbQueryResponse[KeyId, DataBuffer]] + var cnt = 0 + for item in handle.iter(): + cnt.inc + res.insert(item.tryGet(), 0) + if cnt > 1: + handle.cancel = true + + check: + handle.cancel == true + handle.closed == true + res.len == 2 + + res[0].key.get == key2 + res[0].data == val2 + + res[1].key.get == key1 + res[1].data == val1 + + test "Key should query all keys without values": + let + q = dbQuery(key= key1, value= false) + + ds.put(key1, val1).tryGet + ds.put(key2, val2).tryGet + ds.put(key3, val3).tryGet + + var + handle = ds.query(q).tryGet + let + res = handle.iter().toSeq().mapIt(it.tryGet()) + + check: + res.len == 3 + res[0].key.get == key1 + res[0].data.len == 0 + + res[1].key.get == key2 + res[1].data.len == 0 + + res[2].key.get == key3 + res[2].data.len == 0 + + + test "Key should not query parent": + let + q = dbQuery(key= key2, value= true) + + ds.put(key1, val1).tryGet + ds.put(key2, val2).tryGet + ds.put(key3, val3).tryGet + + var + handle = ds.query(q).tryGet + let + res = handle.iter().toSeq().mapIt(it.tryGet()) + + check: + res.len == 2 + res[0].key.get == key2 + res[0].data == val2 + + res[1].key.get == key3 + res[1].data == val3 + + test "Key should all list all keys at the same level": + let + queryKey = Key.init("/a").tryGet + q = dbQuery(key= key1, value= true) + + ds.put(key1, val1).tryGet + ds.put(key2, val2).tryGet + ds.put(key3, val3).tryGet + + var + handle = ds.query(q).tryGet + res = handle.iter().toSeq().mapIt(it.tryGet()) + + res.sort do (a, b: DbQueryResponse[KeyId, DataBuffer]) -> int: + cmp($a.key.get, $b.key.get) + + check: + res.len == 3 + res[0].key.get == key1 + res[0].data == val1 + + res[1].key.get == key2 + res[1].data == val2 + + res[2].key.get == key3 + res[2].data == val3 + + test "Should apply limit": + let + key = Key.init("/a").tryGet + q = dbQuery(key= key1, limit= 10, value= false) + + for i in 0..<100: + let + key = KeyId.new $Key.init(key, Key.init("/" & $i).tryGet).tryGet + val = DataBuffer.new("val " & $i) + + ds.put(key, val).tryGet + + var + handle = ds.query(q).tryGet + let + res = handle.iter().toSeq().mapIt(it.tryGet()) + + check: + res.len == 10 + + test "Should not apply offset": + let + key = Key.init("/a").tryGet + keyId = KeyId.new $key + q = dbQuery(key= keyId, offset= 90) + + for i in 0..<100: + let + key = KeyId.new $Key.init(key, Key.init("/" & $i).tryGet).tryGet + val = DataBuffer.new("val " & $i) + + ds.put(key, val).tryGet + + var + qr = ds.query(q) + # echo "RES: ", qr.repr + + var + handle = ds.query(q).tryGet + let + res = handle.iter().toSeq().mapIt(it.tryGet()) + + # echo "RES: ", res.mapIt(it.key) + check: + res.len == 10 + + test "Should not apply offset and limit": + let + key = Key.init("/a").tryGet + keyId = KeyId.new $key + q = dbQuery(key= keyId, offset= 95, limit= 5) + + for i in 0..<100: + let + key = KeyId.new $Key.init(key, Key.init("/" & $i).tryGet).tryGet + val = DataBuffer.new("val " & $i) + + ds.put(key, val).tryGet + + var + handle = ds.query(q).tryGet + res = handle.iter().toSeq().mapIt(it.tryGet()) + + check: + res.len == 5 + + for i in 0.. int: + cmp($a.key.get, $b.key.get) + + kvs = kvs.reversed + var + handle = ds.query(q).tryGet + res = handle.iter().toSeq().mapIt(it.tryGet()) + + check: + res.len == 100 + + for i, r in res[1..^1]: + check: + res[i].key.get == kvs[i].key.get + res[i].data == kvs[i].data \ No newline at end of file diff --git a/tests/datastore/querycommontests.nim b/tests/datastore/querycommontests.nim index 460804b7..0bba6e45 100644 --- a/tests/datastore/querycommontests.nim +++ b/tests/datastore/querycommontests.nim @@ -1,11 +1,4 @@ -import std/options -import std/sequtils -from std/algorithm import sort, reversed - -import pkg/asynctest -import pkg/chronos -import pkg/stew/results -import pkg/stew/byteutils + import pkg/datastore diff --git a/tests/datastore/sql/testsqliteds.nim b/tests/datastore/sql/testsqliteds.nim index 1309dcd3..ef57cbf6 100644 --- a/tests/datastore/sql/testsqliteds.nim +++ b/tests/datastore/sql/testsqliteds.nim @@ -11,62 +11,8 @@ import pkg/stew/byteutils import pkg/datastore/sql/sqliteds import pkg/datastore/key -import ../dscommontests +import ../backendCommonTests -proc testBasic[K, V]( - ds: SQLiteBackend[K,V], - key: K, - bytes: V, - otherBytes: V, - batch: seq[DbBatchEntry[K, V]], - extended = true -) = - - test "put": - ds.put(key, bytes).tryGet() - - test "get": - check: - ds.get(key).tryGet() == bytes - - test "put update": - ds.put(key, otherBytes).tryGet() - - test "get updated": - check: - ds.get(key).tryGet() == otherBytes - - test "delete": - ds.delete(key).tryGet() - - test "contains": - check key notin ds - - test "put batch": - - ds.put(batch).tryGet - - for (k, v) in batch: - check: ds.has(k).tryGet - - test "delete batch": - var keys: seq[K] - for (k, v) in batch: - keys.add(k) - - ds.delete(keys).tryGet - - for (k, v) in batch: - check: not ds.has(k).tryGet - - test "handle missing key": - when K is KeyId: - let key = KeyId.new Key.init("/missing/key").tryGet().id() - elif K is string: - let key = $KeyId.new Key.init("/missing/key").tryGet().id() - - expect(DatastoreKeyNotFound): - discard ds.get(key).tryGet() # non existing key suite "Test Basic SQLiteDatastore": let @@ -84,7 +30,7 @@ suite "Test Basic SQLiteDatastore": suiteTeardown: ds.close().tryGet() - testBasic(ds, key, bytes, otherBytes, batch) + testBasicBackend(ds, key, bytes, otherBytes, batch) suite "Test DataBuffer SQLiteDatastore": let @@ -102,7 +48,7 @@ suite "Test DataBuffer SQLiteDatastore": suiteTeardown: ds.close().tryGet() - testBasic(ds, key, bytes, otherBytes, batch) + testBasicBackend(ds, key, bytes, otherBytes, batch) suite "queryTests": From 439fd92d50d120f66d6c1859b4321a7ebf8cb894 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 18:11:28 -0700 Subject: [PATCH 32/74] refactor tests --- tests/datastore/backendCommonTests.nim | 20 +- tests/datastore/sql/testsqliteds.nim | 253 ++----------------------- 2 files changed, 23 insertions(+), 250 deletions(-) diff --git a/tests/datastore/backendCommonTests.nim b/tests/datastore/backendCommonTests.nim index d3273e03..e09e6121 100644 --- a/tests/datastore/backendCommonTests.nim +++ b/tests/datastore/backendCommonTests.nim @@ -54,18 +54,22 @@ template testBasicBackend*[K, V, DB]( expect(DatastoreKeyNotFound): discard ds.get(key).tryGet() # non existing key -template queryTests*[K, V, DB]( - ds: DB, +template queryTests*( + ds: untyped, + key1, key2, key3: untyped, + val1, val2, val3: untyped, + extended = true ) = setup: let - key1 = KeyId.new "/a" - key2 = KeyId.new "/a/b" - key3 = KeyId.new "/a/b/c" - val1 = DataBuffer.new "value for 1" - val2 = DataBuffer.new "value for 2" - val3 = DataBuffer.new "value for 3" + ds = dsNew() + key1 = key1 + key2 = key2 + key3 = key3 + val1 = val1 + val2 = val2 + val3 = val3 test "Key should query all keys and all it's children": let diff --git a/tests/datastore/sql/testsqliteds.nim b/tests/datastore/sql/testsqliteds.nim index ef57cbf6..18454ba6 100644 --- a/tests/datastore/sql/testsqliteds.nim +++ b/tests/datastore/sql/testsqliteds.nim @@ -52,245 +52,14 @@ suite "Test DataBuffer SQLiteDatastore": suite "queryTests": - setup: - let - ds = newSQLiteBackend[KeyId, DataBuffer](Memory).tryGet() - key1 = KeyId.new "/a" - key2 = KeyId.new "/a/b" - key3 = KeyId.new "/a/b/c" - val1 = DataBuffer.new "value for 1" - val2 = DataBuffer.new "value for 2" - val3 = DataBuffer.new "value for 3" - - test "Key should query all keys and all it's children": - let - q = dbQuery(key=key1, value=true) - - ds.put(key1, val1).tryGet - ds.put(key2, val2).tryGet - ds.put(key3, val3).tryGet - - var - handle = ds.query(q).tryGet - res = handle.iter().toSeq().mapIt(it.tryGet()) - - check: - res.len == 3 - res[0].key.get == key1 - res[0].data == val1 - - res[1].key.get == key2 - res[1].data == val2 - - res[2].key.get == key3 - res[2].data == val3 - - test "query should cancel": - let - q = dbQuery(key= key1, value= true) - - ds.put(key1, val1).tryGet - ds.put(key2, val2).tryGet - ds.put(key3, val3).tryGet - - var - handle = ds.query(q).tryGet - - var res: seq[DbQueryResponse[KeyId, DataBuffer]] - var cnt = 0 - for item in handle.iter(): - cnt.inc - res.insert(item.tryGet(), 0) - if cnt > 1: - handle.cancel = true - - check: - handle.cancel == true - handle.closed == true - res.len == 2 - - res[0].key.get == key2 - res[0].data == val2 - - res[1].key.get == key1 - res[1].data == val1 - - test "Key should query all keys without values": - let - q = dbQuery(key= key1, value= false) - - ds.put(key1, val1).tryGet - ds.put(key2, val2).tryGet - ds.put(key3, val3).tryGet - - var - handle = ds.query(q).tryGet - let - res = handle.iter().toSeq().mapIt(it.tryGet()) - - check: - res.len == 3 - res[0].key.get == key1 - res[0].data.len == 0 - - res[1].key.get == key2 - res[1].data.len == 0 - - res[2].key.get == key3 - res[2].data.len == 0 - - - test "Key should not query parent": - let - q = dbQuery(key= key2, value= true) - - ds.put(key1, val1).tryGet - ds.put(key2, val2).tryGet - ds.put(key3, val3).tryGet - - var - handle = ds.query(q).tryGet - let - res = handle.iter().toSeq().mapIt(it.tryGet()) - - check: - res.len == 2 - res[0].key.get == key2 - res[0].data == val2 - - res[1].key.get == key3 - res[1].data == val3 - - test "Key should all list all keys at the same level": - let - queryKey = Key.init("/a").tryGet - q = dbQuery(key= key1, value= true) - - ds.put(key1, val1).tryGet - ds.put(key2, val2).tryGet - ds.put(key3, val3).tryGet - - var - handle = ds.query(q).tryGet - res = handle.iter().toSeq().mapIt(it.tryGet()) - - res.sort do (a, b: DbQueryResponse[KeyId, DataBuffer]) -> int: - cmp($a.key.get, $b.key.get) - - check: - res.len == 3 - res[0].key.get == key1 - res[0].data == val1 - - res[1].key.get == key2 - res[1].data == val2 - - res[2].key.get == key3 - res[2].data == val3 - - test "Should apply limit": - let - key = Key.init("/a").tryGet - q = dbQuery(key= key1, limit= 10, value= false) - - for i in 0..<100: - let - key = KeyId.new $Key.init(key, Key.init("/" & $i).tryGet).tryGet - val = DataBuffer.new("val " & $i) - - ds.put(key, val).tryGet - - var - handle = ds.query(q).tryGet - let - res = handle.iter().toSeq().mapIt(it.tryGet()) - - check: - res.len == 10 - - test "Should not apply offset": - let - key = Key.init("/a").tryGet - keyId = KeyId.new $key - q = dbQuery(key= keyId, offset= 90) - - for i in 0..<100: - let - key = KeyId.new $Key.init(key, Key.init("/" & $i).tryGet).tryGet - val = DataBuffer.new("val " & $i) - - ds.put(key, val).tryGet - - var - qr = ds.query(q) - # echo "RES: ", qr.repr - - var - handle = ds.query(q).tryGet - let - res = handle.iter().toSeq().mapIt(it.tryGet()) - - # echo "RES: ", res.mapIt(it.key) - check: - res.len == 10 - - test "Should not apply offset and limit": - let - key = Key.init("/a").tryGet - keyId = KeyId.new $key - q = dbQuery(key= keyId, offset= 95, limit= 5) - - for i in 0..<100: - let - key = KeyId.new $Key.init(key, Key.init("/" & $i).tryGet).tryGet - val = DataBuffer.new("val " & $i) - - ds.put(key, val).tryGet - - var - handle = ds.query(q).tryGet - res = handle.iter().toSeq().mapIt(it.tryGet()) - - check: - res.len == 5 - - for i in 0.. int: - cmp($a.key.get, $b.key.get) - - kvs = kvs.reversed - var - handle = ds.query(q).tryGet - res = handle.iter().toSeq().mapIt(it.tryGet()) - - check: - res.len == 100 - - for i, r in res[1..^1]: - check: - res[i].key.get == kvs[i].key.get - res[i].data == kvs[i].data + let + dsNew = proc(): SQLiteBackend[KeyId, DataBuffer] = + newSQLiteBackend[KeyId, DataBuffer](Memory).tryGet() + key1 = KeyId.new "/a" + key2 = KeyId.new "/a/b" + key3 = KeyId.new "/a/b/c" + val1 = DataBuffer.new "value for 1" + val2 = DataBuffer.new "value for 2" + val3 = DataBuffer.new "value for 3" + + queryTests(ds, key1, key2, key3, val1, val2, val3, extended=true) From c215f9cb1a8105d41d54a9dde07739326954bc3c Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 18:20:46 -0700 Subject: [PATCH 33/74] refactor tests --- datastore/fsds.nim | 5 +- tests/datastore/testfsds.nim | 236 ++++++++++++++++++----------------- 2 files changed, 124 insertions(+), 117 deletions(-) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index 13181581..459b8605 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -68,6 +68,9 @@ proc has*(self: FSDatastore, key: KeyId): ?!bool = let key = key.toKey() return self.findPath(key).?fileExists() +proc contains*[K](self: FSDatastore, key: K): bool = + return self.has(key).get() + proc delete*(self: FSDatastore, key: KeyId): ?!void = let key = key.toKey() @@ -243,6 +246,6 @@ proc new*( return failure "directory does not exist: " & root success T( - root: root, + root: DataBuffer.new root, ignoreProtected: ignoreProtected, depth: depth) diff --git a/tests/datastore/testfsds.nim b/tests/datastore/testfsds.nim index 26336772..5400fc4e 100644 --- a/tests/datastore/testfsds.nim +++ b/tests/datastore/testfsds.nim @@ -3,136 +3,140 @@ import std/sequtils import std/os from std/algorithm import sort, reversed -import pkg/asynctest +import pkg/unittest2 import pkg/chronos import pkg/stew/results import pkg/stew/byteutils import pkg/datastore/fsds +import pkg/datastore/key +import pkg/datastore/backend -import ./dscommontests -import ./querycommontests +import ./backendCommonTests suite "Test Basic FSDatastore": let path = currentSourcePath() # get this file's name basePath = "tests_data" basePathAbs = path.parentDir / basePath - key = Key.init("/a/b").tryGet() - bytes = "some bytes".toBytes - otherBytes = "some other bytes".toBytes + keyFull = Key.init("/a/b").tryGet() + key = KeyId.new keyFull.id() + bytes = DataBuffer.new "some bytes" + otherBytes = DataBuffer.new "some other bytes".toBytes - var - fsStore: FSDatastore - - setupAll: - removeDir(basePathAbs) - require(not dirExists(basePathAbs)) - createDir(basePathAbs) - - fsStore = FSDatastore.new(root = basePathAbs, depth = 3).tryGet() - - teardownAll: - removeDir(basePathAbs) - require(not dirExists(basePathAbs)) + var batch: seq[tuple[key: KeyId, data: DataBuffer]] + for k in 0..<100: + let kk = Key.init($keyFull, $k).tryGet().id() + batch.add( (KeyId.new kk, DataBuffer.new @[k.byte]) ) - basicStoreTests(fsStore, key, bytes, otherBytes) - -suite "Test Misc FSDatastore": - let - path = currentSourcePath() # get this file's name - basePath = "tests_data" - basePathAbs = path.parentDir / basePath - bytes = "some bytes".toBytes - - setup: - removeDir(basePathAbs) - require(not dirExists(basePathAbs)) - createDir(basePathAbs) - - teardown: - removeDir(basePathAbs) - require(not dirExists(basePathAbs)) - - test "Test validDepth()": - let - fs = FSDatastore.new(root = "/", depth = 3).tryGet() - invalid = Key.init("/a/b/c/d").tryGet() - valid = Key.init("/a/b/c").tryGet() - - check: - not fs.validDepth(invalid) - fs.validDepth(valid) - - test "Test invalid key (path) depth": - let - fs = FSDatastore.new(root = basePathAbs, depth = 3).tryGet() - key = Key.init("/a/b/c/d").tryGet() - - check: - (await fs.put(key, bytes)).isErr - (await fs.get(key)).isErr - (await fs.delete(key)).isErr - (await fs.has(key)).isErr - - test "Test valid key (path) depth": - let - fs = FSDatastore.new(root = basePathAbs, depth = 3).tryGet() - key = Key.init("/a/b/c").tryGet() - - check: - (await fs.put(key, bytes)).isOk - (await fs.get(key)).isOk - (await fs.delete(key)).isOk - (await fs.has(key)).isOk - - test "Test key cannot write outside of root": - let - fs = FSDatastore.new(root = basePathAbs, depth = 3).tryGet() - key = Key.init("/a/../../c").tryGet() - - check: - (await fs.put(key, bytes)).isErr - (await fs.get(key)).isErr - (await fs.delete(key)).isErr - (await fs.has(key)).isErr - - test "Test key cannot convert to invalid path": - let - fs = FSDatastore.new(root = basePathAbs).tryGet() - - for c in invalidFilenameChars: - if c == ':': continue - if c == '/': continue - - let - key = Key.init("/" & c).tryGet() - - check: - (await fs.put(key, bytes)).isErr - (await fs.get(key)).isErr - (await fs.delete(key)).isErr - (await fs.has(key)).isErr - -suite "Test Query": - let - path = currentSourcePath() # get this file's name - basePath = "tests_data" - basePathAbs = path.parentDir / basePath + removeDir(basePathAbs) + require(not dirExists(basePathAbs)) + createDir(basePathAbs) var - ds: FSDatastore - - setup: - removeDir(basePathAbs) - require(not dirExists(basePathAbs)) - createDir(basePathAbs) - - ds = FSDatastore.new(root = basePathAbs, depth = 5).tryGet() - - teardown: - - removeDir(basePathAbs) - require(not dirExists(basePathAbs)) + fsStore = FSDatastore.new(root = basePathAbs, depth = 3).tryGet() - queryTests(ds, false) + testBasicBackend(fsStore, key, bytes, otherBytes, batch) + + removeDir(basePathAbs) + require(not dirExists(basePathAbs)) + +# suite "Test Misc FSDatastore": +# let +# path = currentSourcePath() # get this file's name +# basePath = "tests_data" +# basePathAbs = path.parentDir / basePath +# bytes = "some bytes".toBytes + +# setup: +# removeDir(basePathAbs) +# require(not dirExists(basePathAbs)) +# createDir(basePathAbs) + +# teardown: +# removeDir(basePathAbs) +# require(not dirExists(basePathAbs)) + +# test "Test validDepth()": +# let +# fs = FSDatastore.new(root = "/", depth = 3).tryGet() +# invalid = Key.init("/a/b/c/d").tryGet() +# valid = Key.init("/a/b/c").tryGet() + +# check: +# not fs.validDepth(invalid) +# fs.validDepth(valid) + +# test "Test invalid key (path) depth": +# let +# fs = FSDatastore.new(root = basePathAbs, depth = 3).tryGet() +# key = Key.init("/a/b/c/d").tryGet() + +# check: +# (await fs.put(key, bytes)).isErr +# (await fs.get(key)).isErr +# (await fs.delete(key)).isErr +# (await fs.has(key)).isErr + +# test "Test valid key (path) depth": +# let +# fs = FSDatastore.new(root = basePathAbs, depth = 3).tryGet() +# key = Key.init("/a/b/c").tryGet() + +# check: +# (await fs.put(key, bytes)).isOk +# (await fs.get(key)).isOk +# (await fs.delete(key)).isOk +# (await fs.has(key)).isOk + +# test "Test key cannot write outside of root": +# let +# fs = FSDatastore.new(root = basePathAbs, depth = 3).tryGet() +# key = Key.init("/a/../../c").tryGet() + +# check: +# (await fs.put(key, bytes)).isErr +# (await fs.get(key)).isErr +# (await fs.delete(key)).isErr +# (await fs.has(key)).isErr + +# test "Test key cannot convert to invalid path": +# let +# fs = FSDatastore.new(root = basePathAbs).tryGet() + +# for c in invalidFilenameChars: +# if c == ':': continue +# if c == '/': continue + +# let +# key = Key.init("/" & c).tryGet() + +# check: +# (await fs.put(key, bytes)).isErr +# (await fs.get(key)).isErr +# (await fs.delete(key)).isErr +# (await fs.has(key)).isErr + + +# suite "Test Query": +# let +# path = currentSourcePath() # get this file's name +# basePath = "tests_data" +# basePathAbs = path.parentDir / basePath + +# var +# ds: FSDatastore + +# setup: +# removeDir(basePathAbs) +# require(not dirExists(basePathAbs)) +# createDir(basePathAbs) + +# ds = FSDatastore.new(root = basePathAbs, depth = 5).tryGet() + +# teardown: + +# removeDir(basePathAbs) +# require(not dirExists(basePathAbs)) + +# queryTests(ds, false) From ef5a30f7d311f82075164d18e6c60fac6873804d Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 18:27:29 -0700 Subject: [PATCH 34/74] refactor tests --- datastore/fsds.nim | 3 ++- datastore/threads/databuffer.nim | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index 459b8605..a49f800d 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -111,12 +111,13 @@ proc readFile[V](self: FSDatastore, path: string): ?!V = when V is seq[byte]: var bytes = newSeq[byte](size) elif V is DataBuffer: - var bytes = DataBuffer.new(capacity=size) + var bytes = DataBuffer.new(size=size) else: {.error: "unhandled result type".} var read = 0 + echo "BYTES: ", bytes.repr while read < size: read += file.readBytes(bytes.toOpenArray(), read, size) diff --git a/datastore/threads/databuffer.nim b/datastore/threads/databuffer.nim index 1b5e6d25..3cb414a7 100644 --- a/datastore/threads/databuffer.nim +++ b/datastore/threads/databuffer.nim @@ -47,14 +47,14 @@ template `==`*[T: char | byte](a: DataBuffer, b: openArray[T]): bool = elif a[].size != b.len: false else: a.hash() == b.hash() -proc new*(tp: type DataBuffer, capacity: int = 0): DataBuffer = +proc new*(tp: type DataBuffer, size: int = 0): DataBuffer = ## allocate new buffer with given capacity ## newSharedPtr(DataBufferHolder( - buf: cast[typeof(result[].buf)](allocShared0(capacity)), - size: 0, - cap: capacity, + buf: cast[typeof(result[].buf)](allocShared0(size)), + size: size, + cap: size, )) proc new*[T: byte | char](tp: type DataBuffer, data: openArray[T], opts: set[DataBufferOpt] = {}): DataBuffer = From ed34fe2d3cfaef63773202e5d7550fb8de35920b Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 18:29:04 -0700 Subject: [PATCH 35/74] refactor tests --- tests/datastore/testfsds.nim | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/datastore/testfsds.nim b/tests/datastore/testfsds.nim index 5400fc4e..bd67777a 100644 --- a/tests/datastore/testfsds.nim +++ b/tests/datastore/testfsds.nim @@ -41,6 +41,32 @@ suite "Test Basic FSDatastore": removeDir(basePathAbs) require(not dirExists(basePathAbs)) +suite "Test Basic FSDatastore": + let + path = currentSourcePath() # get this file's name + basePath = "tests_data" + basePathAbs = path.parentDir / basePath + key = Key.init("/a/b").tryGet() + bytes = "some bytes".toBytes + otherBytes = "some other bytes".toBytes + + var batch: seq[tuple[key: string, data: seq[byte]]] + for k in 0..<100: + let kk = Key.init($key, $k).tryGet().id() + batch.add( (kk, @[k.byte]) ) + + removeDir(basePathAbs) + require(not dirExists(basePathAbs)) + createDir(basePathAbs) + + var + fsStore = FSDatastore.new(root = basePathAbs, depth = 3).tryGet() + + testBasicBackend(fsStore, key, bytes, otherBytes, batch) + + removeDir(basePathAbs) + require(not dirExists(basePathAbs)) + # suite "Test Misc FSDatastore": # let # path = currentSourcePath() # get this file's name From 2a96b1bcef29ffd666ab91aa1cba0b05b841f346 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 18:36:32 -0700 Subject: [PATCH 36/74] refactor tests --- datastore/fsds.nim | 35 +++++++++++++++++------------------ datastore/sql/sqliteds.nim | 4 ++-- tests/datastore/testfsds.nim | 4 ++-- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index a49f800d..9c725468 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -15,7 +15,7 @@ export datastore push: {.upraises: [].} type - FSDatastore* = object + FSDatastore*[K, V] = object root*: DataBuffer ignoreProtected: bool depth: int @@ -26,11 +26,12 @@ proc isRootSubdir*(root, path: string): bool = proc validDepth(self: FSDatastore, key: Key): bool = key.len <= self.depth -proc findPath*(self: FSDatastore, key: Key): ?!string = +proc findPath*[K,V](self: FSDatastore[K,V], key: K): ?!string = ## Return filename corresponding to the key ## or failure if the key doesn't correspond to a valid filename ## let root = $self.root + let key = Key.init($key).get() if not self.validDepth(key): return failure "Path has invalid depth!" @@ -64,14 +65,14 @@ proc findPath*(self: FSDatastore, key: Key): ?!string = return success fullname -proc has*(self: FSDatastore, key: KeyId): ?!bool = +proc has*[K,V](self: FSDatastore[K,V], key: KeyId): ?!bool = let key = key.toKey() return self.findPath(key).?fileExists() proc contains*[K](self: FSDatastore, key: K): bool = return self.has(key).get() -proc delete*(self: FSDatastore, key: KeyId): ?!void = +proc delete*[K,V](self: FSDatastore[K,V], key: KeyId): ?!void = let key = key.toKey() without path =? self.findPath(key), error: @@ -87,7 +88,7 @@ proc delete*(self: FSDatastore, key: KeyId): ?!void = return success() -proc delete*(self: FSDatastore, keys: openArray[KeyId]): ?!void = +proc delete*[K,V](self: FSDatastore[K,V], keys: openArray[KeyId]): ?!void = for key in keys: if err =? self.delete(key).errorOption: return failure err @@ -130,7 +131,7 @@ proc readFile[V](self: FSDatastore, path: string): ?!V = except CatchableError as e: return failure e -proc get*(self: FSDatastore, key: KeyId): ?!DataBuffer = +proc get*[K,V](self: FSDatastore[K,V], key: KeyId): ?!DataBuffer = let key = key.toKey() without path =? self.findPath(key), error: return failure error @@ -141,11 +142,10 @@ proc get*(self: FSDatastore, key: KeyId): ?!DataBuffer = return readFile[DataBuffer](self, path) -proc put*( - self: FSDatastore, +proc put*[K,V]( + self: FSDatastore[K,V], key: KeyId, data: DataBuffer): ?!void = - let key = key.toKey() without path =? self.findPath(key), error: return failure error @@ -176,11 +176,11 @@ iterator dirIter(path: string): string {.gcsafe.} = except CatchableError as exc: raise newException(Defect, exc.msg) -proc close*(self: FSDatastore): ?!void = +proc close*[K,V](self: FSDatastore[K,V]): ?!void = return success() type - FsQueryEnv* = tuple[self: FSDatastore, basePath: DataBuffer] + FsQueryEnv*[K,V] = tuple[self: FSDatastore[K,V], basePath: DataBuffer] proc query*( self: FSDatastore, @@ -231,12 +231,11 @@ iterator iter*[K, V](handle: var DbQueryHandle[K, V, DataBuffer]): ?!DbQueryResp yield success (key.some, data) -proc new*( - T: type FSDatastore, - root: string, - depth = 2, - caseSensitive = true, - ignoreProtected = false): ?!T = +proc newFSDatastore*[K,V](root: string, + depth = 2, + caseSensitive = true, + ignoreProtected = false + ): ?!FSDatastore[K,V] = let root = ? ( block: @@ -246,7 +245,7 @@ proc new*( if not dirExists(root): return failure "directory does not exist: " & root - success T( + success FSDatastore[K,V]( root: DataBuffer.new root, ignoreProtected: ignoreProtected, depth: depth) diff --git a/datastore/sql/sqliteds.nim b/datastore/sql/sqliteds.nim index feb0785b..e86fb905 100644 --- a/datastore/sql/sqliteds.nim +++ b/datastore/sql/sqliteds.nim @@ -227,7 +227,7 @@ proc newSQLiteBackend*[K,V]( success SQLiteBackend[K,V](db: ? SQLiteDsDb[K,V].open(path, flags)) -proc newSQLiteBackend*[K,V]( - db: SQLiteDsDb[K,V]): ?!SQLiteBackend[K,V] = +proc newSQLiteBackend*[K,V](db: SQLiteDsDb[K,V] + ): ?!SQLiteBackend[K,V] = success SQLiteBackend[K,V](db: db) diff --git a/tests/datastore/testfsds.nim b/tests/datastore/testfsds.nim index bd67777a..6a829b8b 100644 --- a/tests/datastore/testfsds.nim +++ b/tests/datastore/testfsds.nim @@ -34,7 +34,7 @@ suite "Test Basic FSDatastore": createDir(basePathAbs) var - fsStore = FSDatastore.new(root = basePathAbs, depth = 3).tryGet() + fsStore = newFSDatastore[KeyId, DataBuffer](root = basePathAbs, depth = 3).tryGet() testBasicBackend(fsStore, key, bytes, otherBytes, batch) @@ -60,7 +60,7 @@ suite "Test Basic FSDatastore": createDir(basePathAbs) var - fsStore = FSDatastore.new(root = basePathAbs, depth = 3).tryGet() + fsStore = newFSDatastore[KeyId, DataBuffer](root = basePathAbs, depth = 3).tryGet() testBasicBackend(fsStore, key, bytes, otherBytes, batch) From 4fa532e72a19a0cdc38f59269b899358fc66775e Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 18:39:20 -0700 Subject: [PATCH 37/74] refactor tests --- datastore/fsds.nim | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index 9c725468..2b649759 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -65,16 +65,15 @@ proc findPath*[K,V](self: FSDatastore[K,V], key: K): ?!string = return success fullname -proc has*[K,V](self: FSDatastore[K,V], key: KeyId): ?!bool = - let key = key.toKey() - return self.findPath(key).?fileExists() +proc has*[K,V](self: FSDatastore[K,V], key: K): ?!bool = + without path =? self.findPath(key), error: + return failure error + success path.fileExists() proc contains*[K](self: FSDatastore, key: K): bool = return self.has(key).get() -proc delete*[K,V](self: FSDatastore[K,V], key: KeyId): ?!void = - let key = key.toKey() - +proc delete*[K,V](self: FSDatastore[K,V], key: K): ?!void = without path =? self.findPath(key), error: return failure error @@ -88,7 +87,7 @@ proc delete*[K,V](self: FSDatastore[K,V], key: KeyId): ?!void = return success() -proc delete*[K,V](self: FSDatastore[K,V], keys: openArray[KeyId]): ?!void = +proc delete*[K,V](self: FSDatastore[K,V], keys: openArray[K]): ?!void = for key in keys: if err =? self.delete(key).errorOption: return failure err @@ -131,8 +130,7 @@ proc readFile[V](self: FSDatastore, path: string): ?!V = except CatchableError as e: return failure e -proc get*[K,V](self: FSDatastore[K,V], key: KeyId): ?!DataBuffer = - let key = key.toKey() +proc get*[K,V](self: FSDatastore[K,V], key: K): ?!DataBuffer = without path =? self.findPath(key), error: return failure error @@ -159,9 +157,9 @@ proc put*[K,V]( return success() -proc put*( +proc put*[K,V]( self: FSDatastore, - batch: seq[DbBatchEntry[KeyId, DataBuffer]]): ?!void = + batch: seq[DbBatchEntry[K, V]]): ?!void = for entry in batch: if err =? self.put(entry.key, entry.data).errorOption: From 1b7866aad2e95acb83472b3e36f06d2f16ed10a8 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 18:48:48 -0700 Subject: [PATCH 38/74] refactor --- datastore/backend.nim | 3 --- datastore/fsds.nim | 40 ++++++++++++++++---------------- datastore/threads/databuffer.nim | 4 ++-- tests/datastore/testfsds.nim | 2 +- 4 files changed, 23 insertions(+), 26 deletions(-) diff --git a/datastore/backend.nim b/datastore/backend.nim index 5790b121..bb722b9b 100644 --- a/datastore/backend.nim +++ b/datastore/backend.nim @@ -62,6 +62,3 @@ proc new*(tp: typedesc[KeyId], id: cstring): KeyId = proc new*(tp: typedesc[KeyId], id: string): KeyId = KeyId(data: DataBuffer.new(id)) - -template toOpenArray*(x: DbKey): openArray[char] = - x.data.toOpenArray(char) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index 2b649759..8f2c7705 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -110,8 +110,8 @@ proc readFile[V](self: FSDatastore, path: string): ?!V = when V is seq[byte]: var bytes = newSeq[byte](size) - elif V is DataBuffer: - var bytes = DataBuffer.new(size=size) + elif V is V: + var bytes = V.new(size=size) else: {.error: "unhandled result type".} var @@ -119,7 +119,7 @@ proc readFile[V](self: FSDatastore, path: string): ?!V = echo "BYTES: ", bytes.repr while read < size: - read += file.readBytes(bytes.toOpenArray(), read, size) + read += file.readBytes(bytes.toOpenArray(0, size-1), read, size) if read < size: return failure $read & " bytes were read from " & path & @@ -130,7 +130,7 @@ proc readFile[V](self: FSDatastore, path: string): ?!V = except CatchableError as e: return failure e -proc get*[K,V](self: FSDatastore[K,V], key: K): ?!DataBuffer = +proc get*[K,V](self: FSDatastore[K,V], key: K): ?!V = without path =? self.findPath(key), error: return failure error @@ -138,12 +138,12 @@ proc get*[K,V](self: FSDatastore[K,V], key: K): ?!DataBuffer = return failure( newException(DatastoreKeyNotFound, "Key doesn't exist")) - return readFile[DataBuffer](self, path) + return readFile[V](self, path) -proc put*[K,V]( - self: FSDatastore[K,V], - key: KeyId, - data: DataBuffer): ?!void = +proc put*[K,V](self: FSDatastore[K,V], + key: K, + data: V + ): ?!void = without path =? self.findPath(key), error: return failure error @@ -151,7 +151,7 @@ proc put*[K,V]( try: var data = data createDir(parentDir(path)) - writeFile(path, data.toOpenArray()) + writeFile(path, data.toOpenArray(0, data.len()-1)) except CatchableError as e: return failure e @@ -178,12 +178,12 @@ proc close*[K,V](self: FSDatastore[K,V]): ?!void = return success() type - FsQueryEnv*[K,V] = tuple[self: FSDatastore[K,V], basePath: DataBuffer] + FsQueryEnv*[K,V] = tuple[self: FSDatastore[K,V], basePath: V] -proc query*( - self: FSDatastore, - query: DbQuery[KeyId], -): Result[DbQueryHandle[KeyId, DataBuffer, FsQueryEnv], ref CatchableError] = +proc query*[K,V]( + self: FSDatastore[K,V], + query: DbQuery[K], +): Result[DbQueryHandle[KeyId, V, FsQueryEnv], ref CatchableError] = let key = query.key.toKey() without path =? self.findPath(key), error: @@ -199,10 +199,10 @@ proc query*( else: path.changeFileExt("") - let env: FsQueryEnv = (self: self, basePath: DataBuffer.new(basePath)) - success DbQueryHandle[KeyId, DataBuffer, FsQueryEnv](env: env) + let env: FsQueryEnv = (self: self, basePath: V.new(basePath)) + success DbQueryHandle[KeyId, V, FsQueryEnv](env: env) -iterator iter*[K, V](handle: var DbQueryHandle[K, V, DataBuffer]): ?!DbQueryResponse[K, V] = +iterator iter*[K, V](handle: var DbQueryHandle[K, V, V]): ?!DbQueryResponse[K, V] = let root = $(handle.env) for path in root.dirIter(): @@ -220,12 +220,12 @@ iterator iter*[K, V](handle: var DbQueryHandle[K, V, DataBuffer]): ?!DbQueryResp key = Key.init(keyPath).expect("should not fail") data = if query.value: - let res = readFile[DataBuffer](handle.env.self, fl) + let res = readFile[V](handle.env.self, fl) if res.isErr(): yield failure res.error() res.get() else: - DataBuffer.new() + V.new() yield success (key.some, data) diff --git a/datastore/threads/databuffer.nim b/datastore/threads/databuffer.nim index 3cb414a7..5f80cbfe 100644 --- a/datastore/threads/databuffer.nim +++ b/datastore/threads/databuffer.nim @@ -137,5 +137,5 @@ template toOpenArray*[T: byte | char](data: var DataBuffer, t: typedesc[T]): var var bf = cast[ptr UncheckedArray[T]](data[].buf) bf.toOpenArray(0, data[].size-1) -template toOpenArray*(data: var DataBuffer): var openArray[byte] = - toOpenArray(data, byte) +template toOpenArray*(data: var DataBuffer, first, last: int): var openArray[byte] = + toOpenArray(data, byte).toOpenArray(first, last) diff --git a/tests/datastore/testfsds.nim b/tests/datastore/testfsds.nim index 6a829b8b..d1ad8052 100644 --- a/tests/datastore/testfsds.nim +++ b/tests/datastore/testfsds.nim @@ -60,7 +60,7 @@ suite "Test Basic FSDatastore": createDir(basePathAbs) var - fsStore = newFSDatastore[KeyId, DataBuffer](root = basePathAbs, depth = 3).tryGet() + fsStore = newFSDatastore[Key, seq[byte]](root = basePathAbs, depth = 3).tryGet() testBasicBackend(fsStore, key, bytes, otherBytes, batch) From f587677be850666aae943e498a34052ad0dc8a18 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 18:50:42 -0700 Subject: [PATCH 39/74] refactor - tests --- tests/datastore/testfsds.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/datastore/testfsds.nim b/tests/datastore/testfsds.nim index d1ad8052..d2053422 100644 --- a/tests/datastore/testfsds.nim +++ b/tests/datastore/testfsds.nim @@ -50,9 +50,9 @@ suite "Test Basic FSDatastore": bytes = "some bytes".toBytes otherBytes = "some other bytes".toBytes - var batch: seq[tuple[key: string, data: seq[byte]]] + var batch: seq[tuple[key: Key, data: seq[byte]]] for k in 0..<100: - let kk = Key.init($key, $k).tryGet().id() + let kk = Key.init($key, $k).tryGet() batch.add( (kk, @[k.byte]) ) removeDir(basePathAbs) From 9e946e683e175ac097262eb23dae10ec6ec25a05 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 18:53:46 -0700 Subject: [PATCH 40/74] refactor - tests --- datastore/fsds.nim | 2 +- tests/datastore/testfsds.nim | 148 +++++++++++++++++------------------ 2 files changed, 75 insertions(+), 75 deletions(-) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index 8f2c7705..fb027c9c 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -23,7 +23,7 @@ type proc isRootSubdir*(root, path: string): bool = path.startsWith(root) -proc validDepth(self: FSDatastore, key: Key): bool = +proc validDepth*(self: FSDatastore, key: Key): bool = key.len <= self.depth proc findPath*[K,V](self: FSDatastore[K,V], key: K): ?!string = diff --git a/tests/datastore/testfsds.nim b/tests/datastore/testfsds.nim index d2053422..33c1a051 100644 --- a/tests/datastore/testfsds.nim +++ b/tests/datastore/testfsds.nim @@ -67,81 +67,81 @@ suite "Test Basic FSDatastore": removeDir(basePathAbs) require(not dirExists(basePathAbs)) -# suite "Test Misc FSDatastore": -# let -# path = currentSourcePath() # get this file's name -# basePath = "tests_data" -# basePathAbs = path.parentDir / basePath -# bytes = "some bytes".toBytes - -# setup: -# removeDir(basePathAbs) -# require(not dirExists(basePathAbs)) -# createDir(basePathAbs) - -# teardown: -# removeDir(basePathAbs) -# require(not dirExists(basePathAbs)) +suite "Test Misc FSDatastore": + let + path = currentSourcePath() # get this file's name + basePath = "tests_data" + basePathAbs = path.parentDir / basePath + bytes = "some bytes".toBytes -# test "Test validDepth()": -# let -# fs = FSDatastore.new(root = "/", depth = 3).tryGet() -# invalid = Key.init("/a/b/c/d").tryGet() -# valid = Key.init("/a/b/c").tryGet() - -# check: -# not fs.validDepth(invalid) -# fs.validDepth(valid) - -# test "Test invalid key (path) depth": -# let -# fs = FSDatastore.new(root = basePathAbs, depth = 3).tryGet() -# key = Key.init("/a/b/c/d").tryGet() - -# check: -# (await fs.put(key, bytes)).isErr -# (await fs.get(key)).isErr -# (await fs.delete(key)).isErr -# (await fs.has(key)).isErr - -# test "Test valid key (path) depth": -# let -# fs = FSDatastore.new(root = basePathAbs, depth = 3).tryGet() -# key = Key.init("/a/b/c").tryGet() - -# check: -# (await fs.put(key, bytes)).isOk -# (await fs.get(key)).isOk -# (await fs.delete(key)).isOk -# (await fs.has(key)).isOk - -# test "Test key cannot write outside of root": -# let -# fs = FSDatastore.new(root = basePathAbs, depth = 3).tryGet() -# key = Key.init("/a/../../c").tryGet() - -# check: -# (await fs.put(key, bytes)).isErr -# (await fs.get(key)).isErr -# (await fs.delete(key)).isErr -# (await fs.has(key)).isErr - -# test "Test key cannot convert to invalid path": -# let -# fs = FSDatastore.new(root = basePathAbs).tryGet() - -# for c in invalidFilenameChars: -# if c == ':': continue -# if c == '/': continue - -# let -# key = Key.init("/" & c).tryGet() - -# check: -# (await fs.put(key, bytes)).isErr -# (await fs.get(key)).isErr -# (await fs.delete(key)).isErr -# (await fs.has(key)).isErr + setup: + removeDir(basePathAbs) + require(not dirExists(basePathAbs)) + createDir(basePathAbs) + + teardown: + removeDir(basePathAbs) + require(not dirExists(basePathAbs)) + + test "Test validDepth()": + let + fs = newFSDatastore[Key, seq[byte]](root = basePathAbs, depth = 3).tryGet() + invalid = Key.init("/a/b/c/d").tryGet() + valid = Key.init("/a/b/c").tryGet() + + check: + not fs.validDepth(invalid) + fs.validDepth(valid) + + test "Test invalid key (path) depth": + let + fs = newFSDatastore[Key, seq[byte]](root = basePathAbs, depth = 3).tryGet() + key = Key.init("/a/b/c/d").tryGet() + + check: + (fs.put(key, bytes)).isErr + (fs.get(key)).isErr + (fs.delete(key)).isErr + (fs.has(key)).isErr + + test "Test valid key (path) depth": + let + fs = newFSDatastore[Key, seq[byte]](root = basePathAbs, depth = 3).tryGet() + key = Key.init("/a/b/c").tryGet() + + check: + (fs.put(key, bytes)).isOk + (fs.get(key)).isOk + (fs.delete(key)).isOk + (fs.has(key)).isOk + + test "Test key cannot write outside of root": + let + fs = newFSDatastore[Key, seq[byte]](root = basePathAbs, depth = 3).tryGet() + key = Key.init("/a/../../c").tryGet() + + check: + (fs.put(key, bytes)).isErr + (fs.get(key)).isErr + (fs.delete(key)).isErr + (fs.has(key)).isErr + + test "Test key cannot convert to invalid path": + let + fs = newFSDatastore[Key, seq[byte]](root = basePathAbs).tryGet() + + for c in invalidFilenameChars: + if c == ':': continue + if c == '/': continue + + let + key = Key.init("/" & c).tryGet() + + check: + (fs.put(key, bytes)).isErr + (fs.get(key)).isErr + (fs.delete(key)).isErr + (fs.has(key)).isErr # suite "Test Query": From 1c2c5f10200d9d93d92f6f690ed28e03a8830b3f Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 18:57:17 -0700 Subject: [PATCH 41/74] refactor - tests --- tests/datastore/backendCommonTests.nim | 2 +- tests/datastore/sql/testsqliteds.nim | 2 +- tests/datastore/testfsds.nim | 26 ++++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/tests/datastore/backendCommonTests.nim b/tests/datastore/backendCommonTests.nim index e09e6121..b3fecbe6 100644 --- a/tests/datastore/backendCommonTests.nim +++ b/tests/datastore/backendCommonTests.nim @@ -55,7 +55,7 @@ template testBasicBackend*[K, V, DB]( discard ds.get(key).tryGet() # non existing key template queryTests*( - ds: untyped, + dsNew: untyped, key1, key2, key3: untyped, val1, val2, val3: untyped, extended = true diff --git a/tests/datastore/sql/testsqliteds.nim b/tests/datastore/sql/testsqliteds.nim index 18454ba6..e52d90b1 100644 --- a/tests/datastore/sql/testsqliteds.nim +++ b/tests/datastore/sql/testsqliteds.nim @@ -62,4 +62,4 @@ suite "queryTests": val2 = DataBuffer.new "value for 2" val3 = DataBuffer.new "value for 3" - queryTests(ds, key1, key2, key3, val1, val2, val3, extended=true) + queryTests(dsNew, key1, key2, key3, val1, val2, val3, extended=true) diff --git a/tests/datastore/testfsds.nim b/tests/datastore/testfsds.nim index 33c1a051..4eb4db30 100644 --- a/tests/datastore/testfsds.nim +++ b/tests/datastore/testfsds.nim @@ -166,3 +166,29 @@ suite "Test Misc FSDatastore": # require(not dirExists(basePathAbs)) # queryTests(ds, false) + +suite "queryTests": + + let + path = currentSourcePath() # get this file's name + basePath = "tests_data" + basePathAbs = path.parentDir / basePath + + removeDir(basePathAbs) + require(not dirExists(basePathAbs)) + createDir(basePathAbs) + + let + fsNew = proc(): FSDatastore[KeyId, DataBuffer] = + newFSDatastore[KeyId, DataBuffer](root = basePathAbs, depth = 3).tryGet() + key1 = KeyId.new "/a" + key2 = KeyId.new "/a/b" + key3 = KeyId.new "/a/b/c" + val1 = DataBuffer.new "value for 1" + val2 = DataBuffer.new "value for 2" + val3 = DataBuffer.new "value for 3" + + queryTests(fsNew, key1, key2, key3, val1, val2, val3, extended=true) + + removeDir(basePathAbs) + require(not dirExists(basePathAbs)) From 534555015b823a4ae002c593b5f11e11c68961d8 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 19:00:54 -0700 Subject: [PATCH 42/74] refactor - tests --- datastore/fsds.nim | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index fb027c9c..5fe34326 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -178,14 +178,16 @@ proc close*[K,V](self: FSDatastore[K,V]): ?!void = return success() type - FsQueryEnv*[K,V] = tuple[self: FSDatastore[K,V], basePath: V] + FsQueryEnv*[K,V] = object + self: FSDatastore[K,V] + basePath: DataBuffer proc query*[K,V]( self: FSDatastore[K,V], query: DbQuery[K], -): Result[DbQueryHandle[KeyId, V, FsQueryEnv], ref CatchableError] = +): Result[DbQueryHandle[K, V, FsQueryEnv[K,V]], ref CatchableError] = - let key = query.key.toKey() + let key = query.key without path =? self.findPath(key), error: return failure error @@ -199,8 +201,8 @@ proc query*[K,V]( else: path.changeFileExt("") - let env: FsQueryEnv = (self: self, basePath: V.new(basePath)) - success DbQueryHandle[KeyId, V, FsQueryEnv](env: env) + let env = FsQueryEnv[K,V](self: self, basePath: DataBuffer.new(basePath)) + success DbQueryHandle[KeyId, V, FsQueryEnv[K,V]](env: env) iterator iter*[K, V](handle: var DbQueryHandle[K, V, V]): ?!DbQueryResponse[K, V] = let root = $(handle.env) From e778fdbb8e19621ccf7d5c8686a1b587e3aff1b4 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 19:08:58 -0700 Subject: [PATCH 43/74] refactor - tests --- datastore/fsds.nim | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index 5fe34326..4ffd523a 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -202,29 +202,35 @@ proc query*[K,V]( path.changeFileExt("") let env = FsQueryEnv[K,V](self: self, basePath: DataBuffer.new(basePath)) - success DbQueryHandle[KeyId, V, FsQueryEnv[K,V]](env: env) + success DbQueryHandle[KeyId, V, FsQueryEnv[K,V]](query: query, env: env) -iterator iter*[K, V](handle: var DbQueryHandle[K, V, V]): ?!DbQueryResponse[K, V] = +iterator iter*[K, V](handle: var DbQueryHandle[K, V, FsQueryEnv[K,V]]): ?!DbQueryResponse[K, V] = let root = $(handle.env) for path in root.dirIter(): if handle.cancel: - return + break - var keyPath = handle.basePath + var + basePath = $handle.env.basePath + keyPath = basePath keyPath.removePrefix(root) keyPath = keyPath / path.changeFileExt("") keyPath = keyPath.replace("\\", "/") let - fl = (handle.env.basePath / path).absolutePath() - key = Key.init(keyPath).expect("should not fail") + flres = (basePath / path).absolutePath().catch + if flres.isErr(): + yield DbQueryResponse[K,V].failure flres.error() + + let + key = K.toKey(keyPath) data = - if query.value: - let res = readFile[V](handle.env.self, fl) + if handle.query.value: + let res = readFile[V](handle.env.self, flres.get) if res.isErr(): - yield failure res.error() + yield DbQueryResponse[K,V].failure res.error() res.get() else: V.new() From d5850ebe9002995b16c2d393637d56071ee0958b Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 19:12:42 -0700 Subject: [PATCH 44/74] refactor - tests --- datastore/fsds.nim | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index 4ffd523a..4cbaac40 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -117,7 +117,7 @@ proc readFile[V](self: FSDatastore, path: string): ?!V = var read = 0 - echo "BYTES: ", bytes.repr + # echo "BYTES: ", bytes.repr while read < size: read += file.readBytes(bytes.toOpenArray(0, size-1), read, size) @@ -208,7 +208,9 @@ iterator iter*[K, V](handle: var DbQueryHandle[K, V, FsQueryEnv[K,V]]): ?!DbQuer let root = $(handle.env) for path in root.dirIter(): + echo "FS:path: ", path if handle.cancel: + echo "FS:CANCELLED!" break var @@ -222,6 +224,7 @@ iterator iter*[K, V](handle: var DbQueryHandle[K, V, FsQueryEnv[K,V]]): ?!DbQuer let flres = (basePath / path).absolutePath().catch if flres.isErr(): + echo "FS:ERROR: ", flres.error() yield DbQueryResponse[K,V].failure flres.error() let @@ -230,11 +233,13 @@ iterator iter*[K, V](handle: var DbQueryHandle[K, V, FsQueryEnv[K,V]]): ?!DbQuer if handle.query.value: let res = readFile[V](handle.env.self, flres.get) if res.isErr(): + echo "FS:ERROR: ", res.error() yield DbQueryResponse[K,V].failure res.error() res.get() else: V.new() + echo "FS:SUCCESS: ", key yield success (key.some, data) proc newFSDatastore*[K,V](root: string, From ade0898fe78fdef1ea6f7c3cc117b4b2c1775458 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 19:13:41 -0700 Subject: [PATCH 45/74] refactor - tests --- datastore/fsds.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index 4cbaac40..14eeafe8 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -206,6 +206,7 @@ proc query*[K,V]( iterator iter*[K, V](handle: var DbQueryHandle[K, V, FsQueryEnv[K,V]]): ?!DbQueryResponse[K, V] = let root = $(handle.env) + echo "FS:root: ", root for path in root.dirIter(): echo "FS:path: ", path From cac5d52b350351cd1ca5a0ed1744eba355f41886 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 19:18:37 -0700 Subject: [PATCH 46/74] refactor - tests --- datastore/backend.nim | 5 +++-- datastore/fsds.nim | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/datastore/backend.nim b/datastore/backend.nim index bb722b9b..410f5b11 100644 --- a/datastore/backend.nim +++ b/datastore/backend.nim @@ -51,8 +51,9 @@ proc dbQuery*[K]( proc `$`*(id: KeyId): string = $(id.data) -proc toKey*(tp: typedesc[KeyId], id: cstring): KeyId = KeyId.new(id) -proc toKey*(tp: typedesc[string], id: cstring): string = $(id) +proc toKey*(tp: typedesc[KeyId], id: string|cstring): KeyId = KeyId.new($id) +proc toKey*(tp: typedesc[string], id: string|cstring): string = $(id) +# proc toKey*(tp: typedesc[Key], id: string|cstring): KeyId = Key.init($id).expect("valid key") template toVal*(tp: typedesc[DataBuffer], id: openArray[byte]): DataBuffer = DataBuffer.new(id) template toVal*(tp: typedesc[seq[byte]], id: openArray[byte]): seq[byte] = @(id) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index 14eeafe8..e9c9946f 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -204,8 +204,9 @@ proc query*[K,V]( let env = FsQueryEnv[K,V](self: self, basePath: DataBuffer.new(basePath)) success DbQueryHandle[KeyId, V, FsQueryEnv[K,V]](query: query, env: env) -iterator iter*[K, V](handle: var DbQueryHandle[K, V, FsQueryEnv[K,V]]): ?!DbQueryResponse[K, V] = - let root = $(handle.env) +iterator iter*[K, V](handle: var DbQueryHandle[K, V, FsQueryEnv[K,V]] + ): ?!DbQueryResponse[K, V] = + let root = $(handle.env.self.root) echo "FS:root: ", root for path in root.dirIter(): From d4a7549e12daf648eaecd96a1c8186000a47c87d Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 19:19:20 -0700 Subject: [PATCH 47/74] refactor - tests --- datastore/fsds.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index e9c9946f..81f2bec9 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -230,7 +230,7 @@ iterator iter*[K, V](handle: var DbQueryHandle[K, V, FsQueryEnv[K,V]] yield DbQueryResponse[K,V].failure flres.error() let - key = K.toKey(keyPath) + key = K.toKey($Key.init(keyPath).expect("valid key")) data = if handle.query.value: let res = readFile[V](handle.env.self, flres.get) From 17337ea7cd8cfd5fc8b88ac17446809e4a1d9b98 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 19:21:39 -0700 Subject: [PATCH 48/74] refactor - tests --- datastore/fsds.nim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index 81f2bec9..5da60d88 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -204,6 +204,10 @@ proc query*[K,V]( let env = FsQueryEnv[K,V](self: self, basePath: DataBuffer.new(basePath)) success DbQueryHandle[KeyId, V, FsQueryEnv[K,V]](query: query, env: env) +proc close*[K,V](handle: var DbQueryHandle[K,V,FsQueryEnv]) = + if not handle.closed: + handle.closed = true + iterator iter*[K, V](handle: var DbQueryHandle[K, V, FsQueryEnv[K,V]] ): ?!DbQueryResponse[K, V] = let root = $(handle.env.self.root) From f0591a0f9f5393651938a9321ad1b1836ed5de08 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 19:22:55 -0700 Subject: [PATCH 49/74] refactor - tests --- datastore/fsds.nim | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index 5da60d88..f81ec26e 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -204,19 +204,19 @@ proc query*[K,V]( let env = FsQueryEnv[K,V](self: self, basePath: DataBuffer.new(basePath)) success DbQueryHandle[KeyId, V, FsQueryEnv[K,V]](query: query, env: env) -proc close*[K,V](handle: var DbQueryHandle[K,V,FsQueryEnv]) = +proc close*[K,V](handle: var DbQueryHandle[K,V,FsQueryEnv[K,V]]) = if not handle.closed: handle.closed = true iterator iter*[K, V](handle: var DbQueryHandle[K, V, FsQueryEnv[K,V]] ): ?!DbQueryResponse[K, V] = let root = $(handle.env.self.root) - echo "FS:root: ", root + # echo "FS:root: ", root for path in root.dirIter(): - echo "FS:path: ", path + # echo "FS:path: ", path if handle.cancel: - echo "FS:CANCELLED!" + # echo "FS:CANCELLED!" break var @@ -230,7 +230,7 @@ iterator iter*[K, V](handle: var DbQueryHandle[K, V, FsQueryEnv[K,V]] let flres = (basePath / path).absolutePath().catch if flres.isErr(): - echo "FS:ERROR: ", flres.error() + # echo "FS:ERROR: ", flres.error() yield DbQueryResponse[K,V].failure flres.error() let @@ -239,14 +239,15 @@ iterator iter*[K, V](handle: var DbQueryHandle[K, V, FsQueryEnv[K,V]] if handle.query.value: let res = readFile[V](handle.env.self, flres.get) if res.isErr(): - echo "FS:ERROR: ", res.error() + # echo "FS:ERROR: ", res.error() yield DbQueryResponse[K,V].failure res.error() res.get() else: V.new() - echo "FS:SUCCESS: ", key + # echo "FS:SUCCESS: ", key yield success (key.some, data) + handle.close() proc newFSDatastore*[K,V](root: string, depth = 2, From 16bd98d32630de313c086c128516e4d6b1a16378 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 19:23:47 -0700 Subject: [PATCH 50/74] refactor - tests --- datastore/fsds.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index f81ec26e..cad4a8e2 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -241,7 +241,8 @@ iterator iter*[K, V](handle: var DbQueryHandle[K, V, FsQueryEnv[K,V]] if res.isErr(): # echo "FS:ERROR: ", res.error() yield DbQueryResponse[K,V].failure res.error() - res.get() + else: + res.get() else: V.new() From ae311856a86003f5c5c69ae9eea82f7d23dc5bfd Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 19:27:43 -0700 Subject: [PATCH 51/74] refactor - tests --- datastore/fsds.nim | 5 +- tests/datastore/backendCommonTests.nim | 151 +++++++++++++------------ tests/datastore/testfsds.nim | 2 +- 3 files changed, 80 insertions(+), 78 deletions(-) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index cad4a8e2..3e09210e 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -232,6 +232,7 @@ iterator iter*[K, V](handle: var DbQueryHandle[K, V, FsQueryEnv[K,V]] if flres.isErr(): # echo "FS:ERROR: ", flres.error() yield DbQueryResponse[K,V].failure flres.error() + continue let key = K.toKey($Key.init(keyPath).expect("valid key")) @@ -241,8 +242,8 @@ iterator iter*[K, V](handle: var DbQueryHandle[K, V, FsQueryEnv[K,V]] if res.isErr(): # echo "FS:ERROR: ", res.error() yield DbQueryResponse[K,V].failure res.error() - else: - res.get() + continue + res.get() else: V.new() diff --git a/tests/datastore/backendCommonTests.nim b/tests/datastore/backendCommonTests.nim index b3fecbe6..e292e6ee 100644 --- a/tests/datastore/backendCommonTests.nim +++ b/tests/datastore/backendCommonTests.nim @@ -197,109 +197,110 @@ template queryTests*( res[2].key.get == key3 res[2].data == val3 - test "Should apply limit": - let - key = Key.init("/a").tryGet - q = dbQuery(key= key1, limit= 10, value= false) - - for i in 0..<100: + if extended: + test "Should apply limit": let - key = KeyId.new $Key.init(key, Key.init("/" & $i).tryGet).tryGet - val = DataBuffer.new("val " & $i) - - ds.put(key, val).tryGet - - var - handle = ds.query(q).tryGet - let - res = handle.iter().toSeq().mapIt(it.tryGet()) + key = Key.init("/a").tryGet + q = dbQuery(key= key1, limit= 10, value= false) - check: - res.len == 10 + for i in 0..<100: + let + key = KeyId.new $Key.init(key, Key.init("/" & $i).tryGet).tryGet + val = DataBuffer.new("val " & $i) - test "Should not apply offset": - let - key = Key.init("/a").tryGet - keyId = KeyId.new $key - q = dbQuery(key= keyId, offset= 90) + ds.put(key, val).tryGet - for i in 0..<100: + var + handle = ds.query(q).tryGet let - key = KeyId.new $Key.init(key, Key.init("/" & $i).tryGet).tryGet - val = DataBuffer.new("val " & $i) - - ds.put(key, val).tryGet - - var - qr = ds.query(q) - # echo "RES: ", qr.repr - - var - handle = ds.query(q).tryGet - let - res = handle.iter().toSeq().mapIt(it.tryGet()) - - # echo "RES: ", res.mapIt(it.key) - check: - res.len == 10 + res = handle.iter().toSeq().mapIt(it.tryGet()) - test "Should not apply offset and limit": - let - key = Key.init("/a").tryGet - keyId = KeyId.new $key - q = dbQuery(key= keyId, offset= 95, limit= 5) + check: + res.len == 10 - for i in 0..<100: + test "Should not apply offset": let - key = KeyId.new $Key.init(key, Key.init("/" & $i).tryGet).tryGet - val = DataBuffer.new("val " & $i) + key = Key.init("/a").tryGet + keyId = KeyId.new $key + q = dbQuery(key= keyId, offset= 90) - ds.put(key, val).tryGet + for i in 0..<100: + let + key = KeyId.new $Key.init(key, Key.init("/" & $i).tryGet).tryGet + val = DataBuffer.new("val " & $i) - var - handle = ds.query(q).tryGet - res = handle.iter().toSeq().mapIt(it.tryGet()) + ds.put(key, val).tryGet - check: - res.len == 5 + var + qr = ds.query(q) + # echo "RES: ", qr.repr - for i in 0.. int: - cmp($a.key.get, $b.key.get) + ds.put(key, val).tryGet - kvs = kvs.reversed var handle = ds.query(q).tryGet res = handle.iter().toSeq().mapIt(it.tryGet()) check: - res.len == 100 + res.len == 5 + + for i in 0.. int: + cmp($a.key.get, $b.key.get) + + kvs = kvs.reversed + var + handle = ds.query(q).tryGet + res = handle.iter().toSeq().mapIt(it.tryGet()) + + check: + res.len == 100 + + for i, r in res[1..^1]: + check: + res[i].key.get == kvs[i].key.get + res[i].data == kvs[i].data \ No newline at end of file diff --git a/tests/datastore/testfsds.nim b/tests/datastore/testfsds.nim index 4eb4db30..8a0dd176 100644 --- a/tests/datastore/testfsds.nim +++ b/tests/datastore/testfsds.nim @@ -188,7 +188,7 @@ suite "queryTests": val2 = DataBuffer.new "value for 2" val3 = DataBuffer.new "value for 3" - queryTests(fsNew, key1, key2, key3, val1, val2, val3, extended=true) + queryTests(fsNew, key1, key2, key3, val1, val2, val3, extended=false) removeDir(basePathAbs) require(not dirExists(basePathAbs)) From dcb70ca429da9ac92c01e5889b2d35f598069b76 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 19:35:42 -0700 Subject: [PATCH 52/74] refactor - tests --- datastore/fsds.nim | 2 +- tests/datastore/backendCommonTests.nim | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index 3e09210e..24874a0f 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -102,7 +102,7 @@ proc readFile[V](self: FSDatastore, path: string): ?!V = file.close if not file.open(path): - return failure "unable to open file!" + return failure "unable to open file! path: " & path try: let diff --git a/tests/datastore/backendCommonTests.nim b/tests/datastore/backendCommonTests.nim index e292e6ee..9af3de74 100644 --- a/tests/datastore/backendCommonTests.nim +++ b/tests/datastore/backendCommonTests.nim @@ -160,7 +160,10 @@ template queryTests*( var handle = ds.query(q).tryGet let - res = handle.iter().toSeq().mapIt(it.tryGet()) + res = handle.iter().toSeq().mapIt(block: + echo "RES: ", it.repr + it.tryGet() + ) check: res.len == 2 From 0f0e113f4faae58ea14720cd08f5e1adccc6fbb6 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 19:49:59 -0700 Subject: [PATCH 53/74] refactor - tests --- datastore/fsds.nim | 5 +++-- tests/datastore/backendCommonTests.nim | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index 24874a0f..9d15372f 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -190,6 +190,7 @@ proc query*[K,V]( let key = query.key without path =? self.findPath(key), error: return failure error + echo "query:key: ", key let basePath = # it there is a file in the directory @@ -211,10 +212,10 @@ proc close*[K,V](handle: var DbQueryHandle[K,V,FsQueryEnv[K,V]]) = iterator iter*[K, V](handle: var DbQueryHandle[K, V, FsQueryEnv[K,V]] ): ?!DbQueryResponse[K, V] = let root = $(handle.env.self.root) - # echo "FS:root: ", root + echo "FS:root: ", root for path in root.dirIter(): - # echo "FS:path: ", path + echo "FS:path: ", path if handle.cancel: # echo "FS:CANCELLED!" break diff --git a/tests/datastore/backendCommonTests.nim b/tests/datastore/backendCommonTests.nim index 9af3de74..3e99ceea 100644 --- a/tests/datastore/backendCommonTests.nim +++ b/tests/datastore/backendCommonTests.nim @@ -161,9 +161,11 @@ template queryTests*( handle = ds.query(q).tryGet let res = handle.iter().toSeq().mapIt(block: - echo "RES: ", it.repr + echo "\nRES: ", it.repr + # quit(1) it.tryGet() - ) + ) + # ).filterIt(it.isOk).mapIt(it.tryGet()) check: res.len == 2 From fb73d6b5a48c612eb57d2552607d727a4c3b65b6 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 19:57:50 -0700 Subject: [PATCH 54/74] refactor - tests --- datastore/fsds.nim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index 9d15372f..87fb7ad8 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -212,9 +212,11 @@ proc close*[K,V](handle: var DbQueryHandle[K,V,FsQueryEnv[K,V]]) = iterator iter*[K, V](handle: var DbQueryHandle[K, V, FsQueryEnv[K,V]] ): ?!DbQueryResponse[K, V] = let root = $(handle.env.self.root) + let basePath = $(handle.env.basePath) echo "FS:root: ", root + echo "FS:basePath: ", basePath - for path in root.dirIter(): + for path in basePath.dirIter(): echo "FS:path: ", path if handle.cancel: # echo "FS:CANCELLED!" From 1ef4825c324c06192291dd060697e2cefa36f091 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 19:58:42 -0700 Subject: [PATCH 55/74] refactor - tests --- datastore/fsds.nim | 7 ------- tests/datastore/backendCommonTests.nim | 6 +----- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index 87fb7ad8..d0eacceb 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -213,13 +213,9 @@ iterator iter*[K, V](handle: var DbQueryHandle[K, V, FsQueryEnv[K,V]] ): ?!DbQueryResponse[K, V] = let root = $(handle.env.self.root) let basePath = $(handle.env.basePath) - echo "FS:root: ", root - echo "FS:basePath: ", basePath for path in basePath.dirIter(): - echo "FS:path: ", path if handle.cancel: - # echo "FS:CANCELLED!" break var @@ -233,7 +229,6 @@ iterator iter*[K, V](handle: var DbQueryHandle[K, V, FsQueryEnv[K,V]] let flres = (basePath / path).absolutePath().catch if flres.isErr(): - # echo "FS:ERROR: ", flres.error() yield DbQueryResponse[K,V].failure flres.error() continue @@ -243,14 +238,12 @@ iterator iter*[K, V](handle: var DbQueryHandle[K, V, FsQueryEnv[K,V]] if handle.query.value: let res = readFile[V](handle.env.self, flres.get) if res.isErr(): - # echo "FS:ERROR: ", res.error() yield DbQueryResponse[K,V].failure res.error() continue res.get() else: V.new() - # echo "FS:SUCCESS: ", key yield success (key.some, data) handle.close() diff --git a/tests/datastore/backendCommonTests.nim b/tests/datastore/backendCommonTests.nim index 3e99ceea..a7fd3145 100644 --- a/tests/datastore/backendCommonTests.nim +++ b/tests/datastore/backendCommonTests.nim @@ -160,11 +160,7 @@ template queryTests*( var handle = ds.query(q).tryGet let - res = handle.iter().toSeq().mapIt(block: - echo "\nRES: ", it.repr - # quit(1) - it.tryGet() - ) + res = handle.iter().toSeq().mapIt(it.tryGet()) # ).filterIt(it.isOk).mapIt(it.tryGet()) check: From ea4790a6b01095a5b317eee846b803dd3232068e Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 19:59:41 -0700 Subject: [PATCH 56/74] refactor - tests --- datastore.nimble | 2 +- datastore/fsds.nim | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/datastore.nimble b/datastore.nimble index bcd31398..f108da78 100644 --- a/datastore.nimble +++ b/datastore.nimble @@ -9,7 +9,7 @@ license = "Apache License 2.0 or MIT" requires "nim >= 1.6.14", "asynctest >= 0.3.1 & < 0.4.0", "chronos#0277b65be2c7a365ac13df002fba6e172be55537", - "questionable", + "questionable#head", "sqlite3_abi", "stew", "unittest2", diff --git a/datastore/fsds.nim b/datastore/fsds.nim index d0eacceb..2877d7cc 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -190,7 +190,6 @@ proc query*[K,V]( let key = query.key without path =? self.findPath(key), error: return failure error - echo "query:key: ", key let basePath = # it there is a file in the directory From 58fcdd9460ccd34cb0f3a5c04a8c01761b65ed41 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 20:04:18 -0700 Subject: [PATCH 57/74] switch to two sqlites for now --- tests/datastore/testtieredds.nim | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/tests/datastore/testtieredds.nim b/tests/datastore/testtieredds.nim index 4ea76a74..4186cd51 100644 --- a/tests/datastore/testtieredds.nim +++ b/tests/datastore/testtieredds.nim @@ -23,7 +23,8 @@ suite "Test Basic Tired Datastore": var ds1: SQLiteDatastore - ds2: FSDatastore + ds2: SQLiteDatastore + # ds2: FSDatastore tiredDs: TieredDatastore setupAll: @@ -32,8 +33,9 @@ suite "Test Basic Tired Datastore": createDir(rootAbs) ds1 = SQLiteDatastore.new(Memory).tryGet - ds2 = FSDatastore.new(rootAbs, depth = 5).tryGet - tiredDs = TieredDatastore.new(@[ds1, ds2]).tryGet + ds2 = SQLiteDatastore.new(Memory).tryGet + # ds2 = FSDatastore.new(rootAbs, depth = 5).tryGet + tiredDs = TieredDatastore.new(@[Datastore ds1, ds2]).tryGet teardownAll: removeDir(rootAbs) @@ -52,14 +54,16 @@ suite "TieredDatastore": var ds1: SQLiteDatastore - ds2: FSDatastore + ds2: SQLiteDatastore + # ds2: FSDatastore setup: removeDir(rootAbs) require(not dirExists(rootAbs)) createDir(rootAbs) ds1 = SQLiteDatastore.new(Memory).get - ds2 = FSDatastore.new(rootAbs, depth = 5).get + ds2 = SQLiteDatastore.new(Memory).get + # ds2 = FSDatastore.new(rootAbs, depth = 5).get teardown: if not ds1.isNil: @@ -76,17 +80,17 @@ suite "TieredDatastore": TieredDatastore.new([]).isErr TieredDatastore.new(@[]).isErr TieredDatastore.new(ds1, ds2).isOk - TieredDatastore.new([ds1, ds2]).isOk - TieredDatastore.new(@[ds1, ds2]).isOk + TieredDatastore.new([Datastore ds1, ds2]).isOk + TieredDatastore.new(@[Datastore ds1, ds2]).isOk test "accessors": let - stores = @[ds1, ds2] + stores = @[Datastore ds1, ds2] check: TieredDatastore.new(ds1, ds2).tryGet.stores == stores - TieredDatastore.new([ds1, ds2]).tryGet.stores == stores - TieredDatastore.new(@[ds1, ds2]).tryGet.stores == stores + TieredDatastore.new([Datastore ds1, ds2]).tryGet.stores == stores + TieredDatastore.new(@[Datastore ds1, ds2]).tryGet.stores == stores test "put": let From e761957fb654305601d49f3a2999f5dfc617a6a7 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 20:06:01 -0700 Subject: [PATCH 58/74] switch to two sqlites for now --- tests/datastore/testmountedds.nim | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/datastore/testmountedds.nim b/tests/datastore/testmountedds.nim index fbe0dfdc..4e6a7af1 100644 --- a/tests/datastore/testmountedds.nim +++ b/tests/datastore/testmountedds.nim @@ -28,7 +28,8 @@ suite "Test Basic Mounted Datastore": var sql: SQLiteDatastore - fs: FSDatastore + sql2: SQLiteDatastore + # fs: FSDatastore mountedDs: MountedDatastore setupAll: @@ -37,10 +38,11 @@ suite "Test Basic Mounted Datastore": createDir(rootAbs) sql = SQLiteDatastore.new(Memory).tryGet - fs = FSDatastore.new(rootAbs, depth = 5).tryGet + sql2 = SQLiteDatastore.new(Memory).tryGet + # fs = FSDatastore.new(rootAbs, depth = 5).tryGet mountedDs = MountedDatastore.new({ sqlKey: Datastore(sql), - fsKey: Datastore(fs)}.toTable) + fsKey: Datastore(sql2)}.toTable) .tryGet teardownAll: From b7c93083c24bf31ece8ca6ce44a6bc8a203d4203 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 20:09:53 -0700 Subject: [PATCH 59/74] switch to two sqlites for now --- tests/datastore/testfsds.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/datastore/testfsds.nim b/tests/datastore/testfsds.nim index 8a0dd176..1d954140 100644 --- a/tests/datastore/testfsds.nim +++ b/tests/datastore/testfsds.nim @@ -14,6 +14,7 @@ import pkg/datastore/backend import ./backendCommonTests + suite "Test Basic FSDatastore": let path = currentSourcePath() # get this file's name From 8f2f06e39f11db4fef1beb3ab8749c08d617dc97 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 20:10:47 -0700 Subject: [PATCH 60/74] skip fsds for now --- tests/testall.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testall.nim b/tests/testall.nim index 83bdf381..f058de01 100644 --- a/tests/testall.nim +++ b/tests/testall.nim @@ -1,7 +1,7 @@ import ./datastore/testkey, ./datastore/testdatastore, - ./datastore/testfsds, + # ./datastore/testfsds, ./datastore/testsql, ./datastore/testtieredds, ./datastore/testmountedds, From 4b804d1c9f681a22d61168066d09e6ce1b029bb7 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 20:13:33 -0700 Subject: [PATCH 61/74] change threding lib --- datastore.nimble | 2 +- tests/testall.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datastore.nimble b/datastore.nimble index f108da78..a6505981 100644 --- a/datastore.nimble +++ b/datastore.nimble @@ -14,7 +14,7 @@ requires "nim >= 1.6.14", "stew", "unittest2", "pretty", - "threading", + "https://github.com/elcritch/threading#test-smartptrsleak", "taskpools", "upraises >= 0.1.0 & < 0.2.0", "chronicles" diff --git a/tests/testall.nim b/tests/testall.nim index f058de01..47e2960a 100644 --- a/tests/testall.nim +++ b/tests/testall.nim @@ -1,8 +1,8 @@ import ./datastore/testkey, ./datastore/testdatastore, - # ./datastore/testfsds, ./datastore/testsql, + ./datastore/testfsds, ./datastore/testtieredds, ./datastore/testmountedds, ./datastore/testdatabuffer, From e6afc68caba26782afe9c85aba457b9429621ba7 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 20:28:40 -0700 Subject: [PATCH 62/74] whats up with windows + questionable + generics --- datastore/threads/threadproxyds.nim | 30 +++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/datastore/threads/threadproxyds.nim b/datastore/threads/threadproxyds.nim index 8be8a8a5..e85038c8 100644 --- a/datastore/threads/threadproxyds.nim +++ b/datastore/threads/threadproxyds.nim @@ -141,8 +141,9 @@ proc hasTask[T, DB](ctx: TaskCtx[T], ds: DB, key: KeyId) {.gcsafe.} = method has*[BT](self: ThreadDatastore[BT], key: Key): Future[?!bool] {.async.} = await self.semaphore.acquire() - without signal =? acquireSignal(), err: - return failure err + let signal = acquireSignal().get() + # without signal =? acquireSignal(), err: + # return failure err let ctx = newTaskCtx(bool, signal=signal) dispatchTask(self, signal): @@ -160,8 +161,9 @@ method delete*[BT](self: ThreadDatastore[BT], key: Key): Future[?!void] {.async.} = ## delete key await self.semaphore.acquire() - without signal =? acquireSignal(), err: - return failure err + let signal = acquireSignal().get() + # without signal =? acquireSignal(), err: + # return failure err let ctx = newTaskCtx(void, signal=signal) dispatchTask(self, signal): @@ -191,8 +193,9 @@ method put*[BT](self: ThreadDatastore[BT], data: seq[byte]): Future[?!void] {.async.} = ## put key with data await self.semaphore.acquire() - without signal =? acquireSignal(), err: - return failure err + let signal = acquireSignal().get() + # without signal =? acquireSignal(), err: + # return failure err let ctx = newTaskCtx(void, signal=signal) dispatchTask(self, signal): @@ -224,8 +227,9 @@ method get*[BT](self: ThreadDatastore[BT], key: Key, ): Future[?!seq[byte]] {.async.} = await self.semaphore.acquire() - without signal =? acquireSignal(), err: - return failure err + let signal = acquireSignal().get() + # without signal =? acquireSignal(), err: + # return failure err let ctx = newTaskCtx(DataBuffer, signal=signal) dispatchTask(self, signal): @@ -287,10 +291,12 @@ method query*[BT](self: ThreadDatastore[BT], ## keeps one thread running queryTask until finished ## await self.semaphore.acquire() - without signal =? acquireSignal(), err: - return failure err - without nextSignal =? acquireSignal(), err: - return failure err + let signal = acquireSignal().get() + # without signal =? acquireSignal(), err: + # return failure err + let nextSignal = acquireSignal().get() + # without nextSignal =? acquireSignal(), err: + # return failure err let ctx = newTaskCtx(QResult, signal=signal, nextSignal=nextSignal) proc iterDispose() {.async.} = From eba40334b8c53a65dae560e13a00a7b4d491270f Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 21:03:35 -0700 Subject: [PATCH 63/74] whats up with windows + questionable + generics --- datastore/fsds.nim | 15 ++++++-- datastore/sql/sqliteds.nim | 17 +++++++-- datastore/threads/threadproxyds.nim | 10 ++++- tests/datastore/testthreadproxyds.nim | 55 +++++++++++++-------------- 4 files changed, 59 insertions(+), 38 deletions(-) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index 2877d7cc..ee18c566 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -178,6 +178,12 @@ proc close*[K,V](self: FSDatastore[K,V]): ?!void = return success() type + FsQueryHandle*[K, V] = object + query*: DbQuery[K] + cancel*: bool + closed*: bool + env*: FsQueryEnv[K,V] + FsQueryEnv*[K,V] = object self: FSDatastore[K,V] basePath: DataBuffer @@ -185,7 +191,7 @@ type proc query*[K,V]( self: FSDatastore[K,V], query: DbQuery[K], -): Result[DbQueryHandle[K, V, FsQueryEnv[K,V]], ref CatchableError] = +): Result[FsQueryHandle[K, V], ref CatchableError] = let key = query.key without path =? self.findPath(key), error: @@ -202,14 +208,15 @@ proc query*[K,V]( path.changeFileExt("") let env = FsQueryEnv[K,V](self: self, basePath: DataBuffer.new(basePath)) - success DbQueryHandle[KeyId, V, FsQueryEnv[K,V]](query: query, env: env) + success FsQueryHandle[K, V](query: query, env: env) proc close*[K,V](handle: var DbQueryHandle[K,V,FsQueryEnv[K,V]]) = if not handle.closed: handle.closed = true -iterator iter*[K, V](handle: var DbQueryHandle[K, V, FsQueryEnv[K,V]] - ): ?!DbQueryResponse[K, V] = +iterator queryIter*[K, V]( + handle: var FsQueryHandle[K, V] +): ?!DbQueryResponse[K, V] = let root = $(handle.env.self.root) let basePath = $(handle.env.basePath) diff --git a/datastore/sql/sqliteds.nim b/datastore/sql/sqliteds.nim index e86fb905..e642b070 100644 --- a/datastore/sql/sqliteds.nim +++ b/datastore/sql/sqliteds.nim @@ -103,10 +103,17 @@ proc close*[K,V](self: SQLiteBackend[K,V]): ?!void = return success() +type + SqQueryHandle*[K, V] = object + query*: DbQuery[K] + cancel*: bool + closed*: bool + env*: RawStmtPtr + proc query*[K,V]( self: SQLiteBackend[K,V], query: DbQuery[K] -): Result[DbQueryHandle[K,V,RawStmtPtr], ref CatchableError] = +): Result[SqQueryHandle[K,V], ref CatchableError] = var queryStr = if query.value: @@ -151,16 +158,18 @@ proc query*[K,V]( if not (v == SQLITE_OK): return failure newException(DatastoreError, $sqlite3_errstr(v)) - success DbQueryHandle[K,V,RawStmtPtr](query: query, env: s) + success SqQueryHandle[K,V](query: query, env: s) -proc close*[K,V](handle: var DbQueryHandle[K,V,RawStmtPtr]) = +proc close*[K,V](handle: var SqQueryHandle[K,V]) = if not handle.closed: handle.closed = true discard sqlite3_reset(handle.env) discard sqlite3_clear_bindings(handle.env) handle.env.dispose() -iterator iter*[K, V](handle: var DbQueryHandle[K, V, RawStmtPtr]): ?!DbQueryResponse[K, V] = +iterator queyIter*[K, V]( + handle: var SqQueryHandle[K, V] +): ?!DbQueryResponse[K, V] = while not handle.cancel: let v = sqlite3_step(handle.env) diff --git a/datastore/threads/threadproxyds.nim b/datastore/threads/threadproxyds.nim index e85038c8..1f4d6796 100644 --- a/datastore/threads/threadproxyds.nim +++ b/datastore/threads/threadproxyds.nim @@ -23,6 +23,7 @@ import ../key import ../query import ../datastore import ../backend +import ../fsds import ../sql/sqliteds import ./asyncsemaphore @@ -251,10 +252,13 @@ method queryTask[DB]( query: DbQuery[KeyId], ) {.gcsafe, nimcall.} = ## run query command + mixin queryIter executeTask(ctx): # we execute this all inside `executeTask` # so we need to return a final result - let handleRes = ds.query(query) + let handleRes = query(ds, query) + static: + echo "HANDLE_RES: ", typeof(handleRes) if handleRes.isErr(): # set error and exit executeTask, which will fire final signal (?!QResult).err(handleRes.error()) @@ -266,7 +270,9 @@ method queryTask[DB]( raise newException(DeadThreadDefect, "queryTask timed out") var handle = handleRes.get() - for item in handle.iter(): + static: + echo "HANDLE: ", typeof(handle) + for item in handle.queyIter(): # wait for next request from async thread if ctx[].cancelled: diff --git a/tests/datastore/testthreadproxyds.nim b/tests/datastore/testthreadproxyds.nim index d59992cc..801f987b 100644 --- a/tests/datastore/testthreadproxyds.nim +++ b/tests/datastore/testthreadproxyds.nim @@ -15,8 +15,8 @@ import pkg/questionable/results import pkg/chronicles import pkg/threading/smartptrs -import pkg/datastore/sql/sqliteds import pkg/datastore/fsds +import pkg/datastore/sql/sqliteds import pkg/datastore/threads/threadproxyds {.all.} import ./dscommontests @@ -82,40 +82,39 @@ for i in 1..N: queryTests(ds, true) GC_fullCollect() -# suite "Test Basic ThreadDatastore with fsds": -# let -# path = currentSourcePath() # get this file's name -# basePath = "tests_data" -# basePathAbs = path.parentDir / basePath -# key = Key.init("/a/b").tryGet() -# bytes = "some bytes".toBytes -# otherBytes = "some other bytes".toBytes +suite "Test Basic ThreadDatastore with fsds": + let + path = currentSourcePath() # get this file's name + basePath = "tests_data" + basePathAbs = path.parentDir / basePath + key = Key.init("/a/b").tryGet() + bytes = "some bytes".toBytes + otherBytes = "some other bytes".toBytes -# var -# fsStore: FSDatastore -# ds: ThreadDatastore -# taskPool: Taskpool + var + fsStore: FSDatastore[KeyId, DataBuffer] + ds: ThreadDatastore[FSDatastore[KeyId, DataBuffer]] + taskPool: Taskpool -# setupAll: -# removeDir(basePathAbs) -# require(not dirExists(basePathAbs)) -# createDir(basePathAbs) + setupAll: + removeDir(basePathAbs) + require(not dirExists(basePathAbs)) + createDir(basePathAbs) -# fsStore = FSDatastore.new(root = basePathAbs, depth = 3).tryGet() -# taskPool = Taskpool.new(NumThreads) -# ds = ThreadDatastore.new(fsStore, withLocks = true, tp = taskPool).tryGet() + fsStore = newFSDatastore[KeyId, DataBuffer](root = basePathAbs, depth = 3).tryGet() + ds = ThreadDatastore.new(fsStore, tp = taskPool).tryGet() -# teardown: -# GC_fullCollect() + teardown: + GC_fullCollect() -# teardownAll: -# (await ds.close()).tryGet() -# taskPool.shutdown() + teardownAll: + (await ds.close()).tryGet() -# removeDir(basePathAbs) -# require(not dirExists(basePathAbs)) + removeDir(basePathAbs) + require(not dirExists(basePathAbs)) + + basicStoreTests(ds, key, bytes, otherBytes) -# basicStoreTests(fsStore, key, bytes, otherBytes) # suite "Test Query ThreadDatastore with fsds": # let From e6533643115c689a278e6bc7023b2e09da174352 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 21:07:31 -0700 Subject: [PATCH 64/74] whats up with windows + questionable + generics --- datastore/fsds.nim | 2 +- datastore/sql/sqliteds.nim | 2 +- datastore/threads/threadproxyds.nim | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/datastore/fsds.nim b/datastore/fsds.nim index ee18c566..e3a6464a 100644 --- a/datastore/fsds.nim +++ b/datastore/fsds.nim @@ -210,7 +210,7 @@ proc query*[K,V]( let env = FsQueryEnv[K,V](self: self, basePath: DataBuffer.new(basePath)) success FsQueryHandle[K, V](query: query, env: env) -proc close*[K,V](handle: var DbQueryHandle[K,V,FsQueryEnv[K,V]]) = +proc close*[K,V](handle: var FsQueryHandle[K,V]) = if not handle.closed: handle.closed = true diff --git a/datastore/sql/sqliteds.nim b/datastore/sql/sqliteds.nim index e642b070..79d4a31c 100644 --- a/datastore/sql/sqliteds.nim +++ b/datastore/sql/sqliteds.nim @@ -167,7 +167,7 @@ proc close*[K,V](handle: var SqQueryHandle[K,V]) = discard sqlite3_clear_bindings(handle.env) handle.env.dispose() -iterator queyIter*[K, V]( +iterator queryIter*[K, V]( handle: var SqQueryHandle[K, V] ): ?!DbQueryResponse[K, V] = while not handle.cancel: diff --git a/datastore/threads/threadproxyds.nim b/datastore/threads/threadproxyds.nim index 1f4d6796..8d85c843 100644 --- a/datastore/threads/threadproxyds.nim +++ b/datastore/threads/threadproxyds.nim @@ -152,7 +152,7 @@ method has*[BT](self: ThreadDatastore[BT], self.tp.spawn hasTask(ctx, ds, key) return ctx[].res.toRes(v => v) -method deleteTask[T, DB](ctx: TaskCtx[T], ds: DB; +proc deleteTask[T, DB](ctx: TaskCtx[T], ds: DB; key: KeyId) {.gcsafe.} = ## run backend command executeTask(ctx): @@ -217,7 +217,7 @@ method put*[DB]( return success() -method getTask[DB](ctx: TaskCtx[DataBuffer], ds: DB; +proc getTask[DB](ctx: TaskCtx[DataBuffer], ds: DB; key: KeyId) {.gcsafe, nimcall.} = ## run backend command executeTask(ctx): @@ -246,11 +246,11 @@ method close*[BT](self: ThreadDatastore[BT]): Future[?!void] {.async.} = type QResult = DbQueryResponse[KeyId, DataBuffer] -method queryTask[DB]( +proc queryTask[DB]( ctx: TaskCtx[QResult], ds: DB, query: DbQuery[KeyId], -) {.gcsafe, nimcall.} = +) = ## run query command mixin queryIter executeTask(ctx): @@ -272,7 +272,7 @@ method queryTask[DB]( var handle = handleRes.get() static: echo "HANDLE: ", typeof(handle) - for item in handle.queyIter(): + for item in handle.queryIter(): # wait for next request from async thread if ctx[].cancelled: From f98432da858ef7cc3074efd24d41a7be9d87f8d0 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 21:08:40 -0700 Subject: [PATCH 65/74] fixed thing --- tests/datastore/testthreadproxyds.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/datastore/testthreadproxyds.nim b/tests/datastore/testthreadproxyds.nim index 801f987b..78a637ea 100644 --- a/tests/datastore/testthreadproxyds.nim +++ b/tests/datastore/testthreadproxyds.nim @@ -94,7 +94,6 @@ suite "Test Basic ThreadDatastore with fsds": var fsStore: FSDatastore[KeyId, DataBuffer] ds: ThreadDatastore[FSDatastore[KeyId, DataBuffer]] - taskPool: Taskpool setupAll: removeDir(basePathAbs) From d2d051d51f80a251ff9d66b066e6ff9f3ac71ed9 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 21:10:30 -0700 Subject: [PATCH 66/74] fixed thing --- datastore/threads/threadproxyds.nim | 1 - tests/datastore/testthreadproxyds.nim | 1 - 2 files changed, 2 deletions(-) diff --git a/datastore/threads/threadproxyds.nim b/datastore/threads/threadproxyds.nim index 8d85c843..6dc1ae5d 100644 --- a/datastore/threads/threadproxyds.nim +++ b/datastore/threads/threadproxyds.nim @@ -133,7 +133,6 @@ template dispatchTask[BT](self: ThreadDatastore[BT], discard ctx[].signal.close() self.semaphore.release() - proc hasTask[T, DB](ctx: TaskCtx[T], ds: DB, key: KeyId) {.gcsafe.} = ## run backend command executeTask(ctx): diff --git a/tests/datastore/testthreadproxyds.nim b/tests/datastore/testthreadproxyds.nim index 78a637ea..3a309484 100644 --- a/tests/datastore/testthreadproxyds.nim +++ b/tests/datastore/testthreadproxyds.nim @@ -114,7 +114,6 @@ suite "Test Basic ThreadDatastore with fsds": basicStoreTests(ds, key, bytes, otherBytes) - # suite "Test Query ThreadDatastore with fsds": # let # path = currentSourcePath() # get this file's name From 3822a97593b11922fb81cf8bbcf98ca3b760fcf8 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 27 Sep 2023 21:13:06 -0700 Subject: [PATCH 67/74] fixed thing --- datastore/sql.nim | 9 ++++----- tests/datastore/backendCommonTests.nim | 18 +++++++++--------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/datastore/sql.nim b/datastore/sql.nim index 28b1f3d0..cfe67ac6 100644 --- a/datastore/sql.nim +++ b/datastore/sql.nim @@ -60,10 +60,9 @@ method put*(self: SQLiteDatastore, method close*(self: SQLiteDatastore): Future[?!void] {.async.} = self.db.close() -method queryIter*( - self: SQLiteDatastore, - query: Query -): ?!(iterator(): ?!QueryResponse) = +method queryIter*(self: SQLiteDatastore, + query: Query + ): ?!(iterator(): ?!QueryResponse) = let dbquery = dbQuery( key= KeyId.new query.key.id(), @@ -75,7 +74,7 @@ method queryIter*( var qhandle = ? self.db.query(dbquery) let iter = iterator(): ?!QueryResponse = - for resp in qhandle.iter(): + for resp in qhandle.queryIter(): without qres =? resp, err: yield QueryResponse.failure err let k = qres.key.map() do(k: KeyId) -> Key: diff --git a/tests/datastore/backendCommonTests.nim b/tests/datastore/backendCommonTests.nim index a7fd3145..f35fd328 100644 --- a/tests/datastore/backendCommonTests.nim +++ b/tests/datastore/backendCommonTests.nim @@ -81,7 +81,7 @@ template queryTests*( var handle = ds.query(q).tryGet - res = handle.iter().toSeq().mapIt(it.tryGet()) + res = handle.queryIter().toSeq().mapIt(it.tryGet()) check: res.len == 3 @@ -107,7 +107,7 @@ template queryTests*( var res: seq[DbQueryResponse[KeyId, DataBuffer]] var cnt = 0 - for item in handle.iter(): + for item in handle.queryIter(): cnt.inc res.insert(item.tryGet(), 0) if cnt > 1: @@ -135,7 +135,7 @@ template queryTests*( var handle = ds.query(q).tryGet let - res = handle.iter().toSeq().mapIt(it.tryGet()) + res = handle.queryIter().toSeq().mapIt(it.tryGet()) check: res.len == 3 @@ -160,7 +160,7 @@ template queryTests*( var handle = ds.query(q).tryGet let - res = handle.iter().toSeq().mapIt(it.tryGet()) + res = handle.queryIter().toSeq().mapIt(it.tryGet()) # ).filterIt(it.isOk).mapIt(it.tryGet()) check: @@ -182,7 +182,7 @@ template queryTests*( var handle = ds.query(q).tryGet - res = handle.iter().toSeq().mapIt(it.tryGet()) + res = handle.queryIter().toSeq().mapIt(it.tryGet()) res.sort do (a, b: DbQueryResponse[KeyId, DataBuffer]) -> int: cmp($a.key.get, $b.key.get) @@ -214,7 +214,7 @@ template queryTests*( var handle = ds.query(q).tryGet let - res = handle.iter().toSeq().mapIt(it.tryGet()) + res = handle.queryIter().toSeq().mapIt(it.tryGet()) check: res.len == 10 @@ -239,7 +239,7 @@ template queryTests*( var handle = ds.query(q).tryGet let - res = handle.iter().toSeq().mapIt(it.tryGet()) + res = handle.queryIter().toSeq().mapIt(it.tryGet()) # echo "RES: ", res.mapIt(it.key) check: @@ -260,7 +260,7 @@ template queryTests*( var handle = ds.query(q).tryGet - res = handle.iter().toSeq().mapIt(it.tryGet()) + res = handle.queryIter().toSeq().mapIt(it.tryGet()) check: res.len == 5 @@ -296,7 +296,7 @@ template queryTests*( kvs = kvs.reversed var handle = ds.query(q).tryGet - res = handle.iter().toSeq().mapIt(it.tryGet()) + res = handle.queryIter().toSeq().mapIt(it.tryGet()) check: res.len == 100 From 46f22dc10e46ac592f1250d22cd57eb027a55e13 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Thu, 28 Sep 2023 13:24:55 -0700 Subject: [PATCH 68/74] test queries --- tests/datastore/testthreadproxyds.nim | 43 +++++++++++++-------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/tests/datastore/testthreadproxyds.nim b/tests/datastore/testthreadproxyds.nim index 3a309484..2fd1bb24 100644 --- a/tests/datastore/testthreadproxyds.nim +++ b/tests/datastore/testthreadproxyds.nim @@ -114,35 +114,32 @@ suite "Test Basic ThreadDatastore with fsds": basicStoreTests(ds, key, bytes, otherBytes) -# suite "Test Query ThreadDatastore with fsds": -# let -# path = currentSourcePath() # get this file's name -# basePath = "tests_data" -# basePathAbs = path.parentDir / basePath +suite "Test Query ThreadDatastore with fsds": + let + path = currentSourcePath() # get this file's name + basePath = "tests_data" + basePathAbs = path.parentDir / basePath -# var -# fsStore: FSDatastore -# ds: ThreadDatastore -# taskPool: Taskpool + var + fsStore: FSDatastore[KeyId, DataBuffer] + ds: ThreadDatastore[FSDatastore[KeyId, DataBuffer]] -# setup: -# removeDir(basePathAbs) -# require(not dirExists(basePathAbs)) -# createDir(basePathAbs) + setup: + removeDir(basePathAbs) + require(not dirExists(basePathAbs)) + createDir(basePathAbs) -# fsStore = FSDatastore.new(root = basePathAbs, depth = 5).tryGet() -# taskPool = Taskpool.new(NumThreads) -# ds = ThreadDatastore.new(fsStore, withLocks = true, tp = taskPool).tryGet() + fsStore = newFSDatastore[KeyId, DataBuffer](root = basePathAbs, depth = 5).tryGet() + ds = ThreadDatastore.new(fsStore, tp = taskPool).tryGet() -# teardown: -# GC_fullCollect() -# (await ds.close()).tryGet() -# taskPool.shutdown() + teardown: + GC_fullCollect() + (await ds.close()).tryGet() -# removeDir(basePathAbs) -# require(not dirExists(basePathAbs)) + removeDir(basePathAbs) + require(not dirExists(basePathAbs)) -# queryTests(ds, false) + queryTests(ds, false) # suite "Test ThreadDatastore cancelations": # var From bd79cb73578c9ced8b987c2b436091717bb39b4d Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Thu, 28 Sep 2023 13:47:48 -0700 Subject: [PATCH 69/74] test task cancel --- datastore/threads/threadproxyds.nim | 10 ++-- tests/datastore/testthreadproxyds.nim | 75 +++++++++++---------------- 2 files changed, 35 insertions(+), 50 deletions(-) diff --git a/datastore/threads/threadproxyds.nim b/datastore/threads/threadproxyds.nim index 6dc1ae5d..cbe5e455 100644 --- a/datastore/threads/threadproxyds.nim +++ b/datastore/threads/threadproxyds.nim @@ -38,10 +38,10 @@ logScope: type TaskCtxObj*[T: ThreadTypes] = object - res: ThreadResult[T] + res*: ThreadResult[T] signal: ThreadSignalPtr - running: bool ## used to mark when a task worker is running - cancelled: bool ## used to cancel a task before it's started + running*: bool ## used to mark when a task worker is running + cancelled*: bool ## used to cancel a task before it's started nextSignal: ThreadSignalPtr TaskCtx*[T] = SharedPtr[TaskCtxObj[T]] @@ -78,7 +78,7 @@ proc acquireSignal(): ?!ThreadSignalPtr = else: success signal.get() -template executeTask[T](ctx: TaskCtx[T], blk: untyped) = +template executeTask*[T](ctx: TaskCtx[T], blk: untyped) = ## executes a task on a thread work and handles cleanup after cancels/errors ## try: @@ -115,7 +115,7 @@ template dispatchTaskWrap[BT](self: ThreadDatastore[BT], runTask() await wait(ctx[].signal) -template dispatchTask[BT](self: ThreadDatastore[BT], +template dispatchTask*[BT](self: ThreadDatastore[BT], signal: ThreadSignalPtr, blk: untyped ): auto = diff --git a/tests/datastore/testthreadproxyds.nim b/tests/datastore/testthreadproxyds.nim index 2fd1bb24..45ac6ad0 100644 --- a/tests/datastore/testthreadproxyds.nim +++ b/tests/datastore/testthreadproxyds.nim @@ -141,55 +141,40 @@ suite "Test Query ThreadDatastore with fsds": queryTests(ds, false) -# suite "Test ThreadDatastore cancelations": -# var -# sqlStore: SQLiteBackend[KeyId,DataBuffer] -# ds: ThreadDatastore -# taskPool: Taskpool - -# privateAccess(ThreadDatastore) # expose private fields -# privateAccess(TaskCtx) # expose private fields - -# setupAll: -# sqlStore = newSQLiteBackend[KeyId, DataBuffer](Memory).tryGet() -# taskPool = Taskpool.new(NumThreads) -# ds = ThreadDatastore.new(sqlStore, tp = taskPool).tryGet() - -# teardown: -# GC_fullCollect() # run full collect after each test - -# teardownAll: -# (await ds.close()).tryGet() -# taskPool.shutdown() - - # test "Should monitor signal and cancel": - # var - # signal = ThreadSignalPtr.new().tryGet() - # res = ThreadResult[void]() - # ctx = newSharedPtr(TaskCtxObj[void](signal: signal)) - # fut = newFuture[void]("signalMonitor") - # threadArgs = (addr ctx, addr fut) - # thread: Thread[type threadArgs] - - # proc threadTask(args: type threadArgs) = - # var (ctx, fut) = args - # proc asyncTask() {.async.} = - # let - # monitor = signalMonitor(ctx, fut[]) - - # await monitor +suite "Test ThreadDatastore cancelations": + var + sqlStore: SQLiteBackend[KeyId,DataBuffer] + sds: ThreadDatastore[SQLiteBackend[KeyId, DataBuffer]] - # waitFor asyncTask() + privateAccess(ThreadDatastore) # expose private fields + privateAccess(TaskCtx) # expose private fields - # createThread(thread, threadTask, threadArgs) - # ctx.cancelled = true - # check: ctx.signal.fireSync.tryGet + setupAll: + sqlStore = newSQLiteBackend[KeyId, DataBuffer](Memory).tryGet() + sds = ThreadDatastore.new(sqlStore, tp = taskPool).tryGet() - # joinThreads(thread) + teardown: + GC_fullCollect() # run full collect after each test - # check: fut.cancelled - # check: ctx.signal.close().isOk - # fut = nil + test "Should monitor signal and cancel": + var + signal = ThreadSignalPtr.new().tryGet() + res = ThreadResult[void]() + + proc cancelTestTask[T](ctx: TaskCtx[T]) {.gcsafe.} = + executeTask(ctx): + (?!bool).ok(true) + + let ctx = newTaskCtx(bool, signal=signal) + ctx[].cancelled = true + dispatchTask(sds, signal): + sds.tp.spawn cancelTestTask(ctx) + + echo "ctx: ", ctx[] + check: + ctx[].res.isErr == true + ctx[].cancelled == true + ctx[].running == false # test "Should monitor and not cancel": # var From 248174630143d9f7356160c8510fcd895b5ffaee Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Thu, 28 Sep 2023 14:25:25 -0700 Subject: [PATCH 70/74] test task cancel --- datastore/threads/threadproxyds.nim | 2 - tests/datastore/testthreadproxyds.nim | 80 ++++++++++++++------------- 2 files changed, 43 insertions(+), 39 deletions(-) diff --git a/datastore/threads/threadproxyds.nim b/datastore/threads/threadproxyds.nim index cbe5e455..19b5def9 100644 --- a/datastore/threads/threadproxyds.nim +++ b/datastore/threads/threadproxyds.nim @@ -305,11 +305,9 @@ method query*[BT](self: ThreadDatastore[BT], let ctx = newTaskCtx(QResult, signal=signal, nextSignal=nextSignal) proc iterDispose() {.async.} = - # echo "signal:CLOSE!" ctx.setCancelled() await ctx[].nextSignal.fire() discard ctx[].signal.close() - # echo "nextSignal:CLOSE!" discard ctx[].nextSignal.close() self.semaphore.release() diff --git a/tests/datastore/testthreadproxyds.nim b/tests/datastore/testthreadproxyds.nim index 45ac6ad0..97994113 100644 --- a/tests/datastore/testthreadproxyds.nim +++ b/tests/datastore/testthreadproxyds.nim @@ -44,7 +44,6 @@ for i in 1..N: setupAll: sqlStore = newSQLiteBackend[KeyId, DataBuffer](Memory).tryGet() - # taskPool = Taskpool.new(NumThreads) ds = ThreadDatastore.new(sqlStore, tp = taskPool).tryGet() teardown: @@ -52,7 +51,6 @@ for i in 1..N: teardownAll: (await ds.close()).tryGet() - # taskPool.shutdown() for i in 1..M: basicStoreTests(ds, key, bytes, otherBytes) @@ -76,7 +74,6 @@ for i in 1..N: GC_fullCollect() (await ds.close()).tryGet() - # taskPool.shutdown() for i in 1..M: queryTests(ds, true) @@ -159,9 +156,8 @@ suite "Test ThreadDatastore cancelations": test "Should monitor signal and cancel": var signal = ThreadSignalPtr.new().tryGet() - res = ThreadResult[void]() - proc cancelTestTask[T](ctx: TaskCtx[T]) {.gcsafe.} = + proc cancelTestTask(ctx: TaskCtx[bool]) {.gcsafe.} = executeTask(ctx): (?!bool).ok(true) @@ -170,40 +166,50 @@ suite "Test ThreadDatastore cancelations": dispatchTask(sds, signal): sds.tp.spawn cancelTestTask(ctx) - echo "ctx: ", ctx[] check: ctx[].res.isErr == true ctx[].cancelled == true ctx[].running == false - # test "Should monitor and not cancel": - # var - # signal = ThreadSignalPtr.new().tryGet() - # res = ThreadResult[void]() - # ctx = TaskCtx[void]( - # ds: sqlStore, - # res: addr res, - # signal: signal) - # fut = newFuture[void]("signalMonitor") - # threadArgs = (addr ctx, addr fut) - # thread: Thread[type threadArgs] - - # proc threadTask(args: type threadArgs) = - # var (ctx, fut) = args - # proc asyncTask() {.async.} = - # let - # monitor = signalMonitor(ctx, fut[]) - - # await monitor - - # waitFor asyncTask() - - # createThread(thread, threadTask, threadArgs) - # ctx.cancelled = false - # check: ctx.signal.fireSync.tryGet - - # joinThreads(thread) - - # check: not fut.cancelled - # check: ctx.signal.close().isOk - # fut = nil + test "Should cancel future": + + var + signal = ThreadSignalPtr.new().tryGet() + ms {.global.}: MutexSignal + flag {.global.}: int = 0 + + ms.init() + + type + TestValue = object + ThreadTestInt = (TestValue, ) + + proc `=destroy`(obj: var TestValue) = + echo "destroy TestObj!" + flag = 10 + + proc errorTestTask(ctx: TaskCtx[ThreadTestInt]) {.gcsafe, nimcall.} = + executeTask(ctx): + discard ctx[].signal.fireSync() + ms.wait() + (?!ThreadTestInt).ok(default(ThreadTestInt)) + + proc runTestTask() {.async.} = + + let ctx = newTaskCtx(ThreadTestInt, signal=signal) + dispatchTask(sds, signal): + sds.tp.spawn errorTestTask(ctx) + + echo "raise error" + raise newException(ValueError, "fake error") + + try: + await runTestTask() + except CatchableError as exc: + echo "caught: ", $exc + finally: + echo "finish" + ms.fire() + os.sleep(10) + check flag == 10 + From 43520c360833744834fca91df7acee9586652068 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Thu, 28 Sep 2023 14:51:27 -0700 Subject: [PATCH 71/74] test task cancel --- datastore/threads/threadproxyds.nim | 5 +++- tests/datastore/testthreadproxyds.nim | 35 +++++++++++++++++++++------ 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/datastore/threads/threadproxyds.nim b/datastore/threads/threadproxyds.nim index 19b5def9..e1a75718 100644 --- a/datastore/threads/threadproxyds.nim +++ b/datastore/threads/threadproxyds.nim @@ -128,8 +128,10 @@ template dispatchTask*[BT](self: ThreadDatastore[BT], trace "Cancelling thread future!", exc = exc.msg ctx.setCancelled() raise exc + except CatchableError as exc: + ctx.setCancelled() + raise exc finally: - # echo "signal:CLOSE!" discard ctx[].signal.close() self.semaphore.release() @@ -359,6 +361,7 @@ method query*[BT](self: ThreadDatastore[BT], return success iter except CancelledError as exc: trace "Cancelling thread future!", exc = exc.msg + ctx.setCancelled() await iterDispose() raise exc diff --git a/tests/datastore/testthreadproxyds.nim b/tests/datastore/testthreadproxyds.nim index 97994113..c7f825de 100644 --- a/tests/datastore/testthreadproxyds.nim +++ b/tests/datastore/testthreadproxyds.nim @@ -14,6 +14,7 @@ import pkg/taskpools import pkg/questionable/results import pkg/chronicles import pkg/threading/smartptrs +import pkg/threading/atomics import pkg/datastore/fsds import pkg/datastore/sql/sqliteds @@ -176,7 +177,8 @@ suite "Test ThreadDatastore cancelations": var signal = ThreadSignalPtr.new().tryGet() ms {.global.}: MutexSignal - flag {.global.}: int = 0 + flag {.global.}: Atomic[bool] + ready {.global.}: Atomic[bool] ms.init() @@ -186,12 +188,25 @@ suite "Test ThreadDatastore cancelations": proc `=destroy`(obj: var TestValue) = echo "destroy TestObj!" - flag = 10 + flag.store(true) + + proc wait(flag: var Atomic[bool]) = + echo "wait for task to be ready..." + defer: echo "" + for i in 1..100: + stdout.write(".") + if flag.load() == true: + return + os.sleep(10) + raise newException(Defect, "timeout") proc errorTestTask(ctx: TaskCtx[ThreadTestInt]) {.gcsafe, nimcall.} = executeTask(ctx): + echo "task:exec" discard ctx[].signal.fireSync() + ready.store(true) ms.wait() + echo "ctx:task: ", ctx[] (?!ThreadTestInt).ok(default(ThreadTestInt)) proc runTestTask() {.async.} = @@ -199,17 +214,21 @@ suite "Test ThreadDatastore cancelations": let ctx = newTaskCtx(ThreadTestInt, signal=signal) dispatchTask(sds, signal): sds.tp.spawn errorTestTask(ctx) - - echo "raise error" - raise newException(ValueError, "fake error") + ready.wait() + echo "raise error" + raise newException(ValueError, "fake error") try: - await runTestTask() + block: + await runTestTask() except CatchableError as exc: echo "caught: ", $exc finally: echo "finish" + check ready.load() == true + ms.fire() - os.sleep(10) - check flag == 10 + GC_fullCollect() + flag.wait() + check flag.load() == true From 47fd3ba61fbbbfe40c4a8e804b88e0665f877bae Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Thu, 28 Sep 2023 14:57:20 -0700 Subject: [PATCH 72/74] test task cancel --- tests/datastore/testthreadproxyds.nim | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/datastore/testthreadproxyds.nim b/tests/datastore/testthreadproxyds.nim index c7f825de..8a20fd4b 100644 --- a/tests/datastore/testthreadproxyds.nim +++ b/tests/datastore/testthreadproxyds.nim @@ -178,11 +178,13 @@ suite "Test ThreadDatastore cancelations": signal = ThreadSignalPtr.new().tryGet() ms {.global.}: MutexSignal flag {.global.}: Atomic[bool] + futFreed {.global.}: Atomic[bool] ready {.global.}: Atomic[bool] ms.init() type + FutTestObj = object TestValue = object ThreadTestInt = (TestValue, ) @@ -190,6 +192,10 @@ suite "Test ThreadDatastore cancelations": echo "destroy TestObj!" flag.store(true) + proc `=destroy`(obj: var FutTestObj) = + echo "destroy FutTestObj!" + futFreed.store(true) + proc wait(flag: var Atomic[bool]) = echo "wait for task to be ready..." defer: echo "" @@ -211,6 +217,10 @@ suite "Test ThreadDatastore cancelations": proc runTestTask() {.async.} = + let obj = FutTestObj() + await sleepAsync(1.milliseconds) + defer: echo "fut FutTestObj: ", obj + let ctx = newTaskCtx(ThreadTestInt, signal=signal) dispatchTask(sds, signal): sds.tp.spawn errorTestTask(ctx) @@ -226,9 +236,12 @@ suite "Test ThreadDatastore cancelations": finally: echo "finish" check ready.load() == true + GC_fullCollect() + futFreed.wait() + echo "future freed it's mem!" + check futFreed.load() == true ms.fire() - GC_fullCollect() flag.wait() check flag.load() == true From 0df63d61023de9303fcfde018dd8f093e595aae3 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Thu, 28 Sep 2023 15:05:07 -0700 Subject: [PATCH 73/74] test task cancel --- tests/datastore/testthreadproxyds.nim | 199 +++++++++++++------------- 1 file changed, 101 insertions(+), 98 deletions(-) diff --git a/tests/datastore/testthreadproxyds.nim b/tests/datastore/testthreadproxyds.nim index 8a20fd4b..021e23f6 100644 --- a/tests/datastore/testthreadproxyds.nim +++ b/tests/datastore/testthreadproxyds.nim @@ -139,109 +139,112 @@ suite "Test Query ThreadDatastore with fsds": queryTests(ds, false) -suite "Test ThreadDatastore cancelations": - var - sqlStore: SQLiteBackend[KeyId,DataBuffer] - sds: ThreadDatastore[SQLiteBackend[KeyId, DataBuffer]] - - privateAccess(ThreadDatastore) # expose private fields - privateAccess(TaskCtx) # expose private fields - - setupAll: - sqlStore = newSQLiteBackend[KeyId, DataBuffer](Memory).tryGet() - sds = ThreadDatastore.new(sqlStore, tp = taskPool).tryGet() - - teardown: - GC_fullCollect() # run full collect after each test - - test "Should monitor signal and cancel": +for i in 1..N: + suite "Test ThreadDatastore cancelations": var - signal = ThreadSignalPtr.new().tryGet() + sqlStore: SQLiteBackend[KeyId,DataBuffer] + sds: ThreadDatastore[SQLiteBackend[KeyId, DataBuffer]] - proc cancelTestTask(ctx: TaskCtx[bool]) {.gcsafe.} = - executeTask(ctx): - (?!bool).ok(true) + privateAccess(ThreadDatastore) # expose private fields + privateAccess(TaskCtx) # expose private fields - let ctx = newTaskCtx(bool, signal=signal) - ctx[].cancelled = true - dispatchTask(sds, signal): - sds.tp.spawn cancelTestTask(ctx) + setupAll: + sqlStore = newSQLiteBackend[KeyId, DataBuffer](Memory).tryGet() + sds = ThreadDatastore.new(sqlStore, tp = taskPool).tryGet() - check: - ctx[].res.isErr == true - ctx[].cancelled == true - ctx[].running == false + teardown: + GC_fullCollect() # run full collect after each test - test "Should cancel future": + test "Should monitor signal and cancel": + var + signal = ThreadSignalPtr.new().tryGet() - var - signal = ThreadSignalPtr.new().tryGet() - ms {.global.}: MutexSignal - flag {.global.}: Atomic[bool] - futFreed {.global.}: Atomic[bool] - ready {.global.}: Atomic[bool] - - ms.init() - - type - FutTestObj = object - TestValue = object - ThreadTestInt = (TestValue, ) - - proc `=destroy`(obj: var TestValue) = - echo "destroy TestObj!" - flag.store(true) - - proc `=destroy`(obj: var FutTestObj) = - echo "destroy FutTestObj!" - futFreed.store(true) - - proc wait(flag: var Atomic[bool]) = - echo "wait for task to be ready..." - defer: echo "" - for i in 1..100: - stdout.write(".") - if flag.load() == true: - return - os.sleep(10) - raise newException(Defect, "timeout") - - proc errorTestTask(ctx: TaskCtx[ThreadTestInt]) {.gcsafe, nimcall.} = - executeTask(ctx): - echo "task:exec" - discard ctx[].signal.fireSync() - ready.store(true) - ms.wait() - echo "ctx:task: ", ctx[] - (?!ThreadTestInt).ok(default(ThreadTestInt)) - - proc runTestTask() {.async.} = - - let obj = FutTestObj() - await sleepAsync(1.milliseconds) - defer: echo "fut FutTestObj: ", obj - - let ctx = newTaskCtx(ThreadTestInt, signal=signal) - dispatchTask(sds, signal): - sds.tp.spawn errorTestTask(ctx) - ready.wait() - echo "raise error" - raise newException(ValueError, "fake error") - - try: - block: - await runTestTask() - except CatchableError as exc: - echo "caught: ", $exc - finally: - echo "finish" - check ready.load() == true - GC_fullCollect() - futFreed.wait() - echo "future freed it's mem!" - check futFreed.load() == true + proc cancelTestTask(ctx: TaskCtx[bool]) {.gcsafe.} = + executeTask(ctx): + (?!bool).ok(true) - ms.fire() - flag.wait() - check flag.load() == true + let ctx = newTaskCtx(bool, signal=signal) + ctx[].cancelled = true + dispatchTask(sds, signal): + sds.tp.spawn cancelTestTask(ctx) + + check: + ctx[].res.isErr == true + ctx[].cancelled == true + ctx[].running == false + + test "Should cancel future": + + var + signal = ThreadSignalPtr.new().tryGet() + ms {.global.}: MutexSignal + flag {.global.}: Atomic[bool] + futFreed {.global.}: Atomic[bool] + ready {.global.}: Atomic[bool] + + ms.init() + + type + FutTestObj = object + val: int + TestValue = object + ThreadTestInt = (TestValue, ) + + proc `=destroy`(obj: var TestValue) = + # echo "destroy TestObj!" + flag.store(true) + + proc `=destroy`(obj: var FutTestObj) = + # echo "destroy FutTestObj!" + futFreed.store(true) + + proc wait(flag: var Atomic[bool], name = "task") = + # echo "wait for " & name & " to be ready..." + defer: echo "" + for i in 1..100: + # stdout.write(".") + if flag.load() == true: + return + os.sleep(10) + raise newException(Defect, "timeout") + + proc errorTestTask(ctx: TaskCtx[ThreadTestInt]) {.gcsafe, nimcall.} = + executeTask(ctx): + # echo "task:exec" + discard ctx[].signal.fireSync() + ready.store(true) + ms.wait() + # echo "ctx:task: ", ctx[] + (?!ThreadTestInt).ok(default(ThreadTestInt)) + + proc runTestTask() {.async.} = + let obj = FutTestObj(val: 42) + await sleepAsync(1.milliseconds) + try: + let ctx = newTaskCtx(ThreadTestInt, signal=signal) + dispatchTask(sds, signal): + sds.tp.spawn errorTestTask(ctx) + ready.wait() + # echo "raise error" + raise newException(ValueError, "fake error") + finally: + # echo "fut FutTestObj: ", obj + assert obj.val == 42 # need to force future to keep ref here + try: + block: + await runTestTask() + except CatchableError as exc: + # echo "caught: ", $exc + discard + finally: + # echo "finish" + check ready.load() == true + GC_fullCollect() + futFreed.wait("futFreed") + echo "future freed it's mem!" + check futFreed.load() == true + + ms.fire() + flag.wait("flag") + check flag.load() == true From 374e5a891f3625c03024e2749b27643cde46486d Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Thu, 28 Sep 2023 15:07:40 -0700 Subject: [PATCH 74/74] test task cancel --- tests/datastore/testthreadproxyds.nim | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/datastore/testthreadproxyds.nim b/tests/datastore/testthreadproxyds.nim index 021e23f6..db20c819 100644 --- a/tests/datastore/testthreadproxyds.nim +++ b/tests/datastore/testthreadproxyds.nim @@ -200,7 +200,7 @@ for i in 1..N: proc wait(flag: var Atomic[bool], name = "task") = # echo "wait for " & name & " to be ready..." - defer: echo "" + # defer: echo "" for i in 1..100: # stdout.write(".") if flag.load() == true: @@ -214,7 +214,7 @@ for i in 1..N: discard ctx[].signal.fireSync() ready.store(true) ms.wait() - # echo "ctx:task: ", ctx[] + echo "task context memory: ", ctx[] (?!ThreadTestInt).ok(default(ThreadTestInt)) proc runTestTask() {.async.} = @@ -247,4 +247,3 @@ for i in 1..N: ms.fire() flag.wait("flag") check flag.load() == true -