@bjornpagen/bumbledb 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COOKBOOK.md +64 -27
- package/README.md +21 -20
- package/dist/changes.d.ts +2 -3
- package/dist/changes.d.ts.map +1 -1
- package/dist/changes.js +35 -32
- package/dist/changes.js.map +1 -1
- package/dist/codec.d.ts +4 -5
- package/dist/codec.d.ts.map +1 -1
- package/dist/codec.js +6 -6
- package/dist/codec.js.map +1 -1
- package/dist/compile.d.ts +1 -2
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +5 -5
- package/dist/compile.js.map +1 -1
- package/dist/db-native.d.ts +37 -41
- package/dist/db-native.d.ts.map +1 -1
- package/dist/db-native.js.map +1 -1
- package/dist/db.d.ts +37 -22
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +71 -57
- package/dist/db.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/log.d.ts +4 -4
- package/dist/internal/log.d.ts.map +1 -1
- package/dist/internal/log.js +1 -1
- package/dist/internal/log.js.map +1 -1
- package/dist/law.d.ts +1 -1
- package/dist/migration.d.ts +2 -3
- package/dist/migration.d.ts.map +1 -1
- package/dist/migration.js +5 -5
- package/dist/migration.js.map +1 -1
- package/dist/native.d.ts +1 -1
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js.map +1 -1
- package/dist/result.d.ts +10 -25
- package/dist/result.d.ts.map +1 -1
- package/dist/result.js +7 -12
- package/dist/result.js.map +1 -1
- package/dist/rows.d.ts +5 -11
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +6 -40
- package/dist/rows.js.map +1 -1
- package/dist/runtime-codes.d.ts +1 -1
- package/dist/runtime-codes.d.ts.map +1 -1
- package/dist/runtime-codes.js +1 -2
- package/dist/runtime-codes.js.map +1 -1
- package/dist/runtime-errors.d.ts +3 -15
- package/dist/runtime-errors.d.ts.map +1 -1
- package/dist/runtime-errors.js +1 -5
- package/dist/runtime-errors.js.map +1 -1
- package/dist/runtime-native.d.ts +16 -34
- package/dist/runtime-native.d.ts.map +1 -1
- package/dist/runtime-native.js.map +1 -1
- package/dist/runtime.d.ts +11 -28
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +14 -54
- package/dist/runtime.js.map +1 -1
- package/pack-provenance.json +3 -3
- package/package.json +4 -4
- package/src/changes.ts +46 -44
- package/src/codec.ts +7 -23
- package/src/compile.ts +5 -6
- package/src/db-native.ts +31 -68
- package/src/db.ts +148 -127
- package/src/index.ts +5 -4
- package/src/internal/log.ts +2 -5
- package/src/law.ts +1 -1
- package/src/migration.ts +5 -12
- package/src/native.ts +0 -1
- package/src/result.ts +16 -45
- package/src/rows.ts +6 -56
- package/src/runtime-codes.ts +1 -2
- package/src/runtime-errors.ts +1 -5
- package/src/runtime-native.ts +13 -46
- package/src/runtime.ts +51 -119
package/src/result.ts
CHANGED
|
@@ -5,31 +5,22 @@ import { dbNative } from "#db-native.ts"
|
|
|
5
5
|
import type { FindColumn } from "#query/atom.ts"
|
|
6
6
|
import { decodeAnswers } from "#query/run.ts"
|
|
7
7
|
import type { CellValue } from "#rows.ts"
|
|
8
|
-
import
|
|
9
|
-
import { deliveryResultBytes, nativeOperationWith, policyWire } from "#runtime.ts"
|
|
8
|
+
import { nativeOperationWith } from "#runtime.ts"
|
|
10
9
|
import type { CloseReport, DbError } from "#runtime-errors.ts"
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
12
|
+
* One complete answer set, sealed after evaluation and independent of its
|
|
13
|
+
* source snapshot. collect() explicitly materializes all rows into JS and
|
|
14
|
+
* leaves the result available. pages() consumes it once through bounded
|
|
15
|
+
* delivery batches; it does not stream query execution.
|
|
17
16
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* `pages`. `pages` is a one-shot consuming stream over the
|
|
22
|
-
* completed result: its first execution atomically spends the result and
|
|
23
|
-
* moves the backing storage into a private cursor owned by the stream's
|
|
24
|
-
* scope — construction alone spends nothing, a second run fails
|
|
25
|
-
* `SpentHandle`, and a run after the result's scope closed fails
|
|
26
|
-
* `ClosedHandle`. Early take, downstream failure and interruption all
|
|
27
|
-
* close/drain the private cursor; EOF cleanup is identical. There is no
|
|
28
|
-
* public cursor, `next`, AsyncIterable, clone or second streaming API.
|
|
17
|
+
* The stream owns its cursor through Effect scope. Early termination,
|
|
18
|
+
* failure, interruption and EOF close/drain it. Creating the stream does
|
|
19
|
+
* not spend the result; running it twice fails with SpentHandle.
|
|
29
20
|
*/
|
|
30
21
|
interface CompleteResult<A> {
|
|
31
|
-
collect(
|
|
32
|
-
pages(
|
|
22
|
+
collect(): Effect.Effect<ReadonlyArray<A>, DbError>
|
|
23
|
+
pages(): Stream.Stream<ReadonlyArray<A>, DbError>
|
|
33
24
|
close(): Effect.Effect<CloseReport>
|
|
34
25
|
}
|
|
35
26
|
|
|
@@ -52,42 +43,27 @@ function decodePage<A>(finds: readonly FindColumn[], rows: readonly (readonly Ce
|
|
|
52
43
|
|
|
53
44
|
/**
|
|
54
45
|
* Internal constructor: `db.ts` publishes results through this after
|
|
55
|
-
* execution completes.
|
|
56
|
-
* operation under the caller's delivery policy — never the completed query's
|
|
57
|
-
* expired execution deadline.
|
|
46
|
+
* execution completes. Each delivery has independent cooperative cancellation.
|
|
58
47
|
*/
|
|
59
48
|
function makeCompleteResult<A>(handle: ResultHandle, finds: readonly FindColumn[]): CompleteResult<A> {
|
|
60
|
-
function deliveryWire(operation: string, delivery: ExecutionPolicy, requested: bigint) {
|
|
61
|
-
return policyWire({ ...delivery, resultBytes: deliveryResultBytes(requested, delivery) }, operation)
|
|
62
|
-
}
|
|
63
49
|
const value: CompleteResult<A> = {
|
|
64
|
-
collect(
|
|
50
|
+
collect() {
|
|
65
51
|
return Effect.suspend(() =>
|
|
66
52
|
nativeOperationWith(
|
|
67
53
|
"CompleteResult.collect",
|
|
68
|
-
(callback) =>
|
|
69
|
-
dbNative.runtimeResultCollect(
|
|
70
|
-
handle,
|
|
71
|
-
deliveryWire("CompleteResult.collect", work, options.maxBytes),
|
|
72
|
-
callback
|
|
73
|
-
),
|
|
54
|
+
(callback) => dbNative.runtimeResultCollect(handle, callback),
|
|
74
55
|
dbNative.runtimeRowsTake,
|
|
75
56
|
(rows) => decodePage<A>(finds, rows)
|
|
76
57
|
)
|
|
77
58
|
)
|
|
78
59
|
},
|
|
79
|
-
pages(
|
|
60
|
+
pages() {
|
|
80
61
|
return Stream.unwrap(
|
|
81
62
|
Effect.gen(function* () {
|
|
82
63
|
const cursor: CursorHandle = yield* Effect.acquireRelease(
|
|
83
64
|
nativeOperationWith(
|
|
84
65
|
"CompleteResult.pages",
|
|
85
|
-
(callback) =>
|
|
86
|
-
dbNative.runtimeResultCursor(
|
|
87
|
-
handle,
|
|
88
|
-
deliveryWire("CompleteResult.pages", work, options.pageBytes),
|
|
89
|
-
callback
|
|
90
|
-
),
|
|
66
|
+
(callback) => dbNative.runtimeResultCursor(handle, callback),
|
|
91
67
|
dbNative.runtimeCursorTake,
|
|
92
68
|
(taken) => taken
|
|
93
69
|
),
|
|
@@ -97,12 +73,7 @@ function makeCompleteResult<A>(handle: ResultHandle, finds: readonly FindColumn[
|
|
|
97
73
|
return Stream.paginate(undefined, () =>
|
|
98
74
|
nativeOperationWith(
|
|
99
75
|
"CompleteResult.page",
|
|
100
|
-
(callback) =>
|
|
101
|
-
dbNative.runtimeCursorNext(
|
|
102
|
-
cursor,
|
|
103
|
-
deliveryWire("CompleteResult.page", work, options.pageBytes),
|
|
104
|
-
callback
|
|
105
|
-
),
|
|
76
|
+
(callback) => dbNative.runtimeCursorNext(cursor, callback),
|
|
106
77
|
dbNative.runtimePageTake,
|
|
107
78
|
(page) => page
|
|
108
79
|
).pipe(
|
package/src/rows.ts
CHANGED
|
@@ -21,9 +21,6 @@ import { isFloatIntervalValue, isIntervalValue, literalShapeError, rosterOf } fr
|
|
|
21
21
|
import type { AnyRelation, Fact, RelationData } from "#relation.ts"
|
|
22
22
|
import { Uuid } from "#uuid.ts"
|
|
23
23
|
|
|
24
|
-
/** Converter-granularity host cell wall (64 KiB). One oversize cell refuses. */
|
|
25
|
-
const HOST_CELL_MAX = 65536n
|
|
26
|
-
|
|
27
24
|
/**
|
|
28
25
|
* One owned cell at the private bridge boundary. The declared sealed field
|
|
29
26
|
* type disambiguates the union: `string` is text, an `uuid` in its
|
|
@@ -44,8 +41,6 @@ type CellValue =
|
|
|
44
41
|
interface FlatRows {
|
|
45
42
|
readonly rows: bigint
|
|
46
43
|
readonly cells: readonly CellValue[]
|
|
47
|
-
/** Conservative charged byte size of the owned cells (input accounting). */
|
|
48
|
-
readonly bytes: bigint
|
|
49
44
|
}
|
|
50
45
|
|
|
51
46
|
function recordOf(fact: object): Readonly<Record<string, unknown>> {
|
|
@@ -64,9 +59,8 @@ function refuseShared(context: string, value: Uint8Array): void {
|
|
|
64
59
|
}
|
|
65
60
|
|
|
66
61
|
/**
|
|
67
|
-
*
|
|
68
|
-
* two bytes per UTF-16 code unit, not their UTF-8 wire size.
|
|
69
|
-
* measures and bounds UTF-8 separately; byte views charge their length.
|
|
62
|
+
* Approximate payload size for host batching, not a memory allowance.
|
|
63
|
+
* Strings use two bytes per UTF-16 code unit, not their UTF-8 wire size.
|
|
70
64
|
*/
|
|
71
65
|
function hostCellCharge(value: unknown): bigint {
|
|
72
66
|
if (typeof value === "string") {
|
|
@@ -87,16 +81,6 @@ function hostCellCharge(value: unknown): bigint {
|
|
|
87
81
|
return 0n
|
|
88
82
|
}
|
|
89
83
|
|
|
90
|
-
function assertHostCellFits(context: string, value: unknown, limit: bigint): bigint {
|
|
91
|
-
const charge = hostCellCharge(value)
|
|
92
|
-
if (charge > limit) {
|
|
93
|
-
throw new AuthoringError({
|
|
94
|
-
message: `${context}: host cell of ${charge} bytes exceeds the ${limit}-byte converter bound — refuse before copy or scan`
|
|
95
|
-
})
|
|
96
|
-
}
|
|
97
|
-
return charge
|
|
98
|
-
}
|
|
99
|
-
|
|
100
84
|
/**
|
|
101
85
|
* The write half of the closed bijection: one handle NAME to its u64 row
|
|
102
86
|
* id (declaration order = row ids). An unknown name is a pointed refusal
|
|
@@ -125,25 +109,6 @@ function handleOf(context: string, closed: AnyClosedRoster, cell: unknown): stri
|
|
|
125
109
|
return handle
|
|
126
110
|
}
|
|
127
111
|
|
|
128
|
-
/** Conservative owned-byte charge of one cell (host accounting, not wire). */
|
|
129
|
-
function cellBytes(cell: CellValue): bigint {
|
|
130
|
-
if (typeof cell === "boolean") {
|
|
131
|
-
return 1n
|
|
132
|
-
}
|
|
133
|
-
if (typeof cell === "bigint" || typeof cell === "number") {
|
|
134
|
-
return 8n
|
|
135
|
-
}
|
|
136
|
-
if (typeof cell === "string") {
|
|
137
|
-
// Account for the host string here; native admission separately
|
|
138
|
-
// checks and charges its exact UTF-8 encoding.
|
|
139
|
-
return BigInt(cell.length) * 2n
|
|
140
|
-
}
|
|
141
|
-
if (cell instanceof Uint8Array) {
|
|
142
|
-
return BigInt(cell.byteLength)
|
|
143
|
-
}
|
|
144
|
-
return 16n
|
|
145
|
-
}
|
|
146
|
-
|
|
147
112
|
function cellOf(context: string, field: AnyField, value: unknown): CellValue {
|
|
148
113
|
const roster = rosterOf(field)
|
|
149
114
|
if (roster !== undefined) {
|
|
@@ -170,7 +135,6 @@ function cellOf(context: string, field: AnyField, value: unknown): CellValue {
|
|
|
170
135
|
if (typeof value !== "string") {
|
|
171
136
|
throw literalShapeError(context, "string", value)
|
|
172
137
|
}
|
|
173
|
-
assertHostCellFits(context, value, HOST_CELL_MAX)
|
|
174
138
|
if (!value.isWellFormed()) {
|
|
175
139
|
throw literalShapeError(context, "well-formed string", value)
|
|
176
140
|
}
|
|
@@ -194,7 +158,6 @@ function cellOf(context: string, field: AnyField, value: unknown): CellValue {
|
|
|
194
158
|
throw literalShapeError(context, "Uint8Array", value)
|
|
195
159
|
}
|
|
196
160
|
refuseShared(context, value)
|
|
197
|
-
assertHostCellFits(context, value, HOST_CELL_MAX)
|
|
198
161
|
if (value.byteLength !== field.width) {
|
|
199
162
|
throw new AuthoringError({
|
|
200
163
|
message: `${context}: bytes<${field.width}> takes exactly ${field.width} bytes (got ${value.byteLength})`
|
|
@@ -231,14 +194,13 @@ function cellOf(context: string, field: AnyField, value: unknown): CellValue {
|
|
|
231
194
|
|
|
232
195
|
/**
|
|
233
196
|
* The flat projector: every fact's cells land in ONE row-major cell array
|
|
234
|
-
* (length rows × arity)
|
|
235
|
-
*
|
|
197
|
+
* (length rows × arity), counting rows while projecting.
|
|
198
|
+
* Missing-field refusal and per-cell judgment are
|
|
236
199
|
* {@link cellOf}'s, byte for byte.
|
|
237
200
|
*/
|
|
238
201
|
function flatRowsOf(data: RelationData, facts: Iterable<object>): FlatRows {
|
|
239
202
|
const cells: CellValue[] = []
|
|
240
203
|
let rows = 0n
|
|
241
|
-
let bytes = 0n
|
|
242
204
|
for (const fact of facts) {
|
|
243
205
|
rows += 1n
|
|
244
206
|
const record = recordOf(fact)
|
|
@@ -248,11 +210,10 @@ function flatRowsOf(data: RelationData, facts: Iterable<object>): FlatRows {
|
|
|
248
210
|
throw new AuthoringError({ message: `relation ${data.name}: fact is missing field ${declared.name}` })
|
|
249
211
|
}
|
|
250
212
|
const cell = cellOf(`relation ${data.name} field ${declared.name}`, declared.field, value)
|
|
251
|
-
bytes += cellBytes(cell)
|
|
252
213
|
cells.push(cell)
|
|
253
214
|
}
|
|
254
215
|
}
|
|
255
|
-
return { rows, cells
|
|
216
|
+
return { rows, cells }
|
|
256
217
|
}
|
|
257
218
|
|
|
258
219
|
function keyCellsOf(
|
|
@@ -387,15 +348,4 @@ function factOfCells<R extends AnyRelation>(relation: R, row: readonly unknown[]
|
|
|
387
348
|
}
|
|
388
349
|
|
|
389
350
|
export type { CellValue, FlatRows }
|
|
390
|
-
export {
|
|
391
|
-
assertHostCellFits,
|
|
392
|
-
cellBytes,
|
|
393
|
-
cellOf,
|
|
394
|
-
decodeCell,
|
|
395
|
-
factOfCells,
|
|
396
|
-
flatRowsOf,
|
|
397
|
-
handleOf,
|
|
398
|
-
hostCellCharge,
|
|
399
|
-
keyCellsOf,
|
|
400
|
-
recordOf
|
|
401
|
-
}
|
|
351
|
+
export { cellOf, decodeCell, factOfCells, flatRowsOf, handleOf, hostCellCharge, keyCellsOf, recordOf }
|
package/src/runtime-codes.ts
CHANGED
package/src/runtime-errors.ts
CHANGED
|
@@ -49,11 +49,7 @@ const Outstanding = Schema.Struct({
|
|
|
49
49
|
retained: Schema.BigInt,
|
|
50
50
|
owners: Schema.BigInt,
|
|
51
51
|
databases: Schema.BigInt,
|
|
52
|
-
natives: Schema.BigInt
|
|
53
|
-
inputBytes: Schema.BigInt,
|
|
54
|
-
workingBytes: Schema.BigInt,
|
|
55
|
-
scratchBytes: Schema.BigInt,
|
|
56
|
-
resultBytes: Schema.BigInt
|
|
52
|
+
natives: Schema.BigInt
|
|
57
53
|
})
|
|
58
54
|
export type OutstandingWork = typeof Outstanding.Type
|
|
59
55
|
const Close = Schema.Union([
|
package/src/runtime-native.ts
CHANGED
|
@@ -34,7 +34,7 @@ export interface RepositoryLockHandle {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
/** Resource kinds installed in native worker tables. */
|
|
37
|
-
export type NativeKind = "snapshot" | "result" | "cursor" | "draft" | "changes" | "repository-lock"
|
|
37
|
+
export type NativeKind = "snapshot" | "prepared" | "result" | "cursor" | "draft" | "changes" | "repository-lock"
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
40
|
* Checked capability into the native registry. JS tokens validate
|
|
@@ -96,28 +96,13 @@ export type LogTakeWire =
|
|
|
96
96
|
readonly digest: Uint8Array
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
export interface PolicyWire {
|
|
100
|
-
readonly inputBytes: bigint
|
|
101
|
-
readonly workingBytes: bigint
|
|
102
|
-
readonly scratchBytes: bigint
|
|
103
|
-
readonly resultBytes: bigint
|
|
104
|
-
readonly rows: bigint
|
|
105
|
-
readonly workUnits: bigint
|
|
106
|
-
readonly timeoutMs: number
|
|
107
|
-
}
|
|
108
|
-
|
|
109
99
|
export interface OptionsWire {
|
|
110
|
-
readonly workers
|
|
111
|
-
readonly queueCapacity
|
|
112
|
-
readonly cleanupCapacity
|
|
113
|
-
readonly ownerCapacity
|
|
114
|
-
readonly nativeHandleCapacity
|
|
115
|
-
readonly
|
|
116
|
-
readonly workingBytes: bigint
|
|
117
|
-
readonly scratchBytes: bigint
|
|
118
|
-
readonly resultBytes: bigint
|
|
119
|
-
readonly chunkBytes: bigint
|
|
120
|
-
readonly cleanupTimeoutMs: number
|
|
100
|
+
readonly workers?: number | undefined
|
|
101
|
+
readonly queueCapacity?: number | undefined
|
|
102
|
+
readonly cleanupCapacity?: number | undefined
|
|
103
|
+
readonly ownerCapacity?: number | undefined
|
|
104
|
+
readonly nativeHandleCapacity?: number | undefined
|
|
105
|
+
readonly cleanupTimeoutMs?: number | undefined
|
|
121
106
|
}
|
|
122
107
|
|
|
123
108
|
export interface InspectionWire {
|
|
@@ -128,10 +113,6 @@ export interface InspectionWire {
|
|
|
128
113
|
readonly owners: bigint
|
|
129
114
|
readonly databases: bigint
|
|
130
115
|
readonly natives: bigint
|
|
131
|
-
readonly inputBytes: bigint
|
|
132
|
-
readonly workingBytes: bigint
|
|
133
|
-
readonly scratchBytes: bigint
|
|
134
|
-
readonly resultBytes: bigint
|
|
135
116
|
}
|
|
136
117
|
|
|
137
118
|
export type CloseWire =
|
|
@@ -142,8 +123,8 @@ export type CloseWire =
|
|
|
142
123
|
interface RuntimeNative {
|
|
143
124
|
runtimeErrorCodes(): readonly string[]
|
|
144
125
|
runtimeOpen(options: OptionsWire): RuntimeHandle
|
|
145
|
-
runtimeReady(runtime: RuntimeHandle,
|
|
146
|
-
runtimeHash(runtime: RuntimeHandle,
|
|
126
|
+
runtimeReady(runtime: RuntimeHandle, callback: () => void): OperationHandle
|
|
127
|
+
runtimeHash(runtime: RuntimeHandle, bytes: Uint8Array, callback: () => void): OperationHandle
|
|
147
128
|
runtimeTake(operation: OperationHandle): Uint8Array | null
|
|
148
129
|
runtimeCancel(operation: OperationHandle, callback: (report: CloseWire) => void): void
|
|
149
130
|
runtimeClose(runtime: RuntimeHandle, callback: (report: CloseWire) => void): void
|
|
@@ -156,20 +137,14 @@ interface RuntimeNative {
|
|
|
156
137
|
* Predelivery: no JS take; cursor stays on row1 so retry does not skip.
|
|
157
138
|
*/
|
|
158
139
|
runtimeArmPublicationCancel(runtime: RuntimeHandle): void
|
|
159
|
-
runtimeDirectoryAcquire(
|
|
160
|
-
runtime: RuntimeHandle,
|
|
161
|
-
policy: PolicyWire,
|
|
162
|
-
path: string,
|
|
163
|
-
callback: () => void
|
|
164
|
-
): OperationHandle
|
|
140
|
+
runtimeDirectoryAcquire(runtime: RuntimeHandle, path: string, callback: () => void): OperationHandle
|
|
165
141
|
runtimeDirectoryTake(operation: OperationHandle): DirectoryHandle
|
|
166
|
-
runtimeDirectoryBegin(owner: DirectoryHandle
|
|
142
|
+
runtimeDirectoryBegin(owner: DirectoryHandle): OperationHandle
|
|
167
143
|
runtimeDirectoryCheck(operation: OperationHandle): void
|
|
168
144
|
runtimeDirectoryEnd(operation: OperationHandle): void
|
|
169
145
|
runtimeDirectoryClose(owner: DirectoryHandle, remove: boolean, callback: (report: CloseWire) => void): void
|
|
170
146
|
runtimeDirectoryDbOpen(
|
|
171
147
|
owner: DirectoryHandle,
|
|
172
|
-
policy: PolicyWire,
|
|
173
148
|
childName: string,
|
|
174
149
|
spec: SchemaSpec,
|
|
175
150
|
create: boolean,
|
|
@@ -178,12 +153,11 @@ interface RuntimeNative {
|
|
|
178
153
|
runtimeDbTake(operation: OperationHandle): ManagedDbOutcome
|
|
179
154
|
runtimeManagedDbClose(db: DbHandle, callback: (report: CloseWire) => void): void
|
|
180
155
|
|
|
181
|
-
// --- successor log grammar on the executor (
|
|
156
|
+
// --- successor log grammar on the executor (hashing over
|
|
182
157
|
// whole canonical change payloads; C06 through C09) ---
|
|
183
158
|
runtimeLogCommandSeal(
|
|
184
159
|
runtime: RuntimeHandle,
|
|
185
160
|
schema: LogSchemaHandle,
|
|
186
|
-
policy: PolicyWire,
|
|
187
161
|
metadata: LogCommandMetadata,
|
|
188
162
|
changes: Uint8Array,
|
|
189
163
|
result: Uint8Array | null,
|
|
@@ -193,14 +167,12 @@ interface RuntimeNative {
|
|
|
193
167
|
runtimeLogCommandParse(
|
|
194
168
|
runtime: RuntimeHandle,
|
|
195
169
|
schema: LogSchemaHandle,
|
|
196
|
-
policy: PolicyWire,
|
|
197
170
|
bytes: Uint8Array,
|
|
198
171
|
limits: LogLimits,
|
|
199
172
|
callback: () => void
|
|
200
173
|
): OperationHandle
|
|
201
174
|
runtimeLogDecisionDecode(
|
|
202
175
|
runtime: RuntimeHandle,
|
|
203
|
-
policy: PolicyWire,
|
|
204
176
|
bytes: Uint8Array,
|
|
205
177
|
parent: LogDecisionStamp | null,
|
|
206
178
|
limits: LogLimits,
|
|
@@ -208,12 +180,7 @@ interface RuntimeNative {
|
|
|
208
180
|
): OperationHandle
|
|
209
181
|
runtimeLogTake(operation: OperationHandle): LogTakeWire
|
|
210
182
|
/** L14 mint: `Runtime::mint_repository_lock` stamps `NativeKind::RepositoryLock` at take. */
|
|
211
|
-
logRepositoryLockAcquire(
|
|
212
|
-
runtime: RuntimeHandle,
|
|
213
|
-
policy: PolicyWire,
|
|
214
|
-
directory: string,
|
|
215
|
-
callback: () => void
|
|
216
|
-
): OperationHandle
|
|
183
|
+
logRepositoryLockAcquire(runtime: RuntimeHandle, directory: string, callback: () => void): OperationHandle
|
|
217
184
|
logRepositoryLockTake(operation: OperationHandle): RepositoryLockHandle
|
|
218
185
|
logRepositoryLockRelease(owner: RepositoryLockHandle, callback: (report: CloseWire) => void): void
|
|
219
186
|
}
|
package/src/runtime.ts
CHANGED
|
@@ -1,43 +1,21 @@
|
|
|
1
1
|
import { Context, Duration, Effect, Exit, Layer } from "effect"
|
|
2
2
|
import type { CloseReport, OutstandingWork } from "#runtime-errors.ts"
|
|
3
3
|
import { CloseFailure, DbError, dbError } from "#runtime-errors.ts"
|
|
4
|
-
import type {
|
|
5
|
-
CloseWire,
|
|
6
|
-
OperationHandle,
|
|
7
|
-
OptionsWire,
|
|
8
|
-
PolicyWire,
|
|
9
|
-
RepositoryLockHandle,
|
|
10
|
-
RuntimeHandle
|
|
11
|
-
} from "#runtime-native.ts"
|
|
4
|
+
import type { CloseWire, OperationHandle, OptionsWire, RepositoryLockHandle, RuntimeHandle } from "#runtime-native.ts"
|
|
12
5
|
import { runtimeNative } from "#runtime-native.ts"
|
|
13
6
|
|
|
14
|
-
export interface ExecutionPolicy {
|
|
15
|
-
readonly inputBytes: bigint
|
|
16
|
-
readonly workingBytes: bigint
|
|
17
|
-
readonly scratchBytes: bigint
|
|
18
|
-
readonly resultBytes: bigint
|
|
19
|
-
readonly rows: bigint
|
|
20
|
-
readonly workUnits: bigint
|
|
21
|
-
readonly timeout: Duration.Input
|
|
22
|
-
}
|
|
23
|
-
|
|
24
7
|
export interface NativeRuntimeOptions {
|
|
25
|
-
readonly workers
|
|
26
|
-
readonly queueCapacity
|
|
27
|
-
readonly cleanupCapacity
|
|
28
|
-
readonly ownerCapacity
|
|
29
|
-
readonly nativeHandleCapacity
|
|
30
|
-
readonly
|
|
31
|
-
readonly workingBytes: bigint
|
|
32
|
-
readonly scratchBytes: bigint
|
|
33
|
-
readonly resultBytes: bigint
|
|
34
|
-
readonly chunkBytes: bigint
|
|
35
|
-
readonly cleanupTimeout: Duration.Input
|
|
8
|
+
readonly workers?: number
|
|
9
|
+
readonly queueCapacity?: number
|
|
10
|
+
readonly cleanupCapacity?: number
|
|
11
|
+
readonly ownerCapacity?: number
|
|
12
|
+
readonly nativeHandleCapacity?: number
|
|
13
|
+
readonly cleanupTimeout?: Duration.Input
|
|
36
14
|
}
|
|
37
15
|
|
|
38
16
|
interface RuntimeService {
|
|
39
17
|
readonly close: () => Effect.Effect<CloseReport>
|
|
40
|
-
readonly inspect: (
|
|
18
|
+
readonly inspect: () => Effect.Effect<OutstandingWork, DbError>
|
|
41
19
|
}
|
|
42
20
|
|
|
43
21
|
const owners = new WeakMap<RuntimeService, RuntimeHandle>()
|
|
@@ -51,43 +29,23 @@ function count(value: number, operation: string): number {
|
|
|
51
29
|
return value
|
|
52
30
|
}
|
|
53
31
|
|
|
54
|
-
function bytes(value: bigint, operation: string): bigint {
|
|
55
|
-
if (typeof value !== "bigint" || value < 0n || value > 0xffffffffffffffffn) throw invalid(operation)
|
|
56
|
-
return value
|
|
57
|
-
}
|
|
58
|
-
|
|
59
32
|
function millis(value: Duration.Input, operation: string): number {
|
|
60
33
|
const duration = Duration.toMillis(value)
|
|
61
34
|
if (!Number.isFinite(duration) || duration < 0 || duration > 0xffffffff) throw invalid(operation)
|
|
62
35
|
return Math.ceil(duration)
|
|
63
36
|
}
|
|
64
37
|
|
|
65
|
-
export function policyWire(work: ExecutionPolicy, operation: string): PolicyWire {
|
|
66
|
-
return {
|
|
67
|
-
inputBytes: bytes(work.inputBytes, operation),
|
|
68
|
-
workingBytes: bytes(work.workingBytes, operation),
|
|
69
|
-
scratchBytes: bytes(work.scratchBytes, operation),
|
|
70
|
-
resultBytes: bytes(work.resultBytes, operation),
|
|
71
|
-
rows: bytes(work.rows, operation),
|
|
72
|
-
workUnits: bytes(work.workUnits, operation),
|
|
73
|
-
timeoutMs: millis(work.timeout, operation)
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
38
|
function options(value: NativeRuntimeOptions): OptionsWire {
|
|
78
39
|
const operation = "NativeRuntime.acquire"
|
|
79
40
|
return {
|
|
80
|
-
workers: count(value.workers, operation),
|
|
81
|
-
queueCapacity: count(value.queueCapacity, operation),
|
|
82
|
-
cleanupCapacity: count(value.cleanupCapacity, operation),
|
|
83
|
-
ownerCapacity: count(value.ownerCapacity, operation),
|
|
84
|
-
nativeHandleCapacity:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
resultBytes: bytes(value.resultBytes, operation),
|
|
89
|
-
chunkBytes: bytes(value.chunkBytes, operation),
|
|
90
|
-
cleanupTimeoutMs: count(millis(value.cleanupTimeout, operation), operation)
|
|
41
|
+
workers: value.workers === undefined ? undefined : count(value.workers, operation),
|
|
42
|
+
queueCapacity: value.queueCapacity === undefined ? undefined : count(value.queueCapacity, operation),
|
|
43
|
+
cleanupCapacity: value.cleanupCapacity === undefined ? undefined : count(value.cleanupCapacity, operation),
|
|
44
|
+
ownerCapacity: value.ownerCapacity === undefined ? undefined : count(value.ownerCapacity, operation),
|
|
45
|
+
nativeHandleCapacity:
|
|
46
|
+
value.nativeHandleCapacity === undefined ? undefined : count(value.nativeHandleCapacity, operation),
|
|
47
|
+
cleanupTimeoutMs:
|
|
48
|
+
value.cleanupTimeout === undefined ? undefined : count(millis(value.cleanupTimeout, operation), operation)
|
|
91
49
|
}
|
|
92
50
|
}
|
|
93
51
|
|
|
@@ -169,14 +127,6 @@ function afterClose<A, E, R>(
|
|
|
169
127
|
return effect.pipe(Effect.onExit(() => Effect.die(new CloseFailure({ operation, report }))))
|
|
170
128
|
}
|
|
171
129
|
|
|
172
|
-
/** Independent caps intersect: a delivery request cannot enlarge `work.resultBytes`. */
|
|
173
|
-
export function deliveryResultBytes(requested: bigint, work: ExecutionPolicy): bigint {
|
|
174
|
-
if (typeof requested !== "bigint" || requested < 0n) {
|
|
175
|
-
throw invalid("deliveryResultBytes")
|
|
176
|
-
}
|
|
177
|
-
return requested < work.resultBytes ? requested : work.resultBytes
|
|
178
|
-
}
|
|
179
|
-
|
|
180
130
|
function close(handle: RuntimeHandle): Effect.Effect<CloseReport> {
|
|
181
131
|
return drain("NativeRuntime.close", (callback) => runtimeNative.runtimeClose(handle, callback))
|
|
182
132
|
}
|
|
@@ -257,54 +207,41 @@ const acquire = Effect.fn("NativeRuntime.acquire")(function* (configuration: Nat
|
|
|
257
207
|
const wire = options(configuration)
|
|
258
208
|
handle = runtimeNative.runtimeOpen(wire)
|
|
259
209
|
const owner = handle
|
|
260
|
-
const lease = runtimeNative.runtimeReady(
|
|
261
|
-
|
|
262
|
-
{
|
|
263
|
-
inputBytes: 0n,
|
|
264
|
-
workingBytes: 0n,
|
|
265
|
-
scratchBytes: 0n,
|
|
266
|
-
resultBytes: 0n,
|
|
267
|
-
rows: 0n,
|
|
268
|
-
workUnits: 1n,
|
|
269
|
-
timeoutMs: wire.cleanupTimeoutMs
|
|
270
|
-
},
|
|
271
|
-
() => {
|
|
272
|
-
if (signal.aborted) {
|
|
273
|
-
try {
|
|
274
|
-
runtimeNative.runtimeClose(owner, (report) => {
|
|
275
|
-
stash.report = closeReport("NativeRuntime.acquire.cancel", report)
|
|
276
|
-
})
|
|
277
|
-
} catch (cause) {
|
|
278
|
-
stash.report = { kind: "failed", error: failure("NativeRuntime.acquire.cancel", cause) }
|
|
279
|
-
}
|
|
280
|
-
return
|
|
281
|
-
}
|
|
210
|
+
const lease = runtimeNative.runtimeReady(owner, () => {
|
|
211
|
+
if (signal.aborted) {
|
|
282
212
|
try {
|
|
283
|
-
runtimeNative.
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
inspect: Effect.fn("NativeRuntime.inspect")(function* (work) {
|
|
287
|
-
yield* nativeOperation(
|
|
288
|
-
"NativeRuntime.inspect",
|
|
289
|
-
(callback) =>
|
|
290
|
-
runtimeNative.runtimeReady(owner, policyWire(work, "NativeRuntime.inspect"), callback),
|
|
291
|
-
() => undefined
|
|
292
|
-
)
|
|
293
|
-
return runtimeNative.runtimeInspect(owner)
|
|
294
|
-
})
|
|
295
|
-
}
|
|
296
|
-
owners.set(service, owner)
|
|
297
|
-
resume(Effect.succeed(service))
|
|
213
|
+
runtimeNative.runtimeClose(owner, (report) => {
|
|
214
|
+
stash.report = closeReport("NativeRuntime.acquire.cancel", report)
|
|
215
|
+
})
|
|
298
216
|
} catch (cause) {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
217
|
+
stash.report = { kind: "failed", error: failure("NativeRuntime.acquire.cancel", cause) }
|
|
218
|
+
}
|
|
219
|
+
return
|
|
220
|
+
}
|
|
221
|
+
try {
|
|
222
|
+
runtimeNative.runtimeTake(lease)
|
|
223
|
+
const service: RuntimeService = {
|
|
224
|
+
close: () => close(owner),
|
|
225
|
+
inspect: Effect.fn("NativeRuntime.inspect")(function* () {
|
|
226
|
+
yield* nativeOperation(
|
|
227
|
+
"NativeRuntime.inspect",
|
|
228
|
+
(callback) => runtimeNative.runtimeReady(owner, callback),
|
|
229
|
+
() => undefined
|
|
303
230
|
)
|
|
304
|
-
|
|
231
|
+
return runtimeNative.runtimeInspect(owner)
|
|
232
|
+
})
|
|
305
233
|
}
|
|
234
|
+
owners.set(service, owner)
|
|
235
|
+
resume(Effect.succeed(service))
|
|
236
|
+
} catch (cause) {
|
|
237
|
+
const error = failure("NativeRuntime.acquire", cause)
|
|
238
|
+
resume(
|
|
239
|
+
close(owner).pipe(
|
|
240
|
+
Effect.flatMap((report) => afterClose("NativeRuntime.acquire", report, Effect.fail(error)))
|
|
241
|
+
)
|
|
242
|
+
)
|
|
306
243
|
}
|
|
307
|
-
)
|
|
244
|
+
})
|
|
308
245
|
} catch (cause) {
|
|
309
246
|
const error = failure("NativeRuntime.acquire", cause)
|
|
310
247
|
resume(
|
|
@@ -333,7 +270,7 @@ const acquire = Effect.fn("NativeRuntime.acquire")(function* (configuration: Nat
|
|
|
333
270
|
export class NativeRuntime extends Context.Service<NativeRuntime, RuntimeService>()(
|
|
334
271
|
"@bjornpagen/bumbledb/NativeRuntime"
|
|
335
272
|
) {
|
|
336
|
-
static layer(options: NativeRuntimeOptions): Layer.Layer<NativeRuntime, DbError> {
|
|
273
|
+
static layer(options: NativeRuntimeOptions = {}): Layer.Layer<NativeRuntime, DbError> {
|
|
337
274
|
return Layer.effect(NativeRuntime, acquire(options))
|
|
338
275
|
}
|
|
339
276
|
}
|
|
@@ -374,17 +311,12 @@ function joinLockRelease(operation: string, owner: RepositoryLockHandle): Effect
|
|
|
374
311
|
*/
|
|
375
312
|
export const internalAcquireRepositoryLock = Effect.fn("internalAcquireRepositoryLock")(function* (
|
|
376
313
|
operation: string,
|
|
377
|
-
directory: string
|
|
378
|
-
work: ExecutionPolicy
|
|
314
|
+
directory: string
|
|
379
315
|
) {
|
|
380
316
|
if (directory.length === 0) {
|
|
381
317
|
return yield* Effect.fail(invalid(operation))
|
|
382
318
|
}
|
|
383
319
|
const runtime = yield* runtimeHandle()
|
|
384
|
-
const wire = yield* Effect.try({
|
|
385
|
-
try: () => policyWire(work, operation),
|
|
386
|
-
catch: () => invalid(operation)
|
|
387
|
-
})
|
|
388
320
|
const closeOp = `${operation}.repositoryLock`
|
|
389
321
|
return yield* Effect.uninterruptibleMask((restore) =>
|
|
390
322
|
Effect.gen(function* () {
|
|
@@ -406,7 +338,7 @@ export const internalAcquireRepositoryLock = Effect.fn("internalAcquireRepositor
|
|
|
406
338
|
const owner = yield* restore(
|
|
407
339
|
nativeOperationWith(
|
|
408
340
|
operation,
|
|
409
|
-
(callback) => runtimeNative.logRepositoryLockAcquire(runtime,
|
|
341
|
+
(callback) => runtimeNative.logRepositoryLockAcquire(runtime, directory, callback),
|
|
410
342
|
runtimeNative.logRepositoryLockTake,
|
|
411
343
|
(value) => value
|
|
412
344
|
)
|
|
@@ -421,7 +353,7 @@ export const internalAcquireRepositoryLock = Effect.fn("internalAcquireRepositor
|
|
|
421
353
|
})
|
|
422
354
|
|
|
423
355
|
/** Internal first executor consumer; not a replacement row codec or public hash API. */
|
|
424
|
-
export const hashChunk = Effect.fn("bumbledb.hashChunk")(function* (input: Uint8Array
|
|
356
|
+
export const hashChunk = Effect.fn("bumbledb.hashChunk")(function* (input: Uint8Array) {
|
|
425
357
|
const runtime = yield* NativeRuntime
|
|
426
358
|
const handle = owners.get(runtime)
|
|
427
359
|
if (handle === undefined) return yield* Effect.fail(invalid("hashChunk"))
|
|
@@ -429,7 +361,7 @@ export const hashChunk = Effect.fn("bumbledb.hashChunk")(function* (input: Uint8
|
|
|
429
361
|
"hashChunk",
|
|
430
362
|
(callback) => {
|
|
431
363
|
if (!(input instanceof Uint8Array) || !(input.buffer instanceof ArrayBuffer)) throw invalid("hashChunk")
|
|
432
|
-
return runtimeNative.runtimeHash(handle,
|
|
364
|
+
return runtimeNative.runtimeHash(handle, input, callback)
|
|
433
365
|
},
|
|
434
366
|
(value) => {
|
|
435
367
|
if (value === null) throw dbError("hashChunk", { _tag: "Internal" })
|