@effect-app/infra 4.0.0-beta.209 → 4.0.0-beta.210
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/CHANGELOG.md +55 -0
- package/dist/Model/Repository/internal/internal.d.ts.map +1 -1
- package/dist/Model/Repository/internal/internal.js +28 -21
- package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
- package/dist/QueueMaker/SQLQueue.js +37 -14
- package/dist/QueueMaker/memQueue.d.ts.map +1 -1
- package/dist/QueueMaker/memQueue.js +37 -14
- package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
- package/dist/QueueMaker/sbqueue.js +28 -13
- package/dist/RequestContext.d.ts +6 -6
- package/dist/RequestContext.js +7 -7
- package/dist/Store/Cosmos.d.ts.map +1 -1
- package/dist/Store/Cosmos.js +80 -66
- package/dist/Store/Disk.d.ts.map +1 -1
- package/dist/Store/Disk.js +48 -14
- package/dist/Store/Memory.d.ts.map +1 -1
- package/dist/Store/Memory.js +60 -35
- package/dist/Store/SQL/Pg.d.ts.map +1 -1
- package/dist/Store/SQL/Pg.js +80 -38
- package/dist/Store/SQL.d.ts.map +1 -1
- package/dist/Store/SQL.js +160 -77
- package/dist/adapters/SQL/Model.d.ts +4 -1
- package/dist/adapters/SQL/Model.d.ts.map +1 -1
- package/dist/adapters/SQL/Model.js +28 -37
- package/dist/api/internal/events.js +2 -2
- package/dist/api/routing/middleware/middleware.js +3 -3
- package/dist/api/routing.d.ts.map +1 -1
- package/dist/api/routing.js +12 -2
- package/dist/otel.d.ts +66 -0
- package/dist/otel.d.ts.map +1 -0
- package/dist/otel.js +56 -0
- package/package.json +6 -2
- package/src/Model/Repository/internal/internal.ts +82 -71
- package/src/QueueMaker/SQLQueue.ts +35 -13
- package/src/QueueMaker/memQueue.ts +35 -13
- package/src/QueueMaker/sbqueue.ts +26 -12
- package/src/RequestContext.ts +6 -6
- package/src/Store/Cosmos.ts +81 -66
- package/src/Store/Disk.ts +50 -16
- package/src/Store/Memory.ts +59 -34
- package/src/Store/SQL/Pg.ts +74 -40
- package/src/Store/SQL.ts +149 -83
- package/src/adapters/SQL/Model.ts +31 -36
- package/src/api/internal/events.ts +1 -1
- package/src/api/routing/middleware/middleware.ts +2 -2
- package/src/api/routing.ts +11 -1
- package/src/otel.ts +141 -0
package/src/Store/Cosmos.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { OptimisticConcurrencyException } from "../errors.js"
|
|
|
8
8
|
import { InfraLogger } from "../logger.js"
|
|
9
9
|
import type { FieldValues } from "../Model/filter/types.js"
|
|
10
10
|
import { type RawQuery } from "../Model/query.js"
|
|
11
|
+
import { annotateCosmosResponse, withDbSpan } from "../otel.js"
|
|
11
12
|
import { buildWhereCosmosQuery3, logQuery } from "./Cosmos/query.js"
|
|
12
13
|
import { storeId } from "./Memory.js"
|
|
13
14
|
import { type FilterArgs, type PersistenceModelType, type StorageConfig, type Store, type StoreConfig, StoreMaker } from "./service.js"
|
|
@@ -35,19 +36,21 @@ const annotateFeed = (resp: {
|
|
|
35
36
|
requestCharge?: number
|
|
36
37
|
diagnostics?: { clientSideRequestStatistics?: { totalResponsePayloadLengthInBytes?: number } }
|
|
37
38
|
}) =>
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
annotateCosmosResponse({
|
|
40
|
+
requestCharge: resp.requestCharge,
|
|
41
|
+
returnedRows: resp.resources.length,
|
|
42
|
+
responseBytes: respBytes(resp)
|
|
42
43
|
})
|
|
43
44
|
|
|
44
45
|
const annotateItem = (resp: {
|
|
45
46
|
requestCharge?: number
|
|
47
|
+
statusCode?: number
|
|
46
48
|
diagnostics?: { clientSideRequestStatistics?: { totalResponsePayloadLengthInBytes?: number } }
|
|
47
49
|
}) =>
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
annotateCosmosResponse({
|
|
51
|
+
requestCharge: resp.requestCharge,
|
|
52
|
+
statusCode: resp.statusCode,
|
|
53
|
+
responseBytes: respBytes(resp)
|
|
51
54
|
})
|
|
52
55
|
|
|
53
56
|
const makeCosmosStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
|
|
@@ -137,7 +140,13 @@ const makeCosmosStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
|
|
|
137
140
|
)
|
|
138
141
|
}),
|
|
139
142
|
Effect.withLogSpan(`Cosmos.seedCheck ${name} in ${ns} [effect-app/infra/Store]`),
|
|
140
|
-
|
|
143
|
+
withDbSpan({
|
|
144
|
+
operation: "seed",
|
|
145
|
+
system: "cosmosdb",
|
|
146
|
+
collection: containerId,
|
|
147
|
+
namespace: ns,
|
|
148
|
+
entity: name
|
|
149
|
+
})
|
|
141
150
|
)
|
|
142
151
|
}
|
|
143
152
|
const seedNamespace = Effect.fn("seedNamespace")(function*(ns: string) {
|
|
@@ -251,9 +260,13 @@ const makeCosmosStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
|
|
|
251
260
|
return batchResult.flat() as unknown as NonEmptyReadonlyArray<Encoded>
|
|
252
261
|
})
|
|
253
262
|
.pipe(
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
263
|
+
withDbSpan({
|
|
264
|
+
operation: "bulkSet",
|
|
265
|
+
system: "cosmosdb",
|
|
266
|
+
collection: containerId,
|
|
267
|
+
namespace: ns,
|
|
268
|
+
entity: name
|
|
269
|
+
})
|
|
257
270
|
)
|
|
258
271
|
|
|
259
272
|
const bulkSet = (items: NonEmptyReadonlyArray<PM>) =>
|
|
@@ -321,14 +334,13 @@ const makeCosmosStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
|
|
|
321
334
|
})) as unknown as NonEmptyReadonlyArray<Encoded>
|
|
322
335
|
})))
|
|
323
336
|
})
|
|
324
|
-
.pipe(
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
}, { captureStackTrace: false }))
|
|
337
|
+
.pipe(withDbSpan({
|
|
338
|
+
operation: "batchSet",
|
|
339
|
+
system: "cosmosdb",
|
|
340
|
+
collection: containerId,
|
|
341
|
+
namespace: ns,
|
|
342
|
+
entity: name
|
|
343
|
+
}))
|
|
332
344
|
))
|
|
333
345
|
}
|
|
334
346
|
|
|
@@ -352,13 +364,14 @@ const makeCosmosStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
|
|
|
352
364
|
)
|
|
353
365
|
})
|
|
354
366
|
.pipe(
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
367
|
+
withDbSpan({
|
|
368
|
+
operation: "queryRaw",
|
|
369
|
+
system: "cosmosdb",
|
|
370
|
+
collection: containerId,
|
|
371
|
+
namespace: ns,
|
|
372
|
+
entity: name,
|
|
373
|
+
query: q.query
|
|
374
|
+
})
|
|
362
375
|
)
|
|
363
376
|
)
|
|
364
377
|
),
|
|
@@ -379,13 +392,13 @@ const makeCosmosStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
|
|
|
379
392
|
)
|
|
380
393
|
)
|
|
381
394
|
.pipe(
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
}
|
|
395
|
+
withDbSpan({
|
|
396
|
+
operation: "batchRemove",
|
|
397
|
+
system: "cosmosdb",
|
|
398
|
+
collection: containerId,
|
|
399
|
+
namespace: ns,
|
|
400
|
+
entity: name
|
|
401
|
+
})
|
|
389
402
|
)
|
|
390
403
|
)),
|
|
391
404
|
all: Effect
|
|
@@ -408,13 +421,14 @@ const makeCosmosStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
|
|
|
408
421
|
return response.resources.map((_) => ({ ...defaultValues, ...mapReverseId(_) }))
|
|
409
422
|
})
|
|
410
423
|
.pipe(
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
424
|
+
withDbSpan({
|
|
425
|
+
operation: "all",
|
|
426
|
+
system: "cosmosdb",
|
|
427
|
+
collection: containerId,
|
|
428
|
+
namespace: ns,
|
|
429
|
+
entity: name,
|
|
430
|
+
query: q.query
|
|
431
|
+
})
|
|
418
432
|
)
|
|
419
433
|
)
|
|
420
434
|
),
|
|
@@ -472,13 +486,14 @@ const makeCosmosStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
|
|
|
472
486
|
return response.resources.map(({ f }) => ({ ...defaultValues, ...mapReverseId(f as any) }) as any)
|
|
473
487
|
})
|
|
474
488
|
.pipe(
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
489
|
+
withDbSpan({
|
|
490
|
+
operation: "filter",
|
|
491
|
+
system: "cosmosdb",
|
|
492
|
+
collection: containerId,
|
|
493
|
+
namespace: ns,
|
|
494
|
+
entity: name,
|
|
495
|
+
query: q.query
|
|
496
|
+
})
|
|
482
497
|
)
|
|
483
498
|
)
|
|
484
499
|
)
|
|
@@ -497,16 +512,17 @@ const makeCosmosStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
|
|
|
497
512
|
Option.map((_) => ({ ...defaultValues, ...mapReverseId(_) }))
|
|
498
513
|
)
|
|
499
514
|
})
|
|
500
|
-
.pipe(
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
}
|
|
515
|
+
.pipe(withDbSpan({
|
|
516
|
+
operation: "find",
|
|
517
|
+
system: "cosmosdb",
|
|
518
|
+
collection: containerId,
|
|
519
|
+
namespace: ns,
|
|
520
|
+
entity: name,
|
|
521
|
+
extra: {
|
|
522
|
+
"azure.cosmosdb.operation.partition_key": nsPartitionValue(ns, { [idKey]: id } as Encoded),
|
|
523
|
+
"app.entity.id": id
|
|
524
|
+
}
|
|
525
|
+
}))
|
|
510
526
|
)),
|
|
511
527
|
set: (e) =>
|
|
512
528
|
resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
@@ -556,15 +572,14 @@ const makeCosmosStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
|
|
|
556
572
|
_etag: x.etag
|
|
557
573
|
}))
|
|
558
574
|
}),
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
}, { captureStackTrace: false })
|
|
575
|
+
withDbSpan({
|
|
576
|
+
operation: "set",
|
|
577
|
+
system: "cosmosdb",
|
|
578
|
+
collection: containerId,
|
|
579
|
+
namespace: ns,
|
|
580
|
+
entity: name,
|
|
581
|
+
extra: { "app.entity.id": e[idKey] }
|
|
582
|
+
})
|
|
568
583
|
)
|
|
569
584
|
)),
|
|
570
585
|
batchSet,
|
package/src/Store/Disk.ts
CHANGED
|
@@ -5,6 +5,7 @@ import fs from "fs"
|
|
|
5
5
|
|
|
6
6
|
import { Console, Effect, flow, Semaphore } from "effect-app"
|
|
7
7
|
import type { FieldValues } from "../Model/filter/types.js"
|
|
8
|
+
import { withDbSpan } from "../otel.js"
|
|
8
9
|
import { makeMemoryStoreInt, storeId } from "./Memory.js"
|
|
9
10
|
import { type PersistenceModelType, type StorageConfig, type Store, type StoreConfig, StoreMaker } from "./service.js"
|
|
10
11
|
|
|
@@ -26,42 +27,75 @@ function makeDiskStoreInt<IdKey extends keyof Encoded, Encoded extends FieldValu
|
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
29
|
const file = dir + "/" + prefix + name + ".json"
|
|
30
|
+
const fileExtra = { "disk.file.path": file }
|
|
29
31
|
const fsStore = {
|
|
30
32
|
get: fu
|
|
31
33
|
.readTextFile(file)
|
|
32
34
|
.pipe(
|
|
33
|
-
|
|
35
|
+
withDbSpan({
|
|
36
|
+
operation: "read.readFile",
|
|
37
|
+
system: "disk",
|
|
38
|
+
collection: name,
|
|
39
|
+
namespace,
|
|
40
|
+
entity: name,
|
|
41
|
+
extra: fileExtra
|
|
42
|
+
}),
|
|
34
43
|
Effect.flatMap((x) =>
|
|
35
44
|
Effect.sync(() => JSON.parse(x) as PM[]).pipe(
|
|
36
|
-
|
|
45
|
+
withDbSpan({
|
|
46
|
+
operation: "read.parse",
|
|
47
|
+
system: "disk",
|
|
48
|
+
collection: name,
|
|
49
|
+
namespace,
|
|
50
|
+
entity: name,
|
|
51
|
+
extra: fileExtra
|
|
52
|
+
})
|
|
37
53
|
)
|
|
38
54
|
),
|
|
39
55
|
Effect.orDie,
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
56
|
+
withDbSpan({
|
|
57
|
+
operation: "read",
|
|
58
|
+
system: "disk",
|
|
59
|
+
collection: name,
|
|
60
|
+
namespace,
|
|
61
|
+
entity: name,
|
|
62
|
+
extra: fileExtra
|
|
63
|
+
})
|
|
43
64
|
),
|
|
44
65
|
setRaw: (v: Iterable<PM>) =>
|
|
45
66
|
Effect
|
|
46
67
|
.sync(() => JSON.stringify([...v], undefined, 2))
|
|
47
68
|
.pipe(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
69
|
+
withDbSpan({
|
|
70
|
+
operation: "stringify",
|
|
71
|
+
system: "disk",
|
|
72
|
+
collection: name,
|
|
73
|
+
namespace,
|
|
74
|
+
entity: name,
|
|
75
|
+
extra: fileExtra
|
|
76
|
+
}),
|
|
51
77
|
Effect
|
|
52
78
|
.flatMap(
|
|
53
79
|
(json) =>
|
|
54
80
|
fu
|
|
55
81
|
.writeTextFile(file, json)
|
|
56
|
-
.pipe(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
82
|
+
.pipe(withDbSpan({
|
|
83
|
+
operation: "write.writeFile",
|
|
84
|
+
system: "disk",
|
|
85
|
+
collection: name,
|
|
86
|
+
namespace,
|
|
87
|
+
entity: name,
|
|
88
|
+
extra: { ...fileExtra, "disk.file.size": json.length }
|
|
89
|
+
}))
|
|
60
90
|
),
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
91
|
+
withDbSpan({
|
|
92
|
+
operation: "write",
|
|
93
|
+
system: "disk",
|
|
94
|
+
collection: name,
|
|
95
|
+
namespace,
|
|
96
|
+
entity: name,
|
|
97
|
+
extra: fileExtra
|
|
98
|
+
})
|
|
65
99
|
)
|
|
66
100
|
}
|
|
67
101
|
|
package/src/Store/Memory.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { Array, Context, Effect, flow, type NonEmptyReadonlyArray, Option, Order
|
|
|
4
4
|
import { NonEmptyString255 } from "effect-app/Schema"
|
|
5
5
|
import { InfraLogger } from "../logger.js"
|
|
6
6
|
import type { FieldValues } from "../Model/filter/types.js"
|
|
7
|
+
import { withDbSpan } from "../otel.js"
|
|
7
8
|
import { codeFilter, codeFilter3_ } from "./codeFilter.js"
|
|
8
9
|
import { type FilterArgs, type PersistenceModelType, type Store, type StoreConfig, StoreMaker } from "./service.js"
|
|
9
10
|
import { makeUpdateETag } from "./utils.js"
|
|
@@ -162,38 +163,48 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
162
163
|
.pipe(
|
|
163
164
|
// Effect.tap(() => logQuery(query, defaultValues)),
|
|
164
165
|
Effect.map(query.memory),
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
166
|
+
withDbSpan({
|
|
167
|
+
operation: "queryRaw",
|
|
168
|
+
system: "memory",
|
|
169
|
+
collection: modelName,
|
|
170
|
+
namespace,
|
|
171
|
+
entity: modelName
|
|
172
|
+
})
|
|
168
173
|
),
|
|
169
174
|
|
|
170
|
-
all: all.pipe(
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
175
|
+
all: all.pipe(withDbSpan({
|
|
176
|
+
operation: "all",
|
|
177
|
+
system: "memory",
|
|
178
|
+
collection: modelName,
|
|
179
|
+
namespace,
|
|
180
|
+
entity: modelName
|
|
181
|
+
})),
|
|
176
182
|
find: (id) =>
|
|
177
183
|
Ref
|
|
178
184
|
.get(store)
|
|
179
185
|
.pipe(
|
|
180
186
|
Effect.map((_) => Option.fromNullishOr(_.get(id))),
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
187
|
+
withDbSpan({
|
|
188
|
+
operation: "find",
|
|
189
|
+
system: "memory",
|
|
190
|
+
collection: modelName,
|
|
191
|
+
namespace,
|
|
192
|
+
entity: modelName,
|
|
193
|
+
extra: { "app.entity.id": id as unknown }
|
|
194
|
+
})
|
|
188
195
|
),
|
|
189
196
|
filter: (f) =>
|
|
190
197
|
all
|
|
191
198
|
.pipe(
|
|
192
199
|
Effect.tap(() => logQuery(f, defaultValues)),
|
|
193
200
|
Effect.map(memFilter(f)),
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
201
|
+
withDbSpan({
|
|
202
|
+
operation: "filter",
|
|
203
|
+
system: "memory",
|
|
204
|
+
collection: modelName,
|
|
205
|
+
namespace,
|
|
206
|
+
entity: modelName
|
|
207
|
+
})
|
|
197
208
|
),
|
|
198
209
|
set: (e) =>
|
|
199
210
|
s
|
|
@@ -208,10 +219,14 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
208
219
|
)
|
|
209
220
|
),
|
|
210
221
|
withPermit,
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
222
|
+
withDbSpan({
|
|
223
|
+
operation: "set",
|
|
224
|
+
system: "memory",
|
|
225
|
+
collection: modelName,
|
|
226
|
+
namespace,
|
|
227
|
+
entity: modelName,
|
|
228
|
+
extra: { "app.entity.id": e[idKey] as unknown }
|
|
229
|
+
})
|
|
215
230
|
),
|
|
216
231
|
batchRemove: (items: NonEmptyReadonlyArray<Encoded[IdKey]>) =>
|
|
217
232
|
pipe(
|
|
@@ -222,10 +237,13 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
222
237
|
Effect.filterOrFail((_) => _.length <= 100, () => "BatchRemove: a batch may not exceed 100 items"),
|
|
223
238
|
Effect.orDie,
|
|
224
239
|
Effect.andThen(batchRemove),
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
240
|
+
withDbSpan({
|
|
241
|
+
operation: "batchRemove",
|
|
242
|
+
system: "memory",
|
|
243
|
+
collection: modelName,
|
|
244
|
+
namespace,
|
|
245
|
+
entity: modelName
|
|
246
|
+
})
|
|
229
247
|
)
|
|
230
248
|
),
|
|
231
249
|
batchSet: (items: readonly [PM, ...PM[]]) =>
|
|
@@ -237,18 +255,25 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
237
255
|
Effect.filterOrFail((_) => _.length <= 100, () => "BatchSet: a batch may not exceed 100 items"),
|
|
238
256
|
Effect.orDie,
|
|
239
257
|
Effect.andThen(batchSet),
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
258
|
+
withDbSpan({
|
|
259
|
+
operation: "batchSet",
|
|
260
|
+
system: "memory",
|
|
261
|
+
collection: modelName,
|
|
262
|
+
namespace,
|
|
263
|
+
entity: modelName
|
|
264
|
+
})
|
|
244
265
|
)
|
|
245
266
|
),
|
|
246
267
|
bulkSet: flow(
|
|
247
268
|
batchSet,
|
|
248
269
|
(_) =>
|
|
249
|
-
_.pipe(
|
|
250
|
-
|
|
251
|
-
|
|
270
|
+
_.pipe(withDbSpan({
|
|
271
|
+
operation: "bulkSet",
|
|
272
|
+
system: "memory",
|
|
273
|
+
collection: modelName,
|
|
274
|
+
namespace,
|
|
275
|
+
entity: modelName
|
|
276
|
+
}))
|
|
252
277
|
)
|
|
253
278
|
}
|
|
254
279
|
return s
|
package/src/Store/SQL/Pg.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { SqlClient } from "effect/unstable/sql"
|
|
|
6
6
|
import { OptimisticConcurrencyException } from "../../errors.js"
|
|
7
7
|
import { InfraLogger } from "../../logger.js"
|
|
8
8
|
import type { FieldValues } from "../../Model/filter/types.js"
|
|
9
|
+
import { withDbSpan } from "../../otel.js"
|
|
9
10
|
import { storeId } from "../Memory.js"
|
|
10
11
|
import { type FilterArgs, type PersistenceModelType, type StorageConfig, type Store, type StoreConfig, StoreMaker } from "../service.js"
|
|
11
12
|
import { makeETag } from "../utils.js"
|
|
@@ -161,25 +162,28 @@ const makePgStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
|
|
|
161
162
|
seedNamespace: (ns) => seedNamespace(ns),
|
|
162
163
|
|
|
163
164
|
all: resolveNamespace.pipe(
|
|
164
|
-
Effect.flatMap((ns) =>
|
|
165
|
-
|
|
165
|
+
Effect.flatMap((ns) => {
|
|
166
|
+
const sqlText = `SELECT id, _etag, data FROM "${tableName}" WHERE _namespace = $1`
|
|
167
|
+
return exec(sqlText, [ns])
|
|
166
168
|
.pipe(
|
|
167
169
|
Effect.map((rows) => (rows as any[]).map((r) => parseRow<Encoded>(r, idKey, defaultValues))),
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
170
|
+
withDbSpan({
|
|
171
|
+
operation: "all",
|
|
172
|
+
system: "postgresql",
|
|
173
|
+
collection: tableName,
|
|
174
|
+
namespace: ns,
|
|
175
|
+
entity: name,
|
|
176
|
+
query: sqlText
|
|
177
|
+
})
|
|
175
178
|
)
|
|
176
|
-
)
|
|
179
|
+
})
|
|
177
180
|
),
|
|
178
181
|
|
|
179
182
|
find: (id) =>
|
|
180
183
|
resolveNamespace.pipe(Effect
|
|
181
|
-
.flatMap((ns) =>
|
|
182
|
-
|
|
184
|
+
.flatMap((ns) => {
|
|
185
|
+
const sqlText = `SELECT id, _etag, data FROM "${tableName}" WHERE id = $1 AND _namespace = $2`
|
|
186
|
+
return exec(sqlText, [id, ns])
|
|
183
187
|
.pipe(
|
|
184
188
|
Effect.map((rows) => {
|
|
185
189
|
const row = (rows as any[])[0]
|
|
@@ -187,11 +191,17 @@ const makePgStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
|
|
|
187
191
|
? Option.some(parseRow<Encoded>(row, idKey, defaultValues))
|
|
188
192
|
: Option.none()
|
|
189
193
|
}),
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
194
|
+
withDbSpan({
|
|
195
|
+
operation: "find",
|
|
196
|
+
system: "postgresql",
|
|
197
|
+
collection: tableName,
|
|
198
|
+
namespace: ns,
|
|
199
|
+
entity: name,
|
|
200
|
+
query: sqlText,
|
|
201
|
+
extra: { "app.entity.id": id }
|
|
202
|
+
})
|
|
193
203
|
)
|
|
194
|
-
)),
|
|
204
|
+
})),
|
|
195
205
|
|
|
196
206
|
filter: <U extends keyof Encoded = never>(f: FilterArgs<Encoded, U>) => {
|
|
197
207
|
const filter = f
|
|
@@ -225,6 +235,7 @@ const makePgStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
|
|
|
225
235
|
})
|
|
226
236
|
.pipe(
|
|
227
237
|
Effect.tap((q) => logQuery(q)),
|
|
238
|
+
Effect.tap((q) => Effect.annotateCurrentSpan({ "db.query.text": q.sql })),
|
|
228
239
|
Effect.flatMap((q) =>
|
|
229
240
|
exec(q.sql, q.params).pipe(
|
|
230
241
|
Effect.map((rows) => {
|
|
@@ -244,9 +255,13 @@ const makePgStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
|
|
|
244
255
|
})
|
|
245
256
|
)
|
|
246
257
|
),
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
258
|
+
withDbSpan({
|
|
259
|
+
operation: "filter",
|
|
260
|
+
system: "postgresql",
|
|
261
|
+
collection: tableName,
|
|
262
|
+
namespace: ns,
|
|
263
|
+
entity: name
|
|
264
|
+
})
|
|
250
265
|
)
|
|
251
266
|
))
|
|
252
267
|
},
|
|
@@ -254,53 +269,72 @@ const makePgStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
|
|
|
254
269
|
set: (e) =>
|
|
255
270
|
resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
256
271
|
setInternal(e, ns).pipe(
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
272
|
+
withDbSpan({
|
|
273
|
+
operation: "set",
|
|
274
|
+
system: "postgresql",
|
|
275
|
+
collection: tableName,
|
|
276
|
+
namespace: ns,
|
|
277
|
+
entity: name,
|
|
278
|
+
extra: { "app.entity.id": e[idKey] }
|
|
279
|
+
})
|
|
260
280
|
)
|
|
261
281
|
)),
|
|
262
282
|
|
|
263
283
|
batchSet: (items) =>
|
|
264
284
|
resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
265
285
|
bulkSetInternal(items, ns).pipe(
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
286
|
+
withDbSpan({
|
|
287
|
+
operation: "batchSet",
|
|
288
|
+
system: "postgresql",
|
|
289
|
+
collection: tableName,
|
|
290
|
+
namespace: ns,
|
|
291
|
+
entity: name
|
|
292
|
+
})
|
|
269
293
|
)
|
|
270
294
|
)),
|
|
271
295
|
|
|
272
296
|
bulkSet: (items) =>
|
|
273
297
|
resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
274
298
|
bulkSetInternal(items, ns).pipe(
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
299
|
+
withDbSpan({
|
|
300
|
+
operation: "bulkSet",
|
|
301
|
+
system: "postgresql",
|
|
302
|
+
collection: tableName,
|
|
303
|
+
namespace: ns,
|
|
304
|
+
entity: name
|
|
305
|
+
})
|
|
278
306
|
)
|
|
279
307
|
)),
|
|
280
308
|
|
|
281
309
|
batchRemove: (ids) => {
|
|
282
310
|
const placeholders = ids.map((_, i) => `$${i + 1}`).join(", ")
|
|
283
311
|
const nsPlaceholder = `$${ids.length + 1}`
|
|
284
|
-
return resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
[...ids, ns]
|
|
288
|
-
)
|
|
312
|
+
return resolveNamespace.pipe(Effect.flatMap((ns) => {
|
|
313
|
+
const sqlText = `DELETE FROM "${tableName}" WHERE id IN (${placeholders}) AND _namespace = ${nsPlaceholder}`
|
|
314
|
+
return exec(sqlText, [...ids, ns])
|
|
289
315
|
.pipe(
|
|
290
316
|
Effect.asVoid,
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
317
|
+
withDbSpan({
|
|
318
|
+
operation: "batchRemove",
|
|
319
|
+
system: "postgresql",
|
|
320
|
+
collection: tableName,
|
|
321
|
+
namespace: ns,
|
|
322
|
+
entity: name,
|
|
323
|
+
query: sqlText
|
|
324
|
+
})
|
|
294
325
|
)
|
|
295
|
-
))
|
|
326
|
+
}))
|
|
296
327
|
},
|
|
297
328
|
|
|
298
329
|
queryRaw: (query) =>
|
|
299
330
|
s.all.pipe(
|
|
300
331
|
Effect.map(query.memory),
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
332
|
+
withDbSpan({
|
|
333
|
+
operation: "queryRaw",
|
|
334
|
+
system: "postgresql",
|
|
335
|
+
collection: tableName,
|
|
336
|
+
entity: name
|
|
337
|
+
})
|
|
304
338
|
)
|
|
305
339
|
}
|
|
306
340
|
|