@vsrepo/prisma7-adapter 1.0.1 → 1.0.3
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/README.md
CHANGED
|
@@ -236,6 +236,36 @@ await userRepository.transaction(async tx => {
|
|
|
236
236
|
});
|
|
237
237
|
```
|
|
238
238
|
|
|
239
|
+
### Concurrency in `deleteManyReturning`
|
|
240
|
+
|
|
241
|
+
`deleteManyReturning` runs a `findMany` on the given `where` (to capture the records it will return) and then **re-applies the same `where`** to a `deleteMany`. Prisma has no native `deleteManyAndReturn`, which is why the delete is driven by the `where` instead of by the records fetched in the `findMany`.
|
|
242
|
+
|
|
243
|
+
Because of this two-step shape, a concurrent change between the `findMany` and the `deleteMany` can make them diverge:
|
|
244
|
+
|
|
245
|
+
- a row **inserted after** the `findMany` that matches the `where` will still be deleted, even though it wasn't returned;
|
|
246
|
+
- a row that **stops matching** the `where` before the `deleteMany` runs won't be deleted, even though it was returned.
|
|
247
|
+
|
|
248
|
+
In other words, the returned records and the rows actually deleted are not guaranteed to be identical under concurrency.
|
|
249
|
+
|
|
250
|
+
If you need to guarantee there are no concurrency issues, run this method inside a `repository.transaction()` at a higher isolation level — e.g. `SERIALIZABLE`:
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
import { VSRepository, TransactionIsolationLevel } from "vsrepo";
|
|
254
|
+
|
|
255
|
+
await userRepository.transaction(async tx => {
|
|
256
|
+
// Atomic: no concurrent insert/update can slip between the findMany
|
|
257
|
+
// and the deleteMany, since SERIALIZABLE isolates this transaction.
|
|
258
|
+
// `deleteManyReturning` is reached through the dynamic method you mapped
|
|
259
|
+
// to the adapter (e.g. a `@DynamicMethod()` exposing `deleteManyReturning`),
|
|
260
|
+
// always passing `{ db: tx }` so it runs inside this transaction.
|
|
261
|
+
const deleted = await userRepository.deleteManyReturningByIdIn([...], { db: tx });
|
|
262
|
+
}, {
|
|
263
|
+
isolationLevel: TransactionIsolationLevel.SERIALIZABLE,
|
|
264
|
+
});
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
> The isolation level applies only when the repository call actually runs **inside** that transaction (i.e. with `{ db: tx }`). When `deleteManyReturning` starts its own internal transaction (no `options.db` passed), it falls back to the default isolation level.
|
|
268
|
+
|
|
239
269
|
## Logging
|
|
240
270
|
|
|
241
271
|
The adapter uses `VSLogger` (from `vsrepo`) internally: every method logs a `DEBUG` line with the resolved Prisma arg, and start/end performance logs (surfacing a `WARN` for slow operations). Set `logLevel` in the constructor config to control verbosity; it defaults to `VSLogLevel.WARN`.
|
package/README.pt-BR.md
CHANGED
|
@@ -236,6 +236,36 @@ await userRepository.transaction(async tx => {
|
|
|
236
236
|
});
|
|
237
237
|
```
|
|
238
238
|
|
|
239
|
+
### Concorrência em `deleteManyReturning`
|
|
240
|
+
|
|
241
|
+
O `deleteManyReturning` roda um `findMany` no `where` informado (pra capturar os registros que vai devolver) e depois **re-aplica o mesmo `where`** num `deleteMany`. Como o Prisma não tem um `deleteManyAndReturn` nativo, o delete é guiado pelo `where` — e não pelos registros retornados no `findMany`.
|
|
242
|
+
|
|
243
|
+
Por causa desse formato em duas etapas, uma alteração concorrente entre o `findMany` e o `deleteMany` pode fazer os dois divergirem:
|
|
244
|
+
|
|
245
|
+
- uma linha **inserida depois** do `findMany` que bate com o `where` ainda será deletada, mesmo não tendo sido retornada;
|
|
246
|
+
- uma linha que **deixa de bater** com o `where` antes do `deleteMany` rodar não será deletada, mesmo tendo sido retornada.
|
|
247
|
+
|
|
248
|
+
Ou seja: os registros retornados e as linhas realmente deletadas não têm garantia de serem idênticos sob concorrência.
|
|
249
|
+
|
|
250
|
+
Se você quiser garantir que não haverá problemas de concorrência, rode esse método **dentro** de um `repository.transaction()` num nível de isolamento mais alto — ex.: `SERIALIZABLE`:
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
import { VSRepository, TransactionIsolationLevel } from "vsrepo";
|
|
254
|
+
|
|
255
|
+
await userRepository.transaction(async tx => {
|
|
256
|
+
// Atômico: nenhum insert/update concorrente consegue entrar entre o
|
|
257
|
+
// findMany e o deleteMany, pois o SERIALIZABLE isola esta transaction.
|
|
258
|
+
// O `deleteManyReturning` é acessado através do método dinâmico que você
|
|
259
|
+
// mapeou pro adapter (ex.: um `@DynamicMethod()` expondo `deleteManyReturning`),
|
|
260
|
+
// sempre passando `{ db: tx }` pra rodar dentro desta transaction.
|
|
261
|
+
const deleted = await userRepository.deleteManyReturningByIdIn([...], { db: tx });
|
|
262
|
+
}, {
|
|
263
|
+
isolationLevel: TransactionIsolationLevel.SERIALIZABLE,
|
|
264
|
+
});
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
> O nível de isolamento só se aplica quando a chamada roda **dentro** daquela transaction (ou seja, com `{ db: tx }`). Quando o `deleteManyReturning` inicia a própria transaction interna (sem `options.db`), ele usa o nível de isolamento padrão.
|
|
268
|
+
|
|
239
269
|
## Logging
|
|
240
270
|
|
|
241
271
|
O adapter usa o `VSLogger` (de `vsrepo`) internamente: todo método loga uma linha `DEBUG` com o arg resolvido do Prisma, além de logs de performance de início/fim (com `WARN` pra operações lentas). Configure `logLevel` na config do construtor pra controlar a verbosidade; o default é `VSLogLevel.WARN`.
|
|
@@ -79,10 +79,18 @@ export declare class VSRepoPrisma7Adapter<T> extends VSRepoAdapter<T> {
|
|
|
79
79
|
deleteMany(where: VSRepoWhere<T>, options?: AdapterMethodOptions<T>): Promise<CountResult>;
|
|
80
80
|
/**
|
|
81
81
|
* Deletes every record matching `where`, returning the deleted records.
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
82
|
+
*
|
|
83
|
+
* Prisma has no `deleteManyAndReturn`, so this does a `findMany` on
|
|
84
|
+
* `where` first and then re-applies the same `where` to a `deleteMany`,
|
|
85
|
+
* both inside a transaction (`runTransactional`).
|
|
86
|
+
*
|
|
87
|
+
* Because the delete is driven by the same `where` (not by the fetched
|
|
88
|
+
* records), a concurrent change between the two operations can make them
|
|
89
|
+
* diverge — e.g. a row inserted after the `findMany` matching the `where`
|
|
90
|
+
* would still be deleted, while a row changed to stop matching wouldn't.
|
|
91
|
+
* If you need to guarantee no concurrency issues, run this method inside a
|
|
92
|
+
* `repository.transaction()` with a higher isolation level, e.g.
|
|
93
|
+
* `TransactionIsolationLevel.SERIALIZABLE`.
|
|
86
94
|
*/
|
|
87
95
|
deleteManyReturning(where: VSRepoWhere<T>, options?: AdapterMethodOptions<T>): Promise<T[]>;
|
|
88
96
|
/** Updates a single record matching `where`. */
|
package/dist/prisma7.adapter.js
CHANGED
|
@@ -40,7 +40,7 @@ class VSRepoPrisma7Adapter extends vsrepo_1.VSRepoAdapter {
|
|
|
40
40
|
this.tableName = validated.tableName;
|
|
41
41
|
this.pkName = validated.pkName;
|
|
42
42
|
this.relations = validated.relations;
|
|
43
|
-
this.logger = new vsrepo_1.VSLogger(validated.logLevel ?? vsrepo_1.VSLogLevel.WARN, this.constructor.name + "Logger", validated.logSlowThresholdMs);
|
|
43
|
+
this.logger = new vsrepo_1.VSLogger(validated.logLevel ?? vsrepo_1.VSLogLevel.WARN, this.constructor.name + "Logger", validated.logSlowThresholdMs ?? 300);
|
|
44
44
|
this.logger.logInfo(`${this.constructor.name} initialized for table '${this.tableName}' (pkName: '${this.pkName}'${this.relations ? `, relations: [${Object.keys(this.relations).join(", ")}]` : ""})`);
|
|
45
45
|
}
|
|
46
46
|
getPrismaRepository(db) {
|
|
@@ -241,10 +241,18 @@ class VSRepoPrisma7Adapter extends vsrepo_1.VSRepoAdapter {
|
|
|
241
241
|
}
|
|
242
242
|
/**
|
|
243
243
|
* Deletes every record matching `where`, returning the deleted records.
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
244
|
+
*
|
|
245
|
+
* Prisma has no `deleteManyAndReturn`, so this does a `findMany` on
|
|
246
|
+
* `where` first and then re-applies the same `where` to a `deleteMany`,
|
|
247
|
+
* both inside a transaction (`runTransactional`).
|
|
248
|
+
*
|
|
249
|
+
* Because the delete is driven by the same `where` (not by the fetched
|
|
250
|
+
* records), a concurrent change between the two operations can make them
|
|
251
|
+
* diverge — e.g. a row inserted after the `findMany` matching the `where`
|
|
252
|
+
* would still be deleted, while a row changed to stop matching wouldn't.
|
|
253
|
+
* If you need to guarantee no concurrency issues, run this method inside a
|
|
254
|
+
* `repository.transaction()` with a higher isolation level, e.g.
|
|
255
|
+
* `TransactionIsolationLevel.SERIALIZABLE`.
|
|
248
256
|
*/
|
|
249
257
|
async deleteManyReturning(where, options) {
|
|
250
258
|
const start = this.logger.startPerformLog("run deleteManyReturning");
|
|
@@ -10,4 +10,5 @@ export declare const adapterConfigSchema: v.ObjectSchema<{
|
|
|
10
10
|
readonly nullable: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
|
|
11
11
|
}, undefined>, undefined>, undefined>;
|
|
12
12
|
readonly logLevel: v.OptionalSchema<v.EnumSchema<typeof VSLogLevel, undefined>, undefined>;
|
|
13
|
+
readonly logSlowThresholdMs: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.GtValueAction<number, 0, undefined>]>, undefined>;
|
|
13
14
|
}, undefined>;
|
|
@@ -42,4 +42,5 @@ exports.adapterConfigSchema = v.object({
|
|
|
42
42
|
pkName: v.pipe(v.string(), v.minLength(1)),
|
|
43
43
|
relations: v.optional(v.record(v.string(), relation_schema_1.relationSchema)),
|
|
44
44
|
logLevel: v.optional(v.enum(vsrepo_1.VSLogLevel)),
|
|
45
|
+
logSlowThresholdMs: v.optional(v.pipe(v.number(), v.gtValue(0))),
|
|
45
46
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vsrepo/prisma7-adapter",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "VSRepository adapter implementation backed by Prisma 7, translating every VSRepository operation into Prisma Client calls.",
|
|
5
5
|
"homepage": "https://github.com/jaobrabo123/VSRepoPrisma7Adapter#readme",
|
|
6
6
|
"repository": {
|