@rvoh/dream 2.30.1 → 2.32.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/dist/cjs/src/Dream.js +9 -5
- package/dist/cjs/src/db/errors.js +2 -0
- package/dist/cjs/src/decorators/Decorators.js +121 -6
- package/dist/cjs/src/decorators/field/sortable/Sortable.js +4 -31
- package/dist/cjs/src/decorators/field/sortable/helpers/cascadeReachesEveryRowInSortScope.js +51 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/performSortableDestroyWork.js +5 -2
- package/dist/cjs/src/decorators/field/sortable/helpers/prepareSortableFieldsForDestroy.js +39 -25
- package/dist/cjs/src/decorators/field/sortable/helpers/sortableCascadeEdge.js +32 -0
- package/dist/cjs/src/dream/Query.js +37 -13
- package/dist/cjs/src/dream/QueryDriver/Base.js +26 -0
- package/dist/cjs/src/dream/QueryDriver/Kysely.js +4 -1
- package/dist/cjs/src/dream/QueryDriver/Postgres.js +7 -1
- package/dist/cjs/src/dream/internal/destroyAssociatedRecords.js +7 -0
- package/dist/cjs/src/dream/internal/destroyDream.js +25 -14
- package/dist/cjs/src/dream/internal/saveDream.js +9 -0
- package/dist/cjs/src/dream/internal/undestroyDream.js +55 -32
- package/dist/cjs/src/dream/internal/withConcurrentWriterRetry.js +64 -0
- package/dist/cjs/src/errors/CannotSaveMissingDream.js +24 -0
- package/dist/cjs/src/errors/SortableBatchRequiresTooManyScopeLocks.js +7 -0
- package/dist/cjs/src/errors/associations/MissingRequiredBelongsToAssociation.js +8 -3
- package/dist/cjs/src/package-exports/errors.js +10 -1
- package/dist/esm/src/Dream.js +9 -5
- package/dist/esm/src/db/errors.js +2 -0
- package/dist/esm/src/decorators/Decorators.js +121 -6
- package/dist/esm/src/decorators/field/sortable/Sortable.js +4 -31
- package/dist/esm/src/decorators/field/sortable/helpers/cascadeReachesEveryRowInSortScope.js +51 -0
- package/dist/esm/src/decorators/field/sortable/helpers/performSortableDestroyWork.js +5 -2
- package/dist/esm/src/decorators/field/sortable/helpers/prepareSortableFieldsForDestroy.js +39 -25
- package/dist/esm/src/decorators/field/sortable/helpers/sortableCascadeEdge.js +32 -0
- package/dist/esm/src/dream/Query.js +37 -13
- package/dist/esm/src/dream/QueryDriver/Base.js +26 -0
- package/dist/esm/src/dream/QueryDriver/Kysely.js +4 -1
- package/dist/esm/src/dream/QueryDriver/Postgres.js +7 -1
- package/dist/esm/src/dream/internal/destroyAssociatedRecords.js +7 -0
- package/dist/esm/src/dream/internal/destroyDream.js +25 -14
- package/dist/esm/src/dream/internal/saveDream.js +9 -0
- package/dist/esm/src/dream/internal/undestroyDream.js +55 -32
- package/dist/esm/src/dream/internal/withConcurrentWriterRetry.js +64 -0
- package/dist/esm/src/errors/CannotSaveMissingDream.js +24 -0
- package/dist/esm/src/errors/SortableBatchRequiresTooManyScopeLocks.js +7 -0
- package/dist/esm/src/errors/associations/MissingRequiredBelongsToAssociation.js +8 -3
- package/dist/esm/src/package-exports/errors.js +10 -1
- package/dist/types/src/Dream.d.ts +9 -5
- package/dist/types/src/db/errors.d.ts +2 -0
- package/dist/types/src/decorators/Decorators.d.ts +121 -6
- package/dist/types/src/decorators/field/sortable/Sortable.d.ts +4 -31
- package/dist/types/src/decorators/field/sortable/helpers/cascadeReachesEveryRowInSortScope.d.ts +39 -0
- package/dist/types/src/decorators/field/sortable/helpers/performSortableDestroyWork.d.ts +6 -1
- package/dist/types/src/decorators/field/sortable/helpers/prepareSortableFieldsForDestroy.d.ts +37 -20
- package/dist/types/src/decorators/field/sortable/helpers/sortableCascadeEdge.d.ts +20 -0
- package/dist/types/src/dream/Query.d.ts +37 -13
- package/dist/types/src/dream/QueryDriver/Base.d.ts +20 -0
- package/dist/types/src/dream/QueryDriver/Postgres.d.ts +2 -0
- package/dist/types/src/dream/internal/destroyDream.d.ts +3 -1
- package/dist/types/src/dream/internal/undestroyDream.d.ts +6 -4
- package/dist/types/src/dream/internal/withConcurrentWriterRetry.d.ts +47 -0
- package/dist/types/src/errors/CannotSaveMissingDream.d.ts +23 -0
- package/dist/types/src/errors/SortableBatchRequiresTooManyScopeLocks.d.ts +7 -0
- package/dist/types/src/errors/associations/MissingRequiredBelongsToAssociation.d.ts +1 -0
- package/dist/types/src/package-exports/errors.d.ts +1 -1
- package/docs/assets/hierarchy.js +1 -1
- package/docs/assets/navigation.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/classes/db.DreamMigrationHelpers.html +11 -11
- package/docs/classes/db.KyselyQueryDriver.html +55 -43
- package/docs/classes/db.PostgresQueryDriver.html +56 -44
- package/docs/classes/db.QueryDriverBase.html +54 -42
- package/docs/classes/errors.CannotSaveMissingDream.html +47 -0
- package/docs/classes/errors.CheckConstraintViolation.html +3 -3
- package/docs/classes/errors.ColumnOverflow.html +3 -3
- package/docs/classes/errors.CreateOrFindByFailedToCreateAndFind.html +3 -3
- package/docs/classes/errors.DataIncompatibleWithDatabaseField.html +3 -3
- package/docs/classes/errors.DataTypeColumnTypeMismatch.html +3 -3
- package/docs/classes/errors.DecryptionError.html +2 -2
- package/docs/classes/errors.DecryptionParseError.html +2 -2
- package/docs/classes/errors.DecryptionRotationError.html +3 -3
- package/docs/classes/errors.GlobalNameNotSet.html +3 -3
- package/docs/classes/errors.InvalidCalendarDate.html +2 -2
- package/docs/classes/errors.InvalidClockTime.html +2 -2
- package/docs/classes/errors.InvalidClockTimeTz.html +2 -2
- package/docs/classes/errors.InvalidDateTime.html +2 -2
- package/docs/classes/errors.MissingSerializersDefinition.html +3 -3
- package/docs/classes/errors.NonLoadedAssociation.html +3 -3
- package/docs/classes/errors.NotNullViolation.html +3 -3
- package/docs/classes/errors.RecordNotFound.html +3 -3
- package/docs/classes/errors.SortableScopeDidNotStabilize.html +3 -3
- package/docs/classes/errors.SortableScopeLockWaitTimedOut.html +3 -3
- package/docs/classes/errors.ValidationError.html +3 -3
- package/docs/classes/index.CalendarDate.html +33 -33
- package/docs/classes/index.ClockTime.html +32 -32
- package/docs/classes/index.ClockTimeTz.html +35 -35
- package/docs/classes/index.DateTime.html +86 -86
- package/docs/classes/index.Decorators.html +101 -22
- package/docs/classes/index.Dream.html +133 -129
- package/docs/classes/index.DreamApp.html +10 -10
- package/docs/classes/index.DreamTransaction.html +2 -2
- package/docs/classes/index.Env.html +2 -2
- package/docs/classes/index.Query.html +88 -67
- package/docs/classes/system.CliFileWriter.html +4 -4
- package/docs/classes/system.DreamBin.html +2 -2
- package/docs/classes/system.DreamCLI.html +7 -7
- package/docs/classes/system.DreamImporter.html +2 -2
- package/docs/classes/system.DreamLogos.html +2 -2
- package/docs/classes/system.DreamSerializerBuilder.html +11 -11
- package/docs/classes/system.ObjectSerializerBuilder.html +8 -8
- package/docs/classes/system.PathHelpers.html +3 -3
- package/docs/classes/utils.Encrypt.html +3 -3
- package/docs/classes/utils.Range.html +2 -2
- package/docs/functions/db.closeAllDbConnections.html +1 -1
- package/docs/functions/db.dreamDbConnections.html +1 -1
- package/docs/functions/db.untypedDb.html +1 -1
- package/docs/functions/db.validateColumn.html +1 -1
- package/docs/functions/db.validateTable.html +1 -1
- package/docs/functions/errors.pgErrorType.html +1 -1
- package/docs/functions/index.DreamSerializer.html +1 -1
- package/docs/functions/index.ObjectSerializer.html +1 -1
- package/docs/functions/index.ReplicaSafe.html +1 -1
- package/docs/functions/index.STI.html +1 -1
- package/docs/functions/index.SoftDelete.html +1 -1
- package/docs/functions/utils.camelize.html +1 -1
- package/docs/functions/utils.capitalize.html +1 -1
- package/docs/functions/utils.cloneDeepSafe.html +1 -1
- package/docs/functions/utils.compact.html +1 -1
- package/docs/functions/utils.groupBy.html +1 -1
- package/docs/functions/utils.hyphenize.html +1 -1
- package/docs/functions/utils.intersection.html +1 -1
- package/docs/functions/utils.isEmpty.html +1 -1
- package/docs/functions/utils.normalizeUnicode.html +1 -1
- package/docs/functions/utils.pascalize.html +1 -1
- package/docs/functions/utils.percent.html +1 -1
- package/docs/functions/utils.range.html +1 -1
- package/docs/functions/utils.round.html +1 -1
- package/docs/functions/utils.sanitizeString.html +1 -1
- package/docs/functions/utils.snakeify.html +1 -1
- package/docs/functions/utils.sort.html +1 -1
- package/docs/functions/utils.sortBy.html +1 -1
- package/docs/functions/utils.sortObjectByKey.html +1 -1
- package/docs/functions/utils.sortObjectByValue.html +1 -1
- package/docs/functions/utils.uncapitalize.html +1 -1
- package/docs/functions/utils.uniq.html +1 -1
- package/docs/hierarchy.html +1 -1
- package/docs/interfaces/openapi.OpenapiDescription.html +2 -2
- package/docs/interfaces/openapi.OpenapiSchemaProperties.html +1 -1
- package/docs/interfaces/openapi.OpenapiSchemaPropertiesShorthand.html +1 -1
- package/docs/interfaces/openapi.OpenapiTypeFieldObject.html +1 -1
- package/docs/interfaces/types.BelongsToStatement.html +2 -2
- package/docs/interfaces/types.DecoratorContext.html +2 -2
- package/docs/interfaces/types.DreamAppInitOptions.html +2 -2
- package/docs/interfaces/types.DreamAppOpts.html +2 -2
- package/docs/interfaces/types.DreamDbConfig.html +5 -5
- package/docs/interfaces/types.DurationObject.html +2 -2
- package/docs/interfaces/types.EncryptOptions.html +2 -2
- package/docs/interfaces/types.InternalAnyTypedSerializerRendersMany.html +2 -2
- package/docs/interfaces/types.InternalAnyTypedSerializerRendersOne.html +2 -2
- package/docs/interfaces/types.SerializerRendererOpts.html +2 -2
- package/docs/modules/errors.html +1 -1
- package/docs/types/openapi.CommonOpenapiSchemaObjectFields.html +1 -1
- package/docs/types/openapi.OpenapiAllTypes.html +1 -1
- package/docs/types/openapi.OpenapiFormats.html +1 -1
- package/docs/types/openapi.OpenapiNumberFormats.html +1 -1
- package/docs/types/openapi.OpenapiPrimitiveBaseTypes.html +1 -1
- package/docs/types/openapi.OpenapiPrimitiveTypes.html +1 -1
- package/docs/types/openapi.OpenapiSchemaArray.html +1 -1
- package/docs/types/openapi.OpenapiSchemaArrayShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaBase.html +1 -1
- package/docs/types/openapi.OpenapiSchemaBody.html +1 -1
- package/docs/types/openapi.OpenapiSchemaBodyShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaCommonFields.html +1 -1
- package/docs/types/openapi.OpenapiSchemaExpressionAllOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaExpressionAnyOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaExpressionOneOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaExpressionRef.html +2 -2
- package/docs/types/openapi.OpenapiSchemaExpressionRefSchemaShorthand.html +2 -2
- package/docs/types/openapi.OpenapiSchemaInteger.html +1 -1
- package/docs/types/openapi.OpenapiSchemaNull.html +2 -2
- package/docs/types/openapi.OpenapiSchemaNumber.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObject.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAllOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAllOfShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAnyOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAnyOfShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectBase.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectBaseShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectOneOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectOneOfShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaPrimitiveGeneric.html +1 -1
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionAllOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionAnyOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionOneOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializableRef.html +2 -2
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializerRef.html +2 -2
- package/docs/types/openapi.OpenapiSchemaShorthandPrimitiveGeneric.html +1 -1
- package/docs/types/openapi.OpenapiSchemaString.html +1 -1
- package/docs/types/openapi.OpenapiShorthandAllTypes.html +1 -1
- package/docs/types/openapi.OpenapiShorthandPrimitiveBaseTypes.html +1 -1
- package/docs/types/openapi.OpenapiShorthandPrimitiveTypes.html +1 -1
- package/docs/types/openapi.OpenapiTypeField.html +1 -1
- package/docs/types/system.DreamAppAllowedPackageManagersEnum.html +1 -1
- package/docs/types/types.CalendarDateDurationUnit.html +1 -1
- package/docs/types/types.CalendarDateObject.html +1 -1
- package/docs/types/types.Camelized.html +1 -1
- package/docs/types/types.ClockTimeObject.html +1 -1
- package/docs/types/types.DbConnectionType.html +1 -1
- package/docs/types/types.DbTypes.html +1 -1
- package/docs/types/types.DreamAssociationMetadata.html +1 -1
- package/docs/types/types.DreamAttributes.html +1 -1
- package/docs/types/types.DreamClassAssociationAndStatement.html +1 -1
- package/docs/types/types.DreamClassColumn.html +1 -1
- package/docs/types/types.DreamColumn.html +1 -1
- package/docs/types/types.DreamColumnNames.html +1 -1
- package/docs/types/types.DreamLogLevel.html +1 -1
- package/docs/types/types.DreamLogger.html +2 -2
- package/docs/types/types.DreamModelSerializerType.html +1 -1
- package/docs/types/types.DreamOrViewModelClassSerializerKey.html +1 -1
- package/docs/types/types.DreamOrViewModelSerializerKey.html +1 -1
- package/docs/types/types.DreamParamSafeAttributes.html +1 -1
- package/docs/types/types.DreamParamSafeColumnNames.html +1 -1
- package/docs/types/types.DreamSerializable.html +1 -1
- package/docs/types/types.DreamSerializableArray.html +1 -1
- package/docs/types/types.DreamSerializerKey.html +1 -1
- package/docs/types/types.DreamSerializers.html +1 -1
- package/docs/types/types.DreamVirtualColumns.html +1 -1
- package/docs/types/types.DurationUnit.html +1 -1
- package/docs/types/types.EncryptAlgorithm.html +1 -1
- package/docs/types/types.HasManyStatement.html +1 -1
- package/docs/types/types.HasOneStatement.html +1 -1
- package/docs/types/types.Hyphenized.html +1 -1
- package/docs/types/types.Pascalized.html +1 -1
- package/docs/types/types.PrimaryKeyType.html +1 -1
- package/docs/types/types.RoundingPrecision.html +1 -1
- package/docs/types/types.SerializerCasing.html +1 -1
- package/docs/types/types.SimpleObjectSerializerType.html +1 -1
- package/docs/types/types.Snakeified.html +1 -1
- package/docs/types/types.StrictInterface.html +1 -1
- package/docs/types/types.UpdateableAssociationProperties.html +1 -1
- package/docs/types/types.UpdateableProperties.html +1 -1
- package/docs/types/types.ValidationType.html +1 -1
- package/docs/types/types.ViewModel.html +2 -2
- package/docs/types/types.ViewModelClass.html +1 -1
- package/docs/types/types.WeekdayName.html +1 -1
- package/docs/types/types.WhereStatementForDream.html +1 -1
- package/docs/types/types.WhereStatementForDreamClass.html +1 -1
- package/docs/variables/index.DreamConst.html +1 -1
- package/docs/variables/index.ops.html +1 -1
- package/docs/variables/openapi.openapiPrimitiveTypes.html +1 -1
- package/docs/variables/openapi.openapiShorthandPrimitiveTypes.html +1 -1
- package/docs/variables/system.DreamAppAllowedPackageManagersEnumValues.html +1 -1
- package/docs/variables/system.primaryKeyTypes.html +1 -1
- package/package.json +2 -2
- package/docs/classes/errors.SortableBatchRequiresTooManyScopeLocks.html +0 -61
package/dist/types/src/decorators/field/sortable/helpers/prepareSortableFieldsForDestroy.d.ts
CHANGED
|
@@ -1,33 +1,50 @@
|
|
|
1
1
|
import Dream from '../../../../Dream.js';
|
|
2
2
|
import DreamTransaction from '../../../../dream/DreamTransaction.js';
|
|
3
|
+
import { SortableFieldConfig } from '../Sortable.js';
|
|
4
|
+
import { SortableCascadeEdge } from './sortableCascadeEdge.js';
|
|
5
|
+
export interface SortableDestroyPreparation {
|
|
6
|
+
/**
|
|
7
|
+
* False when the snapshot read found no row: this destroy has nothing to
|
|
8
|
+
* vacate, and compacting from the instance's remembered position would shift
|
|
9
|
+
* a scope some other writer has already closed. The delete's own affected-row
|
|
10
|
+
* count is the stronger signal (a writer outside the sortable path can remove
|
|
11
|
+
* the row after this read), so the caller checks both.
|
|
12
|
+
*/
|
|
13
|
+
rowExists: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* The sortable fields whose scope this destroy compacts, for
|
|
16
|
+
* `performSortableDestroyWork`.
|
|
17
|
+
*/
|
|
18
|
+
sortableFields: SortableFieldConfig[];
|
|
19
|
+
}
|
|
3
20
|
/**
|
|
4
21
|
* @internal
|
|
5
22
|
*
|
|
6
23
|
* The sortable preparation phase of a destroy. Called from `destroyDream` after
|
|
7
24
|
* every user `beforeDestroy` hook has run and before the delete — a phase, not
|
|
8
|
-
* a hook among them — so the
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
25
|
+
* a hook among them — so the row's real position and scope values are read in
|
|
26
|
+
* one snapshot SELECT covering every field while the row still exists.
|
|
27
|
+
* Pre-delete is the only seat for that read: on a hard destroy the row is gone
|
|
28
|
+
* afterwards, and a soft destroy nulls the position columns in the same UPDATE
|
|
29
|
+
* as `deletedAt`. The per-field compactions (`performSortableDestroyWork`)
|
|
30
|
+
* consume the cached snapshots.
|
|
13
31
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* the delete and the after-destroy
|
|
32
|
+
* A direct destroy takes the operation's whole advisory key set — every
|
|
33
|
+
* sortable field — in one sorted stabilized acquisition around that read, so
|
|
34
|
+
* the snapshot cannot be invalidated before the compaction; per-field
|
|
35
|
+
* acquisition would take multi-field keys in declaration order and deadlock
|
|
36
|
+
* against the paths that acquire sorted. The lock is transaction-scoped, and
|
|
37
|
+
* `destroyDream` opens a transaction spanning the delete and the after-destroy
|
|
38
|
+
* hooks, so one acquisition covers both.
|
|
20
39
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* affected-row count is the stronger signal (a writer outside the sortable
|
|
28
|
-
* path can remove the row after this read), so the caller checks both.
|
|
40
|
+
* A cascaded destroy — one reached through a `dependent: 'destroy'` association,
|
|
41
|
+
* which passes the edge it arrived by — takes no scope lock: a cascade would
|
|
42
|
+
* otherwise hold one per distinct sort scope it touches until the root
|
|
43
|
+
* transaction commits. It reads the same snapshot unlocked and compacts from
|
|
44
|
+
* it, and skips both for a field whose whole sort scope the cascade is
|
|
45
|
+
* destroying, since nothing survives there to observe a compaction.
|
|
29
46
|
*/
|
|
30
|
-
export default function prepareSortableFieldsForDestroy(dream: Dream, txn: DreamTransaction<any
|
|
47
|
+
export default function prepareSortableFieldsForDestroy(dream: Dream, txn: DreamTransaction<any>, cascadeEdge: SortableCascadeEdge | null): Promise<SortableDestroyPreparation>;
|
|
31
48
|
/**
|
|
32
49
|
* @internal
|
|
33
50
|
*
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import Dream from '../../../../Dream.js';
|
|
2
|
+
import { HasStatement } from '../../../../types/associations/shared.js';
|
|
3
|
+
export type SortableCascadeEdge = HasStatement<any, any, any, any, 'HasOne' | 'HasMany'>;
|
|
4
|
+
/**
|
|
5
|
+
* @internal
|
|
6
|
+
*
|
|
7
|
+
* Marks a record a `dependent: 'destroy'` cascade is about to destroy or
|
|
8
|
+
* undestroy, with the association the cascade reached it through. The record's
|
|
9
|
+
* own destroy or undestroy consumes the mark and does its sortable work without
|
|
10
|
+
* the scope lock a direct operation takes.
|
|
11
|
+
*/
|
|
12
|
+
export declare function markSortableCascadeEdge(dream: Dream, association: SortableCascadeEdge): void;
|
|
13
|
+
/**
|
|
14
|
+
* @internal
|
|
15
|
+
*
|
|
16
|
+
* The association a cascade reached this record through, or null on a direct
|
|
17
|
+
* destroy or undestroy. Consuming removes the mark, so a later direct operation
|
|
18
|
+
* on the same instance cannot inherit a cascade's optimism.
|
|
19
|
+
*/
|
|
20
|
+
export declare function consumeSortableCascadeEdge(dream: Dream): SortableCascadeEdge | null;
|
|
@@ -1356,13 +1356,15 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
|
|
|
1356
1356
|
* {@link Query.delete | delete} (a single statement, no hooks or cascade)
|
|
1357
1357
|
* are the right tools for bulk removal.
|
|
1358
1358
|
*
|
|
1359
|
+
* To bypass a default scope, chain `removeDefaultScope` onto the Query
|
|
1360
|
+
* before calling this method; it is Query state, not an option, and it
|
|
1361
|
+
* carries through to the cascade.
|
|
1362
|
+
*
|
|
1359
1363
|
* @param options - Options for destroying the instance
|
|
1360
1364
|
* @param options.skipHooks - If true, skips applying model hooks during the destroy operation. Defaults to false
|
|
1361
1365
|
* @param options.cascade - If false, skips destroying associations marked `dependent: 'destroy'`. Defaults to true
|
|
1362
1366
|
* @param options.lock - If true, each batch is re-selected with an exclusive row lock inside its own transaction before being destroyed, making the destroy a compare-and-set. Defaults to false
|
|
1363
1367
|
* @param options.batchSize - The number of records to process per batch. Must be a positive integer. Defaults to 10 when `lock` is true, and to 1000 otherwise
|
|
1364
|
-
* @param options.bypassAllDefaultScopes - If true, bypasses user-removable default scopes when cascade destroying; an STI child's reserved discriminator remains enforced, including when associations are loaded. Defaults to false
|
|
1365
|
-
* @param options.defaultScopesToBypass - An array of default scope names to bypass when cascade destroying. Defaults to an empty array
|
|
1366
1368
|
* @returns The number of records that were removed
|
|
1367
1369
|
* @throws InvalidBatchSize if `batchSize` is not a positive integer
|
|
1368
1370
|
* @throws BatchingIncompatibleWithLimitOrOffset if the query carries a `limit` or `offset`
|
|
@@ -1433,11 +1435,12 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
|
|
|
1433
1435
|
* transaction ends, so every batch's scope keys are still held when the
|
|
1434
1436
|
* next one preflights. That accumulation is bounded — the preflight counts
|
|
1435
1437
|
* the keys the transaction already holds along with the ones the batch
|
|
1436
|
-
* would add, and
|
|
1437
|
-
*
|
|
1438
|
-
*
|
|
1439
|
-
*
|
|
1440
|
-
* releases the keys as it goes and gives
|
|
1438
|
+
* would add, and raises rather than letting a long run over a
|
|
1439
|
+
* high-cardinality sort scope exhaust `max_locks_per_transaction` for the
|
|
1440
|
+
* whole cluster — so a run long enough raises instead of completing. That
|
|
1441
|
+
* refusal is a sizing failure to fix, not a condition to catch: letting
|
|
1442
|
+
* Dream open a transaction per batch releases the keys as it goes and gives
|
|
1443
|
+
* each batch the whole budget, and a smaller `batchSize` takes fewer.
|
|
1441
1444
|
* - the preflight covers **the claimed rows' own scopes only**. A
|
|
1442
1445
|
* `dependent: 'destroy'` cascade under `destroy({ lock: true })` destroys
|
|
1443
1446
|
* child records of other tables, and a sortable child takes its own table's
|
|
@@ -1454,8 +1457,18 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
|
|
|
1454
1457
|
private lockedBatches;
|
|
1455
1458
|
/**
|
|
1456
1459
|
* Destroys all records matching the Query,
|
|
1457
|
-
*
|
|
1458
|
-
*
|
|
1460
|
+
* permanently removing each matched record from
|
|
1461
|
+
* the database instead of soft deleting it.
|
|
1462
|
+
*
|
|
1463
|
+
* The SoftDelete decorator is bypassed when
|
|
1464
|
+
* deleting each matched record, not when
|
|
1465
|
+
* matching: the Query selects under its default
|
|
1466
|
+
* scopes, `dream:SoftDelete` among them, so on a
|
|
1467
|
+
* SoftDelete model it matches only records that
|
|
1468
|
+
* have not already been soft deleted. Reaching
|
|
1469
|
+
* soft deleted records takes
|
|
1470
|
+
* `removeDefaultScope('dream:SoftDelete')` on
|
|
1471
|
+
* the Query.
|
|
1459
1472
|
*
|
|
1460
1473
|
* Calls model hooks and applies cascade destroy
|
|
1461
1474
|
* to associations with `dependent: 'destroy'`,
|
|
@@ -1470,17 +1483,26 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
|
|
|
1470
1483
|
* // 12
|
|
1471
1484
|
* ```
|
|
1472
1485
|
*
|
|
1486
|
+
* ```ts
|
|
1487
|
+
* await User.removeDefaultScope('dream:SoftDelete')
|
|
1488
|
+
* .where({ email: ops.ilike('%burpcollaborator%') })
|
|
1489
|
+
* .reallyDestroy()
|
|
1490
|
+
* // 12
|
|
1491
|
+
* ```
|
|
1492
|
+
*
|
|
1473
1493
|
* A `limit` or `offset` on the Query is incompatible with `reallyDestroy`,
|
|
1474
1494
|
* exactly as it is with {@link Query.destroy}: a limit- or offset-carrying
|
|
1475
1495
|
* Query throws at runtime.
|
|
1476
1496
|
*
|
|
1497
|
+
* To bypass a default scope, chain `removeDefaultScope` onto the Query
|
|
1498
|
+
* before calling this method; it is Query state, not an option, and it
|
|
1499
|
+
* carries through to the cascade.
|
|
1500
|
+
*
|
|
1477
1501
|
* @param options - Options for destroying the instance
|
|
1478
1502
|
* @param options.skipHooks - If true, skips applying model hooks during the destroy operation. Defaults to false
|
|
1479
1503
|
* @param options.cascade - If false, skips destroying associations marked `dependent: 'destroy'`. Defaults to true
|
|
1480
1504
|
* @param options.lock - If true, each batch is re-selected with an exclusive row lock inside its own transaction before being destroyed, making the destroy a compare-and-set. See {@link Query.destroy} for the full semantics. Defaults to false
|
|
1481
1505
|
* @param options.batchSize - The number of records to process per batch. Must be a positive integer. Defaults to 10 when `lock` is true, and to 1000 otherwise
|
|
1482
|
-
* @param options.bypassAllDefaultScopes - If true, bypasses user-removable default scopes when cascade destroying; an STI child's reserved discriminator remains enforced, including when associations are loaded. Defaults to false
|
|
1483
|
-
* @param options.defaultScopesToBypass - An array of default scope names to bypass when cascade destroying. Defaults to an empty array
|
|
1484
1506
|
* @returns The number of records that were removed
|
|
1485
1507
|
* @throws InvalidBatchSize if `batchSize` is not a positive integer
|
|
1486
1508
|
* @throws BatchingIncompatibleWithLimitOrOffset if the query carries a `limit` or `offset`
|
|
@@ -1509,11 +1531,13 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
|
|
|
1509
1531
|
* re-applied to every window — so a limit- or offset-carrying Query throws
|
|
1510
1532
|
* at runtime.
|
|
1511
1533
|
*
|
|
1534
|
+
* To bypass a default scope, chain `removeDefaultScope` onto the Query
|
|
1535
|
+
* before calling this method; it is Query state, not an option, and it
|
|
1536
|
+
* carries through to the cascade. The SoftDelete scope is always bypassed.
|
|
1537
|
+
*
|
|
1512
1538
|
* @param options - Options for undestroying the instance
|
|
1513
1539
|
* @param options.skipHooks - If true, skips applying model hooks during the undestroy operation. Defaults to false
|
|
1514
1540
|
* @param options.cascade - If false, skips undestroying associations marked `dependent: 'destroy'`. Defaults to true
|
|
1515
|
-
* @param options.bypassAllDefaultScopes - If true, bypasses user-removable default scopes when cascade undestroying; an STI child's reserved discriminator remains enforced, including when associations are loaded. Defaults to false
|
|
1516
|
-
* @param options.defaultScopesToBypass - An array of default scope names to bypass when cascade undestroying (soft delete is always bypassed). Defaults to an empty array
|
|
1517
1541
|
* @returns The number of records that were removed
|
|
1518
1542
|
* @throws BatchingIncompatibleWithLimitOrOffset if the query carries a `limit` or `offset`
|
|
1519
1543
|
*/
|
|
@@ -162,6 +162,26 @@ export default class QueryDriverBase<DreamInstance extends Dream> {
|
|
|
162
162
|
* than silently racing on position values.
|
|
163
163
|
*/
|
|
164
164
|
static supportsAdvisoryTransactionLocks: boolean;
|
|
165
|
+
/**
|
|
166
|
+
* @internal
|
|
167
|
+
*
|
|
168
|
+
* Whether `error` is the database refusing a write for violating a unique
|
|
169
|
+
* constraint. `withConcurrentWriterRetry` asks this of an error raised at
|
|
170
|
+
* COMMIT, which is where a `DEFERRABLE INITIALLY DEFERRED` constraint — the
|
|
171
|
+
* one `DreamMigrationHelpers.addDeferrableUniqueConstraint` declares — reports
|
|
172
|
+
* a collision. A driver that does not recognize one reports false, and nothing
|
|
173
|
+
* is retried.
|
|
174
|
+
*/
|
|
175
|
+
static isUniqueConstraintViolation(error: unknown): boolean;
|
|
176
|
+
/**
|
|
177
|
+
* @internal
|
|
178
|
+
*
|
|
179
|
+
* Whether `error` is the database aborting a transaction to break a deadlock
|
|
180
|
+
* with another. `withConcurrentWriterRetry` reruns a transaction of Dream's
|
|
181
|
+
* own that was aborted this way. A driver that does not recognize one reports
|
|
182
|
+
* false, and nothing is retried.
|
|
183
|
+
*/
|
|
184
|
+
static isDeadlock(error: unknown): boolean;
|
|
165
185
|
/**
|
|
166
186
|
* @internal
|
|
167
187
|
*
|
|
@@ -56,6 +56,8 @@ export default class PostgresQueryDriver<DreamInstance extends Dream> extends Ky
|
|
|
56
56
|
*/
|
|
57
57
|
applyRowLock(kyselyQuery: SelectQueryBuilder<any, any, any>, tableAlias: string): SelectQueryBuilder<any, any, any>;
|
|
58
58
|
static supportsAdvisoryTransactionLocks: boolean;
|
|
59
|
+
static isUniqueConstraintViolation(error: unknown): boolean;
|
|
60
|
+
static isDeadlock(error: unknown): boolean;
|
|
59
61
|
/**
|
|
60
62
|
* @internal
|
|
61
63
|
*
|
|
@@ -10,7 +10,9 @@ export interface ReallyDestroyOptions<DreamInstance extends Dream> extends Destr
|
|
|
10
10
|
*
|
|
11
11
|
* Destroys the Dream and any `dependent: 'destroy'` associations
|
|
12
12
|
* within a transaction. If a transaction is passed, it will be used.
|
|
13
|
-
* Otherwise, a new transaction will be created automatically
|
|
13
|
+
* Otherwise, a new transaction will be created automatically, and the
|
|
14
|
+
* whole destroy is run again if that transaction is undone by a concurrent
|
|
15
|
+
* writer (see `withConcurrentWriterRetry`).
|
|
14
16
|
* If any of the nested associations fails to destroy, then this
|
|
15
17
|
* record will also fail to destroy. If skipHooks is true, model hooks
|
|
16
18
|
* will be bypassed.
|
|
@@ -5,11 +5,13 @@ type UndestroyOptions<DreamInstance extends Dream> = Required<OptionalDestroyOpt
|
|
|
5
5
|
/**
|
|
6
6
|
* @internal
|
|
7
7
|
*
|
|
8
|
-
*
|
|
8
|
+
* Undestroys the Dream and any `dependent: 'destroy'` associations
|
|
9
9
|
* within a transaction. If a transaction is passed, it will be used.
|
|
10
|
-
* Otherwise, a new transaction will be created automatically
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* Otherwise, a new transaction will be created automatically, and the
|
|
11
|
+
* whole undestroy is run again if that transaction is undone by a
|
|
12
|
+
* concurrent writer (see `withConcurrentWriterRetry`).
|
|
13
|
+
* If any of the nested associations fails to undestroy, then this
|
|
14
|
+
* record will also fail to undestroy. If skipHooks is true, model hooks
|
|
13
15
|
* will be bypassed.
|
|
14
16
|
*/
|
|
15
17
|
export default function undestroyDream<I extends Dream>(dream: I, txn: (DreamTransaction<I> | null) | undefined, options: UndestroyOptions<I>): Promise<I>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import Dream from '../../Dream.js';
|
|
2
|
+
import DreamTransaction from '../DreamTransaction.js';
|
|
3
|
+
/**
|
|
4
|
+
* How many times a destroy or undestroy that opened its own transaction runs
|
|
5
|
+
* before a failure caused by a concurrent writer escapes. One such failure
|
|
6
|
+
* needs another writer of the same rows or sort scope during the operation;
|
|
7
|
+
* three in a row is contention past the point of retrying.
|
|
8
|
+
*/
|
|
9
|
+
export declare const MAX_CONCURRENT_WRITER_ATTEMPTS = 3;
|
|
10
|
+
/**
|
|
11
|
+
* @internal
|
|
12
|
+
*
|
|
13
|
+
* Runs `callback` in a transaction of the model's own, and runs it again when
|
|
14
|
+
* that transaction is undone by a concurrent writer: a deadlock, or a unique
|
|
15
|
+
* constraint refusing the COMMIT.
|
|
16
|
+
*
|
|
17
|
+
* A cascaded destroy or undestroy does its sortable position work without the
|
|
18
|
+
* scope lock a direct one takes (see `@deco.Sortable`), so a concurrent writer
|
|
19
|
+
* of the same sort scope can leave two live rows sharing a position, which the
|
|
20
|
+
* scope's deferrable unique constraint refuses at COMMIT — or, when both
|
|
21
|
+
* writers reach COMMIT together, as a deadlock between their two checks. A
|
|
22
|
+
* cascade holding row locks can deadlock against another writer of the same
|
|
23
|
+
* rows mid-statement as well. Either way the database has undone the whole
|
|
24
|
+
* transaction, nothing has committed and no commit hook has run, so running
|
|
25
|
+
* the operation again reads the rows as the other writer left them. The last
|
|
26
|
+
* attempt's error is the adapter's own.
|
|
27
|
+
*
|
|
28
|
+
* A unique violation raised by a statement inside the callback is not a
|
|
29
|
+
* collision but a write that violates a constraint outright, and is not
|
|
30
|
+
* retried; only a refusal of the COMMIT itself is, which is why the callback
|
|
31
|
+
* having resolved is part of the test.
|
|
32
|
+
*
|
|
33
|
+
* Only a transaction Dream opens is retried. Inside a caller's transaction the
|
|
34
|
+
* failure is the caller's, and the caller retries the whole transaction.
|
|
35
|
+
*
|
|
36
|
+
* Saves are deliberately not run through this. When a direct save and a
|
|
37
|
+
* cascade collide, the loser is whichever transaction waits first at COMMIT,
|
|
38
|
+
* and that is usually the save, which is short and reaches COMMIT while the
|
|
39
|
+
* cascade is still working: it fails with the adapter's deadlock error after
|
|
40
|
+
* `deadlock_timeout`. That bounded, occasional failure is the accepted price of
|
|
41
|
+
* cascades holding no scope locks, and the save path's state restore
|
|
42
|
+
* (`saveDream`) already makes calling `save` again correct. The collision needs
|
|
43
|
+
* a position write into the same sort scope inside the cascade's window that
|
|
44
|
+
* picks the same position, which a cascade destroying a scope's owner never
|
|
45
|
+
* exposes since it writes no position at all.
|
|
46
|
+
*/
|
|
47
|
+
export default function withConcurrentWriterRetry<R>(dreamClass: typeof Dream, callback: (txn: DreamTransaction<any>) => Promise<R>): Promise<R>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import Dream from '../Dream.js';
|
|
2
|
+
/**
|
|
3
|
+
* Raised when saving a persisted instance whose database row no longer exists —
|
|
4
|
+
* the row was deleted between the moment the instance was loaded and the moment
|
|
5
|
+
* the save reached the database.
|
|
6
|
+
*
|
|
7
|
+
* This is the write-side counterpart of `RecordNotFound`, and applications catch
|
|
8
|
+
* it for the same reasons: a request updating a record another request has since
|
|
9
|
+
* deleted should answer 404 or 409 rather than 500, and a background job holding
|
|
10
|
+
* a stale instance should usually drop the work rather than fail loudly. Nothing
|
|
11
|
+
* is written when it is raised.
|
|
12
|
+
*
|
|
13
|
+
* It does not mean the save was rejected — a save that fails validation raises
|
|
14
|
+
* `ValidationError`, and a constraint violation raises its own error. It means
|
|
15
|
+
* the row that the instance stands for is gone.
|
|
16
|
+
*
|
|
17
|
+
* Dream raises it from the query driver, so a custom driver overriding
|
|
18
|
+
* `saveDream` translates its own adapter's empty-result error the same way, as
|
|
19
|
+
* the test-app MySQL driver does.
|
|
20
|
+
*/
|
|
21
|
+
export default class CannotSaveMissingDream extends Error {
|
|
22
|
+
constructor(dream: Dream);
|
|
23
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* @internal
|
|
3
|
+
*
|
|
2
4
|
* Raised when a batch of a locked `Query#destroy` or `Query#update` on a
|
|
3
5
|
* sortable model would carry its transaction past the number of advisory
|
|
4
6
|
* sort-scope locks Dream is willing to hold at once.
|
|
@@ -27,6 +29,11 @@
|
|
|
27
29
|
* — a long multi-batch run inside a transaction you opened — either let Dream
|
|
28
30
|
* open a transaction per batch (do not pass a transaction of your own) or
|
|
29
31
|
* commit between runs, which is what releases the locks as it goes.
|
|
32
|
+
*
|
|
33
|
+
* Not exported from `@rvoh/dream/errors`. Exceeding the bound is a sizing
|
|
34
|
+
* mistake in the calling code, not a runtime condition an application can
|
|
35
|
+
* recover from, so there is nothing for a consumer to catch: the fix is a
|
|
36
|
+
* smaller batch or a narrower transaction, never a retry.
|
|
30
37
|
*/
|
|
31
38
|
export default class SortableBatchRequiresTooManyScopeLocks extends Error {
|
|
32
39
|
dreamClassName: string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { pgErrorType } from '../db/errors.js';
|
|
2
2
|
export { default as NonLoadedAssociation } from '../errors/associations/NonLoadedAssociation.js';
|
|
3
|
+
export { default as CannotSaveMissingDream } from '../errors/CannotSaveMissingDream.js';
|
|
3
4
|
export { default as CreateOrFindByFailedToCreateAndFind } from '../errors/CreateOrFindByFailedToCreateAndFind.js';
|
|
4
5
|
export { default as CheckConstraintViolation } from '../errors/db/CheckConstraintViolation.js';
|
|
5
6
|
export { default as ColumnOverflow } from '../errors/db/ColumnOverflow.js';
|
|
@@ -12,7 +13,6 @@ export { default as DecryptionParseError } from '../errors/encrypt/DecryptionPar
|
|
|
12
13
|
export { default as DecryptionRotationError } from '../errors/encrypt/DecryptionRotationError.js';
|
|
13
14
|
export { default as RecordNotFound } from '../errors/RecordNotFound.js';
|
|
14
15
|
export { default as MissingSerializersDefinition } from '../errors/serializers/MissingSerializersDefinition.js';
|
|
15
|
-
export { default as SortableBatchRequiresTooManyScopeLocks } from '../errors/SortableBatchRequiresTooManyScopeLocks.js';
|
|
16
16
|
export { default as SortableScopeDidNotStabilize } from '../errors/SortableScopeDidNotStabilize.js';
|
|
17
17
|
export { default as SortableScopeLockWaitTimedOut } from '../errors/SortableScopeLockWaitTimedOut.js';
|
|
18
18
|
export { default as ValidationError } from '../errors/ValidationError.js';
|
package/docs/assets/hierarchy.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
window.hierarchyData = "
|
|
1
|
+
window.hierarchyData = "eJyVk0FPwyAUx7/LO+McuLCVo1tMjHFqYvSw7MDaN0tGYQE60yz77gZ3wTktO0HJ+/P7Aa97cNYGD2JB+YgRSumSgMO1xjIoazyIPVBK42BkgyDgpUXXzZzaobuVHoHARpkKBGUTAq3TIKDU0nv019VqcFI9qEOjgRwrQEDw1VWMXx0XCJS10pVDA2LBimJ5IMCKIqE/dB51l+zaw/9Vf4kBvWGTqBDHxOHZ+vDh0OdbnEn0eEQqH/KEOq2x3Eyt8cFJZcKbslrGJ/oPjc5Z5wd/RXMc2Ch1sLptzNMO3Vrbzxzyj0AOb8QS3kwGeW9K22xlUCuN7yrUcW0lPd4p1FWGQu8eF3UEH3ISL4VQzimhfPzdo/HjRPu12+Lx9HH2qHwjQ1ln+p4P51zfOP1Z5jbMW60vaZXTSB/zcPgCAPBvFw=="
|
|
@@ -1 +1 @@
|
|
|
1
|
-
window.navigationData = "eJytm01z4zYShv+
|
|
1
|
+
window.navigationData = "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"
|