@rvoh/dream 2.27.0 → 2.28.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 +32 -0
- package/dist/cjs/src/db/errors.js +4 -0
- package/dist/cjs/src/decorators/field/sortable/Sortable.js +43 -118
- package/dist/cjs/src/decorators/field/sortable/helpers/acquireSortableScopeLocks.js +38 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/acquireStabilizedSortableBatchLocks.js +183 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/acquireStabilizedSortableScopeLocks.js +40 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/canonicalScopeValue.js +194 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/clearCachedSortableValues.js +18 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/decrementScopedRecordsGreaterThanPosition.js +9 -9
- package/dist/cjs/src/decorators/field/sortable/helpers/filterQueryToInstanceScope.js +20 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/filterQueryToScopeValues.js +32 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/heldSortableScopeLockKeys.js +34 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/performSortableDestroyWork.js +21 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/performSortablePositionWork.js +36 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/prepareSortableFieldsForDestroy.js +56 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/prepareSortableFieldsForSave.js +84 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/resortAllRecords.js +186 -29
- package/dist/cjs/src/decorators/field/sortable/helpers/setPosition.js +90 -46
- package/dist/cjs/src/decorators/field/sortable/helpers/sortableRowCache.js +73 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/sortableScopeColumns.js +42 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/sortableScopeLockKey.js +99 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/sortableScopeLockKeys.js +72 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/sortableSnapshot.js +101 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/stabilizeSortableScopeLocks.js +86 -0
- package/dist/cjs/src/decorators/field/sortable/helpers/validPosition.js +21 -4
- package/dist/cjs/src/decorators/field/sortable/hooks/afterSortableDestroy.js +24 -7
- package/dist/cjs/src/decorators/field/sortable/hooks/afterSortableUpdate.js +10 -0
- package/dist/cjs/src/dream/Query.js +64 -9
- package/dist/cjs/src/dream/QueryDriver/Base.js +75 -2
- package/dist/cjs/src/dream/QueryDriver/Kysely.js +11 -4
- package/dist/cjs/src/dream/QueryDriver/Postgres.js +74 -0
- package/dist/cjs/src/dream/internal/destroyDream.js +60 -6
- package/dist/cjs/src/dream/internal/saveDream.js +109 -7
- package/dist/cjs/src/dream/internal/similarity/SimilarityBuilder.js +19 -4
- package/dist/cjs/src/dream/internal/softDeleteDream.js +13 -1
- package/dist/cjs/src/dream/internal/undestroyDream.js +56 -8
- package/dist/cjs/src/dream-app/index.js +21 -0
- package/dist/cjs/src/errors/SortableBatchRequiresTooManyScopeLocks.js +57 -0
- package/dist/cjs/src/errors/SortableRequiresAdvisoryTransactionLocks.js +26 -0
- package/dist/cjs/src/errors/SortableScopeDidNotStabilize.js +39 -0
- package/dist/cjs/src/errors/SortableScopeLockWaitTimedOut.js +50 -0
- package/dist/cjs/src/package-exports/errors.js +3 -0
- package/dist/esm/src/Dream.js +32 -0
- package/dist/esm/src/db/errors.js +4 -0
- package/dist/esm/src/decorators/field/sortable/Sortable.js +43 -118
- package/dist/esm/src/decorators/field/sortable/helpers/acquireSortableScopeLocks.js +38 -0
- package/dist/esm/src/decorators/field/sortable/helpers/acquireStabilizedSortableBatchLocks.js +183 -0
- package/dist/esm/src/decorators/field/sortable/helpers/acquireStabilizedSortableScopeLocks.js +40 -0
- package/dist/esm/src/decorators/field/sortable/helpers/canonicalScopeValue.js +194 -0
- package/dist/esm/src/decorators/field/sortable/helpers/clearCachedSortableValues.js +18 -0
- package/dist/esm/src/decorators/field/sortable/helpers/decrementScopedRecordsGreaterThanPosition.js +9 -9
- package/dist/esm/src/decorators/field/sortable/helpers/filterQueryToInstanceScope.js +20 -0
- package/dist/esm/src/decorators/field/sortable/helpers/filterQueryToScopeValues.js +32 -0
- package/dist/esm/src/decorators/field/sortable/helpers/heldSortableScopeLockKeys.js +34 -0
- package/dist/esm/src/decorators/field/sortable/helpers/performSortableDestroyWork.js +21 -0
- package/dist/esm/src/decorators/field/sortable/helpers/performSortablePositionWork.js +36 -0
- package/dist/esm/src/decorators/field/sortable/helpers/prepareSortableFieldsForDestroy.js +56 -0
- package/dist/esm/src/decorators/field/sortable/helpers/prepareSortableFieldsForSave.js +84 -0
- package/dist/esm/src/decorators/field/sortable/helpers/resortAllRecords.js +186 -29
- package/dist/esm/src/decorators/field/sortable/helpers/setPosition.js +90 -46
- package/dist/esm/src/decorators/field/sortable/helpers/sortableRowCache.js +73 -0
- package/dist/esm/src/decorators/field/sortable/helpers/sortableScopeColumns.js +42 -0
- package/dist/esm/src/decorators/field/sortable/helpers/sortableScopeLockKey.js +99 -0
- package/dist/esm/src/decorators/field/sortable/helpers/sortableScopeLockKeys.js +72 -0
- package/dist/esm/src/decorators/field/sortable/helpers/sortableSnapshot.js +101 -0
- package/dist/esm/src/decorators/field/sortable/helpers/stabilizeSortableScopeLocks.js +86 -0
- package/dist/esm/src/decorators/field/sortable/helpers/validPosition.js +21 -4
- package/dist/esm/src/decorators/field/sortable/hooks/afterSortableDestroy.js +24 -7
- package/dist/esm/src/decorators/field/sortable/hooks/afterSortableUpdate.js +10 -0
- package/dist/esm/src/dream/Query.js +64 -9
- package/dist/esm/src/dream/QueryDriver/Base.js +75 -2
- package/dist/esm/src/dream/QueryDriver/Kysely.js +11 -4
- package/dist/esm/src/dream/QueryDriver/Postgres.js +74 -0
- package/dist/esm/src/dream/internal/destroyDream.js +60 -6
- package/dist/esm/src/dream/internal/saveDream.js +109 -7
- package/dist/esm/src/dream/internal/similarity/SimilarityBuilder.js +19 -4
- package/dist/esm/src/dream/internal/softDeleteDream.js +13 -1
- package/dist/esm/src/dream/internal/undestroyDream.js +56 -8
- package/dist/esm/src/dream-app/index.js +21 -0
- package/dist/esm/src/errors/SortableBatchRequiresTooManyScopeLocks.js +57 -0
- package/dist/esm/src/errors/SortableRequiresAdvisoryTransactionLocks.js +26 -0
- package/dist/esm/src/errors/SortableScopeDidNotStabilize.js +39 -0
- package/dist/esm/src/errors/SortableScopeLockWaitTimedOut.js +50 -0
- package/dist/esm/src/package-exports/errors.js +3 -0
- package/dist/types/src/Dream.d.ts +32 -0
- package/dist/types/src/db/errors.d.ts +2 -0
- package/dist/types/src/decorators/field/sortable/Sortable.d.ts +33 -0
- package/dist/types/src/decorators/field/sortable/helpers/acquireSortableScopeLocks.d.ts +25 -0
- package/dist/types/src/decorators/field/sortable/helpers/acquireStabilizedSortableBatchLocks.d.ts +91 -0
- package/dist/types/src/decorators/field/sortable/helpers/acquireStabilizedSortableScopeLocks.d.ts +31 -0
- package/dist/types/src/decorators/field/sortable/helpers/canonicalScopeValue.d.ts +85 -0
- package/dist/types/src/decorators/field/sortable/helpers/clearCachedSortableValues.d.ts +13 -0
- package/dist/types/src/decorators/field/sortable/helpers/decrementScopedRecordsGreaterThanPosition.d.ts +8 -1
- package/dist/types/src/decorators/field/sortable/helpers/filterQueryToInstanceScope.d.ts +24 -0
- package/dist/types/src/decorators/field/sortable/helpers/filterQueryToScopeValues.d.ts +22 -0
- package/dist/types/src/decorators/field/sortable/helpers/heldSortableScopeLockKeys.d.ts +27 -0
- package/dist/types/src/decorators/field/sortable/helpers/performSortableDestroyWork.d.ts +14 -0
- package/dist/types/src/decorators/field/sortable/helpers/performSortablePositionWork.d.ts +15 -0
- package/dist/types/src/decorators/field/sortable/helpers/prepareSortableFieldsForDestroy.d.ts +41 -0
- package/dist/types/src/decorators/field/sortable/helpers/prepareSortableFieldsForSave.d.ts +35 -0
- package/dist/types/src/decorators/field/sortable/helpers/resortAllRecords.d.ts +32 -0
- package/dist/types/src/decorators/field/sortable/helpers/setPosition.d.ts +27 -11
- package/dist/types/src/decorators/field/sortable/helpers/sortableRowCache.d.ts +48 -0
- package/dist/types/src/decorators/field/sortable/helpers/sortableScopeColumns.d.ts +30 -0
- package/dist/types/src/decorators/field/sortable/helpers/sortableScopeLockKey.d.ts +44 -0
- package/dist/types/src/decorators/field/sortable/helpers/sortableScopeLockKeys.d.ts +51 -0
- package/dist/types/src/decorators/field/sortable/helpers/sortableSnapshot.d.ts +92 -0
- package/dist/types/src/decorators/field/sortable/helpers/stabilizeSortableScopeLocks.d.ts +59 -0
- package/dist/types/src/decorators/field/sortable/helpers/validPosition.d.ts +14 -2
- package/dist/types/src/decorators/field/sortable/hooks/afterSortableCreate.d.ts +1 -1
- package/dist/types/src/decorators/field/sortable/hooks/afterSortableDestroy.d.ts +3 -1
- package/dist/types/src/decorators/field/sortable/hooks/afterSortableUpdate.d.ts +1 -1
- package/dist/types/src/dream/Query.d.ts +41 -7
- package/dist/types/src/dream/QueryDriver/Base.d.ts +61 -1
- package/dist/types/src/dream/QueryDriver/Kysely.d.ts +6 -2
- package/dist/types/src/dream/QueryDriver/Postgres.d.ts +40 -0
- package/dist/types/src/dream/internal/softDeleteDream.d.ts +7 -1
- package/dist/types/src/dream-app/index.d.ts +18 -2
- package/dist/types/src/errors/SortableBatchRequiresTooManyScopeLocks.d.ts +37 -0
- package/dist/types/src/errors/SortableRequiresAdvisoryTransactionLocks.d.ts +13 -0
- package/dist/types/src/errors/SortableScopeDidNotStabilize.d.ts +23 -0
- package/dist/types/src/errors/SortableScopeLockWaitTimedOut.d.ts +34 -0
- package/dist/types/src/package-exports/errors.d.ts +3 -0
- 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 +88 -41
- package/docs/classes/db.PostgresQueryDriver.html +82 -42
- package/docs/classes/db.QueryDriverBase.html +86 -39
- 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.SortableBatchRequiresTooManyScopeLocks.html +61 -0
- package/docs/classes/errors.SortableScopeDidNotStabilize.html +48 -0
- package/docs/classes/errors.SortableScopeLockWaitTimedOut.html +58 -0
- 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 +19 -19
- package/docs/classes/index.Dream.html +151 -124
- package/docs/classes/index.DreamApp.html +11 -10
- package/docs/classes/index.DreamTransaction.html +2 -2
- package/docs/classes/index.Env.html +2 -2
- package/docs/classes/index.Query.html +94 -84
- 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 +2 -2
- 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 +1 -1
- package/dist/cjs/src/decorators/field/sortable/helpers/applySortableScopeToQuery.js +0 -13
- package/dist/cjs/src/decorators/field/sortable/helpers/sortableQueryExcludingDream.js +0 -7
- package/dist/cjs/src/decorators/field/sortable/hooks/beforeSortableSave.js +0 -41
- package/dist/esm/src/decorators/field/sortable/helpers/applySortableScopeToQuery.js +0 -13
- package/dist/esm/src/decorators/field/sortable/helpers/sortableQueryExcludingDream.js +0 -7
- package/dist/esm/src/decorators/field/sortable/hooks/beforeSortableSave.js +0 -41
- package/dist/types/src/decorators/field/sortable/helpers/applySortableScopeToQuery.d.ts +0 -12
- package/dist/types/src/decorators/field/sortable/helpers/sortableQueryExcludingDream.d.ts +0 -12
- package/dist/types/src/decorators/field/sortable/hooks/beforeSortableSave.d.ts +0 -12
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import Dream from '../../../../Dream.js';
|
|
2
|
+
import { SortableFieldConfig } from '../Sortable.js';
|
|
3
|
+
export interface SortableCache {
|
|
4
|
+
changingScope: boolean;
|
|
5
|
+
position: number | undefined;
|
|
6
|
+
previousPosition: number | undefined;
|
|
7
|
+
wasNewRecord: boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* @internal
|
|
11
|
+
*
|
|
12
|
+
* The sortable preparation phase of a save. Called from `saveDream` after every
|
|
13
|
+
* consumer before-hook has run — a phase, not a hook among them — so the
|
|
14
|
+
* decision is made from the final state of the instance: a position or scope
|
|
15
|
+
* change introduced by a `beforeSave` hook registered after Sortable's gets the
|
|
16
|
+
* same handling as one made before `save` was called, and a cache is never
|
|
17
|
+
* carried over from a state an intervening hook has made stale.
|
|
18
|
+
*
|
|
19
|
+
* For each sortable field this caches the values the position work needs and
|
|
20
|
+
* applies the position sentinel, then returns the fields whose position work
|
|
21
|
+
* this save will perform — the fields that make the save need a transaction of
|
|
22
|
+
* its own, so that the write and the position write that follows it commit
|
|
23
|
+
* together under one scope lock.
|
|
24
|
+
*
|
|
25
|
+
* - every create on a model with sortable fields performs position work;
|
|
26
|
+
* - an update performs it only when the save changes the position or one of the
|
|
27
|
+
* sortable scope columns.
|
|
28
|
+
*
|
|
29
|
+
* A save that skips hooks performs no position work at all, so it needs no
|
|
30
|
+
* transaction.
|
|
31
|
+
*/
|
|
32
|
+
export default function prepareSortableFieldsForSave(dream: Dream, { alreadyPersisted, skipHooks }: {
|
|
33
|
+
alreadyPersisted: boolean;
|
|
34
|
+
skipHooks: boolean;
|
|
35
|
+
}): SortableFieldConfig[];
|
|
@@ -1,2 +1,34 @@
|
|
|
1
1
|
import Dream from '../../../../Dream.js';
|
|
2
|
+
/**
|
|
3
|
+
* @internal
|
|
4
|
+
*
|
|
5
|
+
* Renumbers every sort scope of a model so that each one runs 1..n with no gaps.
|
|
6
|
+
*
|
|
7
|
+
* Four things make this safe to run against a live table:
|
|
8
|
+
*
|
|
9
|
+
* - **A driver that cannot take the scope lock fails before the table is
|
|
10
|
+
* read**, rather than succeeding for whichever scopes happen to be contiguous
|
|
11
|
+
* and throwing on the first one that is not.
|
|
12
|
+
* - **The STI scope is stripped**, exactly as every sortable hook strips it. An
|
|
13
|
+
* STI hierarchy shares one position space, so renumbering one child's rows in
|
|
14
|
+
* isolation would hand them positions its siblings already hold.
|
|
15
|
+
* - **Groups are identified structurally**, by the scope columns' `[column,
|
|
16
|
+
* value]` tuple rather than by joining the values into one string. Joining
|
|
17
|
+
* merges scopes whose values differ only in where a delimiter falls, and
|
|
18
|
+
* merges a null member with an empty-string one; a merged group is two
|
|
19
|
+
* physical sort scopes renumbered as though they were one, and no single lock
|
|
20
|
+
* key covers it.
|
|
21
|
+
* - **Every discovered scope is renumbered under its own scope lock, from a
|
|
22
|
+
* read taken inside that lock.** The unlocked pass below only discovers
|
|
23
|
+
* *which* sort scopes exist; it decides nothing about their contents, because
|
|
24
|
+
* a concurrent create, destroy or raw update between the two reads is
|
|
25
|
+
* precisely the case this exists to repair. Whether a scope needs renumbering
|
|
26
|
+
* at all is decided under the lock.
|
|
27
|
+
*
|
|
28
|
+
* Each scope's transaction is this function's own: `resort` cannot join a
|
|
29
|
+
* transaction the caller owns, and must not be called from inside one. It would
|
|
30
|
+
* wait on a connection of its own for a scope lock the caller's transaction may
|
|
31
|
+
* already hold — a wait no deadlock detector can see, which ends in
|
|
32
|
+
* `SortableScopeLockWaitTimedOut` rather than in progress.
|
|
33
|
+
*/
|
|
2
34
|
export default function resortAllRecords(dreamClass: typeof Dream, positionField: string, scope?: string | string[]): Promise<void>;
|
|
@@ -1,20 +1,36 @@
|
|
|
1
|
-
import { SelectQueryBuilder, UpdateQueryBuilder } from 'kysely';
|
|
2
1
|
import Dream from '../../../../Dream.js';
|
|
3
2
|
import DreamTransaction from '../../../../dream/DreamTransaction.js';
|
|
4
3
|
import Query from '../../../../dream/Query.js';
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
|
|
4
|
+
import { SortableCache } from './prepareSortableFieldsForSave.js';
|
|
5
|
+
import { SortableSnapshot } from './sortableSnapshot.js';
|
|
6
|
+
/**
|
|
7
|
+
* @internal
|
|
8
|
+
*
|
|
9
|
+
* Everything one sortable field's position write needs. The transaction is
|
|
10
|
+
* required: every path into `setPosition` runs from
|
|
11
|
+
* `performSortablePositionWork`, which is called by `saveDream` inside the
|
|
12
|
+
* transaction that holds the scope lock, and a position write that opened a
|
|
13
|
+
* transaction of its own would block on a key its own caller holds.
|
|
14
|
+
*/
|
|
15
|
+
export interface SortablePositionWrite extends SortableCache {
|
|
8
16
|
dream: Dream;
|
|
9
17
|
positionField: string;
|
|
10
18
|
query: Query<Dream>;
|
|
11
19
|
scope: string | string[] | undefined;
|
|
12
|
-
txn?: DreamTransaction<any> | undefined;
|
|
13
|
-
}
|
|
14
|
-
export interface SortableCacheWithRequiredTransaction extends SortableCacheWithOptionalTransaction {
|
|
15
20
|
txn: DreamTransaction<any>;
|
|
21
|
+
/**
|
|
22
|
+
* The record's physical position and sort scope values, read from the row
|
|
23
|
+
* under the scope lock before the driver write. Present on the update path;
|
|
24
|
+
* absent on create, which has no prior row.
|
|
25
|
+
*/
|
|
26
|
+
snapshot?: SortableSnapshot | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* The position the record holds in the scope its position is being computed
|
|
29
|
+
* over, where the driver write has replaced it on the row with the position
|
|
30
|
+
* sentinel. Set only for a save that lands in the scope the row already
|
|
31
|
+
* occupies; on every other path the scope's own rows carry their real
|
|
32
|
+
* positions, or the record is joining the scope rather than already in it.
|
|
33
|
+
*/
|
|
34
|
+
positionUnderSentinel?: number | undefined;
|
|
16
35
|
}
|
|
17
|
-
export default function setPosition(obj:
|
|
18
|
-
export declare function applySortableScopesToQuery<QB extends UpdateQueryBuilder<any, any, any, any> | SelectQueryBuilder<any, any, any>>(dream: Dream, kyselyQuery: QB, whereValueCB: (column: string) => any, scope?: string | string[]): QB;
|
|
19
|
-
export declare function getPostgresQueryDriver(connectionName: string): typeof PostgresQueryDriver;
|
|
20
|
-
export {};
|
|
36
|
+
export default function setPosition(obj: SortablePositionWrite): Promise<void>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import DreamTransaction from '../../../../dream/DreamTransaction.js';
|
|
2
|
+
/**
|
|
3
|
+
* @internal
|
|
4
|
+
*
|
|
5
|
+
* Stashes rows a locked batch's preflight read under the batch's scope locks,
|
|
6
|
+
* so that the per-record position work that follows does not read them a second
|
|
7
|
+
* time. The preflight reads every candidate's position and sort scope columns
|
|
8
|
+
* to derive the key set; each record's own snapshot read then asks the same
|
|
9
|
+
* question of the same row, in the same transaction, under the same locks.
|
|
10
|
+
*
|
|
11
|
+
* Called once per preflight, with the rows of the pass that converged: an
|
|
12
|
+
* earlier pass read before some of the locks were held, and a preflight that
|
|
13
|
+
* raised holds no lock the rest of the transaction can rely on. The rows are
|
|
14
|
+
* consumed one at a time and the whole entry is dropped by the first position
|
|
15
|
+
* write of the batch, since a compaction moves rows this holds stale positions
|
|
16
|
+
* for — so what it saves is the snapshot reads of the records walked before
|
|
17
|
+
* that first write.
|
|
18
|
+
*
|
|
19
|
+
* @param txn - the batch's transaction
|
|
20
|
+
* @param table - the table the rows came from
|
|
21
|
+
* @param primaryKey - the column the rows are keyed by
|
|
22
|
+
* @param rows - the rows read, carrying at least the position and scope columns
|
|
23
|
+
*/
|
|
24
|
+
export declare function cacheSortableRowsReadUnderLock(txn: DreamTransaction<any>, table: string, primaryKey: string, rows: Record<string, unknown>[]): void;
|
|
25
|
+
/**
|
|
26
|
+
* @internal
|
|
27
|
+
*
|
|
28
|
+
* The cached row for one record, removed from the cache as it is handed over.
|
|
29
|
+
*
|
|
30
|
+
* Consuming is destructive because the consumer is a position-mutating
|
|
31
|
+
* operation: it is about to compact or renumber the scope the row sits in, and
|
|
32
|
+
* a second read of the same cached row would see the position the row had
|
|
33
|
+
* before that. {@link invalidateSortableRowCache} covers the same hazard for
|
|
34
|
+
* the *other* records a compaction moves.
|
|
35
|
+
*/
|
|
36
|
+
export declare function takeCachedSortableRow(txn: DreamTransaction<any>, table: string, primaryKeyValue: unknown): Record<string, unknown> | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* @internal
|
|
39
|
+
*
|
|
40
|
+
* Discards everything cached for this transaction. Called for two reasons: a
|
|
41
|
+
* position write just moved rows the cache cannot identify — a compaction
|
|
42
|
+
* shifts every row in one sort scope above a position, and the cache holds no
|
|
43
|
+
* index by scope — and the batch the rows were read for has finished, after
|
|
44
|
+
* which they describe nothing the rest of a caller-owned transaction should
|
|
45
|
+
* trust. The records still to be walked read their own rows again, which is
|
|
46
|
+
* what they did before the cache existed.
|
|
47
|
+
*/
|
|
48
|
+
export declare function invalidateSortableRowCache(txn: DreamTransaction<any>): void;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import Dream from '../../../../Dream.js';
|
|
2
|
+
/**
|
|
3
|
+
* @internal
|
|
4
|
+
*
|
|
5
|
+
* The scope members of a sortable field paired with the database columns they
|
|
6
|
+
* resolve to. A member names either the column itself or the BelongsTo
|
|
7
|
+
* association whose foreign key backs it, and a caller matching incoming
|
|
8
|
+
* attributes needs both spellings; a member that resolves to no column is
|
|
9
|
+
* dropped.
|
|
10
|
+
*
|
|
11
|
+
* Every sortable path resolves its scope columns through this one function, so
|
|
12
|
+
* that a lock key, a snapshot read, a group identity and a scope filter can
|
|
13
|
+
* never disagree about which columns a sort scope is made of.
|
|
14
|
+
*/
|
|
15
|
+
export declare function sortableScopeColumnPairs(dream: Dream, scope: string | string[] | undefined): [string, string][];
|
|
16
|
+
/**
|
|
17
|
+
* @internal
|
|
18
|
+
*
|
|
19
|
+
* The database columns a sortable field's scope resolves to.
|
|
20
|
+
*/
|
|
21
|
+
export default function sortableScopeColumns(dream: Dream, scope: string | string[] | undefined): string[];
|
|
22
|
+
/**
|
|
23
|
+
* @internal
|
|
24
|
+
*
|
|
25
|
+
* The sort scope as `[column, value]` pairs — the shape a lock key is derived
|
|
26
|
+
* from and a group is identified by. The value for each column comes from the
|
|
27
|
+
* caller, which decides whether it is reading the in-memory instance or a
|
|
28
|
+
* snapshot of the row.
|
|
29
|
+
*/
|
|
30
|
+
export declare function sortableScopeValues(dream: Dream, scope: string | string[] | undefined, valueForColumn: (column: string) => unknown): [string, unknown][];
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import Dream from '../../../../Dream.js';
|
|
2
|
+
/**
|
|
3
|
+
* @internal
|
|
4
|
+
*
|
|
5
|
+
* Derives the advisory lock key that makes one sort scope's position
|
|
6
|
+
* computation exclusive. Every position-mutating path derives it the same way,
|
|
7
|
+
* since two paths that disagree about the key do not exclude each other.
|
|
8
|
+
*
|
|
9
|
+
* The key covers:
|
|
10
|
+
*
|
|
11
|
+
* - the **physical database name**, because Postgres advisory locks are scoped
|
|
12
|
+
* to the cluster rather than to a database. Without it, two unrelated
|
|
13
|
+
* databases in one cluster — including this repo's own parallel test
|
|
14
|
+
* databases — would contend on the same key, and that contention shows up
|
|
15
|
+
* only as writers of unrelated scopes queueing behind each other.
|
|
16
|
+
* - the table and the position field, since a model can declare several
|
|
17
|
+
* sortable fields, each with its own position space.
|
|
18
|
+
* - the scope column values, which are what a "sort scope" actually is.
|
|
19
|
+
*
|
|
20
|
+
* Values are encoded structurally rather than joined into a string, so a null
|
|
21
|
+
* member is distinct from an empty string and no combination of delimiters in
|
|
22
|
+
* one member can be mistaken for a different tuple. They are also canonicalized
|
|
23
|
+
* against the column's database type (`canonicalScopeValue`), so a writer
|
|
24
|
+
* holding a pending JavaScript value and a writer holding the value Postgres
|
|
25
|
+
* hydrated derive one key rather than two.
|
|
26
|
+
*
|
|
27
|
+
* **Scope values are assumed non-adversarial.** The hash is FNV-1a, which is
|
|
28
|
+
* unkeyed: anyone who can choose a scope column's value — where that column
|
|
29
|
+
* holds user-supplied text rather than a foreign key — can compute a value
|
|
30
|
+
* whose key equals another scope's, and share that scope's lock with it. A
|
|
31
|
+
* collision costs contention and never correctness, since a shared lock
|
|
32
|
+
* over-serializes rather than under-locking, and the bounded wait
|
|
33
|
+
* (`sortableScopeLockTimeout`) caps what a deliberate one can achieve at
|
|
34
|
+
* stalls rather than at hangs. A keyed digest would make deliberate collisions
|
|
35
|
+
* expensive, and is not used: changing the derivation would give old and new
|
|
36
|
+
* processes different keys for one scope during a rolling deploy, which is the
|
|
37
|
+
* race this lock exists to eliminate.
|
|
38
|
+
*
|
|
39
|
+
* @param dream - the record whose scope is being locked
|
|
40
|
+
* @param positionField - the sortable position column
|
|
41
|
+
* @param scopeValues - `[column, value]` pairs for the scope being locked
|
|
42
|
+
* @returns a 64-bit signed key
|
|
43
|
+
*/
|
|
44
|
+
export default function sortableScopeLockKey(dream: Dream, positionField: string, scopeValues: [string, unknown][]): bigint;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import Dream from '../../../../Dream.js';
|
|
2
|
+
import { SortableFieldConfig } from '../Sortable.js';
|
|
3
|
+
import { SortableSnapshots } from './sortableSnapshot.js';
|
|
4
|
+
/**
|
|
5
|
+
* @internal
|
|
6
|
+
*
|
|
7
|
+
* The single key for the scope the record currently occupies — the scope its
|
|
8
|
+
* position is about to be computed within. Used by the position write itself,
|
|
9
|
+
* where the record already carries its final scope values.
|
|
10
|
+
*/
|
|
11
|
+
export declare function sortableScopeLockKeyForCurrentScope(dream: Dream, positionField: string, scope: string | string[] | undefined): bigint;
|
|
12
|
+
/**
|
|
13
|
+
* @internal
|
|
14
|
+
*
|
|
15
|
+
* Every key a create's position work needs — one per sortable field, derived
|
|
16
|
+
* from the scope values the row now carries — sorted and deduplicated, so that
|
|
17
|
+
* a multi-sortable-field create acquires in the same global order as every
|
|
18
|
+
* other position-mutating operation. Declaration order cannot serve: the keys
|
|
19
|
+
* hash the runtime scope values, so which field's key sorts first varies from
|
|
20
|
+
* record to record.
|
|
21
|
+
*/
|
|
22
|
+
export declare function sortableCurrentScopeLockKeys(dream: Dream, configs: SortableFieldConfig[]): bigint[];
|
|
23
|
+
/**
|
|
24
|
+
* @internal
|
|
25
|
+
*
|
|
26
|
+
* Every key a per-record position-mutating operation needs, sorted and
|
|
27
|
+
* deduplicated. A save that moves a record between sort scopes compacts the
|
|
28
|
+
* scope it leaves and makes room in the scope it enters, so it needs both keys;
|
|
29
|
+
* a save within one scope needs one, and so do a destroy and an undestroy, which
|
|
30
|
+
* write no scope values at all.
|
|
31
|
+
*
|
|
32
|
+
* The *new*-scope key comes from the values being written. The *old*-scope key
|
|
33
|
+
* comes from the database snapshot of the record's physical scope columns when
|
|
34
|
+
* one is available, and only falls back to the instance's pending changes when
|
|
35
|
+
* it is not — which is the case exactly once per save, on the first
|
|
36
|
+
* acquisition, because the snapshot cannot be read until something is locked.
|
|
37
|
+
* The caller closes that window by re-reading under the lock and calling this
|
|
38
|
+
* again with the snapshot; see `acquireStabilizedSortableScopeLocks`.
|
|
39
|
+
*
|
|
40
|
+
* A pending change is only an *old* scope when it has a previous persisted
|
|
41
|
+
* value. A record created earlier in this process reports every column as
|
|
42
|
+
* changed with `was === undefined` (`Dream#changes` compares against the
|
|
43
|
+
* pre-save state), which is not a scope the row was ever in: reading it as one
|
|
44
|
+
* would take a second, bogus lock — and, since every such record produces the
|
|
45
|
+
* same all-null scope tuple, it would be the *same* bogus key for every record
|
|
46
|
+
* in the table, serializing writers that touch unrelated scopes.
|
|
47
|
+
*
|
|
48
|
+
* Either way this must be called *before* the driver write overwrites the
|
|
49
|
+
* record's scope columns.
|
|
50
|
+
*/
|
|
51
|
+
export declare function sortableSaveLockKeys(dream: Dream, configs: SortableFieldConfig[], snapshots?: SortableSnapshots): bigint[];
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import Dream from '../../../../Dream.js';
|
|
2
|
+
import DreamTransaction from '../../../../dream/DreamTransaction.js';
|
|
3
|
+
import { SortableFieldConfig } from '../Sortable.js';
|
|
4
|
+
/**
|
|
5
|
+
* @internal
|
|
6
|
+
*
|
|
7
|
+
* What a sortable position-mutating path knows about the row as it physically
|
|
8
|
+
* exists, rather than as the in-memory instance remembers it.
|
|
9
|
+
*
|
|
10
|
+
* `position` is the row's real position; `scopeValues` are the row's real sort
|
|
11
|
+
* scope column values, keyed by column. An instance loaded before another
|
|
12
|
+
* writer moved the row carries neither.
|
|
13
|
+
*/
|
|
14
|
+
export interface SortableSnapshot {
|
|
15
|
+
position: number | null;
|
|
16
|
+
scopeValues: Map<string, unknown>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* @internal
|
|
20
|
+
*
|
|
21
|
+
* Snapshots keyed by position field — a model may declare several sortable
|
|
22
|
+
* fields, each with its own scope and its own position space.
|
|
23
|
+
*/
|
|
24
|
+
export type SortableSnapshots = Map<string, SortableSnapshot>;
|
|
25
|
+
/**
|
|
26
|
+
* @internal
|
|
27
|
+
*
|
|
28
|
+
* The outcome of a snapshot read: the snapshots themselves, plus whether the
|
|
29
|
+
* row was physically there to read.
|
|
30
|
+
*
|
|
31
|
+
* The two are deliberately separate. An empty `snapshots` map means only "no
|
|
32
|
+
* snapshot is available", which the model having no sortable fields produces
|
|
33
|
+
* just as readily as a row another writer has already deleted — and a caller
|
|
34
|
+
* that reads absence out of the empty map compacts a scope this operation never
|
|
35
|
+
* vacated.
|
|
36
|
+
*/
|
|
37
|
+
export interface SortableSnapshotRead {
|
|
38
|
+
/**
|
|
39
|
+
* False only when a read positively found no row. A read that never had to
|
|
40
|
+
* run — a model with no sortable fields — reports true, since it observed
|
|
41
|
+
* nothing.
|
|
42
|
+
*/
|
|
43
|
+
rowExists: boolean;
|
|
44
|
+
snapshots: SortableSnapshots;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* @internal
|
|
48
|
+
*
|
|
49
|
+
* Reads the record's real position and sort scope column values straight from
|
|
50
|
+
* the row, inside the given transaction.
|
|
51
|
+
*
|
|
52
|
+
* The read deliberately bypasses Dream's default scopes and goes to the table
|
|
53
|
+
* by primary key: soft-deleted rows have to be readable here (undestroy reads
|
|
54
|
+
* one), and an STI child's row is the same physical row whatever scope the
|
|
55
|
+
* class carries.
|
|
56
|
+
*
|
|
57
|
+
* When the row is not there the snapshots are empty and `rowExists` is false;
|
|
58
|
+
* a caller that goes on to compute a shift falls back to the instance, which is
|
|
59
|
+
* the pre-existing behavior.
|
|
60
|
+
*
|
|
61
|
+
* @param dream - the record to snapshot
|
|
62
|
+
* @param txn - the transaction the read runs in; the read must be protected by
|
|
63
|
+
* whatever scope lock the caller holds in that transaction
|
|
64
|
+
* @param configs - the sortable fields whose position and scope columns to read
|
|
65
|
+
*/
|
|
66
|
+
export declare function readSortableSnapshots(dream: Dream, txn: DreamTransaction<any>, configs: SortableFieldConfig[]): Promise<SortableSnapshotRead>;
|
|
67
|
+
/**
|
|
68
|
+
* @internal
|
|
69
|
+
*
|
|
70
|
+
* Stashes snapshots on the instance so the phase that computes the shift can
|
|
71
|
+
* reach the read the earlier phase made. The read and the shift are separate
|
|
72
|
+
* calls on every path — `prepareSortableFieldsForDestroy` to
|
|
73
|
+
* `performSortableDestroyWork`, the `saveDream` lock acquisition to
|
|
74
|
+
* `performSortablePositionWork` — and the instance is what crosses between
|
|
75
|
+
* them.
|
|
76
|
+
*/
|
|
77
|
+
export declare function cacheSortableSnapshots(dream: Dream, snapshots: SortableSnapshots): void;
|
|
78
|
+
/**
|
|
79
|
+
* @internal
|
|
80
|
+
*/
|
|
81
|
+
export declare function sortableSnapshotFor(dream: Dream, positionField: string): SortableSnapshot | undefined;
|
|
82
|
+
/**
|
|
83
|
+
* @internal
|
|
84
|
+
*/
|
|
85
|
+
export declare function clearSortableSnapshot(dream: Dream, positionField: string): void;
|
|
86
|
+
/**
|
|
87
|
+
* @internal
|
|
88
|
+
*
|
|
89
|
+
* The value to filter a sort scope column by: the row's real value when a
|
|
90
|
+
* snapshot is available, and the caller's own fallback when it is not.
|
|
91
|
+
*/
|
|
92
|
+
export declare function snapshotScopeValue(snapshot: SortableSnapshot | undefined, column: string, fallback: () => unknown): unknown;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import Dream from '../../../../Dream.js';
|
|
2
|
+
import DreamTransaction from '../../../../dream/DreamTransaction.js';
|
|
3
|
+
/**
|
|
4
|
+
* How many acquisition passes a position-mutating operation will make while
|
|
5
|
+
* following a record that has been moved to another sort scope, before giving
|
|
6
|
+
* up. One pass is the ordinary case (lock what the caller says, read, agree); a
|
|
7
|
+
* second covers a competitor that moved the row between the two; a third exists
|
|
8
|
+
* so that a single unlucky retry is not fatal. Beyond that the row is being
|
|
9
|
+
* moved faster than it can be read, and raising is better than compacting a
|
|
10
|
+
* scope it no longer occupies.
|
|
11
|
+
*
|
|
12
|
+
* The bound counts acquisitions, and every one of them counts: the convergence
|
|
13
|
+
* check runs after the last acquisition and the read taken under it, so an
|
|
14
|
+
* operation that settles on its final pass settles rather than raising.
|
|
15
|
+
*/
|
|
16
|
+
export declare const MAX_SCOPE_STABILIZATION_ROUNDS = 3;
|
|
17
|
+
/**
|
|
18
|
+
* @internal
|
|
19
|
+
*
|
|
20
|
+
* The stabilization protocol every sortable lock acquisition follows, in one
|
|
21
|
+
* place: acquire the keys the current knowledge implies, read again under those
|
|
22
|
+
* locks, and take whatever additional key the read reveals — until a pass
|
|
23
|
+
* reveals nothing new, or the bound above is exhausted.
|
|
24
|
+
*
|
|
25
|
+
* The bootstrapping problem it solves: a sort scope's lock key is derived from
|
|
26
|
+
* the record's *physical* scope columns, and those cannot be read safely until
|
|
27
|
+
* that scope is locked. So the first pass locks what the caller can know without
|
|
28
|
+
* reading — an instance's own values, or an unprotected candidate read — and
|
|
29
|
+
* every later pass locks what the read taken under those locks reveals.
|
|
30
|
+
*
|
|
31
|
+
* Nothing here is ever unwound: the locks are transaction-scoped advisory locks,
|
|
32
|
+
* which are additive and are released only when the transaction ends. A pass
|
|
33
|
+
* that discovers a new key simply also takes it, holding at worst one lock too
|
|
34
|
+
* many for the rest of the transaction — which costs contention and never
|
|
35
|
+
* correctness. That is what lets this run inside a caller-owned transaction
|
|
36
|
+
* unchanged; no path here restarts or rolls back a transaction it did not open.
|
|
37
|
+
*
|
|
38
|
+
* Keys the transaction already holds — taken by an earlier phase of the same
|
|
39
|
+
* operation, or by the preflight of the locked batch this record is being
|
|
40
|
+
* walked in — are not taken again; they are held to the end of the transaction
|
|
41
|
+
* regardless. Convergence is judged on that same set, so a pass whose read
|
|
42
|
+
* reveals only keys already held is a pass that agrees.
|
|
43
|
+
*
|
|
44
|
+
* Keys are taken in sorted order within a pass, so that two operations touching
|
|
45
|
+
* the same set of scopes cannot deadlock against each other. Across passes the
|
|
46
|
+
* order is discovery-driven and therefore unsortable in principle, which is the
|
|
47
|
+
* named fallback: two operations moving records in opposite directions between
|
|
48
|
+
* the same two scopes can raise Postgres's `40P01` deadlock error, a visible
|
|
49
|
+
* retryable failure.
|
|
50
|
+
*
|
|
51
|
+
* @param dream - a record of the model being locked; supplies the connection
|
|
52
|
+
* and, when this fails, the class name in the error
|
|
53
|
+
* @param txn - the transaction the locks are scoped to
|
|
54
|
+
* @param keysForRound - reads whatever this pass can see — the caller's own
|
|
55
|
+
* snapshot or candidate read — and returns every lock key it implies. Called
|
|
56
|
+
* once per pass with the number of acquisitions made so far, so a caller whose
|
|
57
|
+
* first pass needs no read can skip it on round 0
|
|
58
|
+
*/
|
|
59
|
+
export default function stabilizeSortableScopeLocks(dream: Dream, txn: DreamTransaction<any>, keysForRound: (round: number) => Promise<Iterable<bigint>>): Promise<void>;
|
|
@@ -1,2 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { SortablePositionWrite } from './setPosition.js';
|
|
2
|
+
/**
|
|
3
|
+
* @internal
|
|
4
|
+
*
|
|
5
|
+
* The position the record will be written at: the caller's, clamped to the
|
|
6
|
+
* scope the record now occupies.
|
|
7
|
+
*
|
|
8
|
+
* The scope is read off the instance rather than off the snapshot because the
|
|
9
|
+
* scope in question is the one being *entered*, which is the instance's — the
|
|
10
|
+
* snapshot holds the scope the row was in before the driver write. On every
|
|
11
|
+
* path into here the driver write has already landed the instance's scope
|
|
12
|
+
* values on the row.
|
|
13
|
+
*/
|
|
14
|
+
export default function validPosition({ changingScope, dream, position, positionField, positionUnderSentinel, query, scope, wasNewRecord, previousPosition, txn, }: SortablePositionWrite): Promise<number>;
|
|
@@ -5,6 +5,6 @@ export default function afterSortableCreate({ positionField, dream, query, txn,
|
|
|
5
5
|
positionField: string;
|
|
6
6
|
dream: Dream;
|
|
7
7
|
query: Query<Dream>;
|
|
8
|
-
txn
|
|
8
|
+
txn: DreamTransaction<any>;
|
|
9
9
|
scope: string | string[] | undefined;
|
|
10
10
|
}): Promise<void>;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import Dream from '../../../../Dream.js';
|
|
2
|
+
import DreamTransaction from '../../../../dream/DreamTransaction.js';
|
|
2
3
|
import Query from '../../../../dream/Query.js';
|
|
3
|
-
export default function afterSortableDestroy({ positionField, dream, query, scope, }: {
|
|
4
|
+
export default function afterSortableDestroy({ positionField, dream, query, scope, txn, }: {
|
|
4
5
|
positionField: string;
|
|
5
6
|
dream: Dream;
|
|
6
7
|
query: Query<Dream>;
|
|
7
8
|
scope: string | string[] | undefined;
|
|
9
|
+
txn: DreamTransaction<any>;
|
|
8
10
|
}): Promise<void>;
|
|
@@ -5,6 +5,6 @@ export default function afterUpdateSortable({ positionField, dream, query, txn,
|
|
|
5
5
|
positionField: string;
|
|
6
6
|
dream: Dream;
|
|
7
7
|
query: Query<Dream>;
|
|
8
|
-
txn
|
|
8
|
+
txn: DreamTransaction<any>;
|
|
9
9
|
scope: string | string[] | undefined;
|
|
10
10
|
}): Promise<void>;
|
|
@@ -1328,9 +1328,11 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
|
|
|
1328
1328
|
* read is a plain `FOR UPDATE`: if another transaction already holds a lock
|
|
1329
1329
|
* on one of the batch's rows, this call blocks until that transaction ends,
|
|
1330
1330
|
* holding its own transaction and its pooled connection open the whole
|
|
1331
|
-
* time. Dream sets no `lock_timeout` or `statement_timeout
|
|
1332
|
-
*
|
|
1333
|
-
*
|
|
1331
|
+
* time. Dream sets no `lock_timeout` or `statement_timeout` on a row-lock
|
|
1332
|
+
* wait — the one it does set, around a sortable model's scope-lock
|
|
1333
|
+
* acquisition, is restored before the rows are claimed — so a long-running
|
|
1334
|
+
* writer on the other side stalls the run indefinitely and, at scale, ties
|
|
1335
|
+
* up connections. If that is a risk in your application, set a
|
|
1334
1336
|
* `lock_timeout` on the connection so a stuck batch fails instead of
|
|
1335
1337
|
* hanging.
|
|
1336
1338
|
* - **A `limit` or `offset` on the Query is incompatible with `destroy` on
|
|
@@ -1408,6 +1410,36 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
|
|
|
1408
1410
|
* When the Query already carries a transaction, that transaction is used for
|
|
1409
1411
|
* every batch rather than opening one per batch, so the caller's transaction
|
|
1410
1412
|
* boundary wins and the locks are held until the caller commits.
|
|
1413
|
+
*
|
|
1414
|
+
* On a sortable model, the advisory scope keys this batch needs are acquired
|
|
1415
|
+
* before the claim (see `acquireStabilizedSortableBatchLocks`), which removes
|
|
1416
|
+
* the row-lock/advisory-lock inversion for the rows the batch itself claims.
|
|
1417
|
+
* Two things stay outside that guarantee and are fallbacks rather than
|
|
1418
|
+
* guarantees, with Postgres's `40P01` as the residual failure in both:
|
|
1419
|
+
*
|
|
1420
|
+
* - the ordering is **per batch**. A multi-batch run inside a caller-owned
|
|
1421
|
+
* transaction keeps batch N-1's row locks while batch N preflights. The
|
|
1422
|
+
* same shape accumulates *advisory* locks: they are released only when the
|
|
1423
|
+
* transaction ends, so every batch's scope keys are still held when the
|
|
1424
|
+
* next one preflights. That accumulation is bounded — the preflight counts
|
|
1425
|
+
* the keys the transaction already holds along with the ones the batch
|
|
1426
|
+
* would add, and refuses with `SortableBatchRequiresTooManyScopeLocks`
|
|
1427
|
+
* rather than letting a long run over a high-cardinality sort scope exhaust
|
|
1428
|
+
* `max_locks_per_transaction` for the whole cluster — so a run long enough
|
|
1429
|
+
* raises instead of completing. Letting Dream open a transaction per batch
|
|
1430
|
+
* releases the keys as it goes and gives each batch the whole budget.
|
|
1431
|
+
* - the preflight covers **the claimed rows' own scopes only**. A
|
|
1432
|
+
* `dependent: 'destroy'` cascade under `destroy({ lock: true })` destroys
|
|
1433
|
+
* child records of other tables, and a sortable child takes its own table's
|
|
1434
|
+
* advisory key inside `perBatch` — after this transaction is already
|
|
1435
|
+
* holding the root rows' row locks. Walking
|
|
1436
|
+
* `dependentDestroyAssociationNames()` to fold those keys into the sorted
|
|
1437
|
+
* pass is deliberately not done: the cascade's shape is only knowable per
|
|
1438
|
+
* record, and the deadlock it leaves is detected and retryable. The keys
|
|
1439
|
+
* the preflight *did* take are held across the whole batch, cascade
|
|
1440
|
+
* included — a per-record destroy holds its scope lock across one delete
|
|
1441
|
+
* and one compaction, while here the hold spans batch size × cascade depth.
|
|
1442
|
+
* A smaller `batchSize` shortens it (the default is 10).
|
|
1411
1443
|
*/
|
|
1412
1444
|
private lockedBatches;
|
|
1413
1445
|
/**
|
|
@@ -1651,10 +1683,12 @@ export default class Query<DreamInstance extends Dream, QueryTypeOpts extends Re
|
|
|
1651
1683
|
* read is a plain `FOR UPDATE`: if another transaction already holds a
|
|
1652
1684
|
* lock on one of the batch's rows, this call blocks until that transaction
|
|
1653
1685
|
* ends, holding its own transaction and its pooled connection open the
|
|
1654
|
-
* whole time. Dream sets no `lock_timeout` or `statement_timeout
|
|
1655
|
-
*
|
|
1656
|
-
*
|
|
1657
|
-
*
|
|
1686
|
+
* whole time. Dream sets no `lock_timeout` or `statement_timeout` on a
|
|
1687
|
+
* row-lock wait — the one it does set, around a sortable model's scope-lock
|
|
1688
|
+
* acquisition, is restored before the rows are claimed — so a long-running
|
|
1689
|
+
* writer on the other side stalls the run indefinitely and, at scale, ties
|
|
1690
|
+
* up connections. If that is a risk in your application, set a
|
|
1691
|
+
* `lock_timeout` on the connection so a stuck batch fails instead of
|
|
1658
1692
|
* hanging.
|
|
1659
1693
|
* - **`skipHooks` composes with `lock`, but stays on the per-instance
|
|
1660
1694
|
* path.** With an attributes object and no `lock`, `skipHooks: true`
|
|
@@ -145,6 +145,62 @@ export default class QueryDriverBase<DreamInstance extends Dream> {
|
|
|
145
145
|
* worker's lifetime.
|
|
146
146
|
*/
|
|
147
147
|
static openTestDatabaseLockSession(connectionName: string): Promise<TestDatabaseLockSession>;
|
|
148
|
+
/**
|
|
149
|
+
* @internal
|
|
150
|
+
*
|
|
151
|
+
* Whether this driver implements the transaction-scoped advisory lock seam
|
|
152
|
+
* ({@link acquireAdvisoryTransactionLocks}). Adapters that can hold a
|
|
153
|
+
* key-addressed lock for the life of a transaction override this to `true`.
|
|
154
|
+
* It defaults to `false` so that a driver without the primitive fails loud
|
|
155
|
+
* for features that depend on it (today, the `Sortable` decorator) rather
|
|
156
|
+
* than silently racing on position values.
|
|
157
|
+
*/
|
|
158
|
+
static supportsAdvisoryTransactionLocks: boolean;
|
|
159
|
+
/**
|
|
160
|
+
* @internal
|
|
161
|
+
*
|
|
162
|
+
* Acquire exclusive, transaction-scoped locks on arbitrary keys, waiting
|
|
163
|
+
* until any other transaction holding one of them finishes. The locks are
|
|
164
|
+
* released when the enclosing transaction commits or rolls back — never by an
|
|
165
|
+
* explicit unlock — so callers only ever have to acquire (Postgres:
|
|
166
|
+
* `pg_advisory_xact_lock(bigint)`).
|
|
167
|
+
*
|
|
168
|
+
* Unlike {@link applyRowLock}, this does not depend on rows existing: it is
|
|
169
|
+
* the only way to serialize writers that are about to insert into the same
|
|
170
|
+
* logical group. The `Sortable` decorator uses it to make each sort scope's
|
|
171
|
+
* position computation exclusive, keyed on the physical database, table,
|
|
172
|
+
* position field and scope values.
|
|
173
|
+
*
|
|
174
|
+
* The seam takes the whole key set rather than one key at a time so that a
|
|
175
|
+
* driver able to express it can take them in a single round trip — the
|
|
176
|
+
* batched, row-locking query APIs can need one key per candidate record per
|
|
177
|
+
* sortable field. **Keys must be acquired in the order given**: callers sort
|
|
178
|
+
* them, and that shared order is what keeps two operations over the same
|
|
179
|
+
* scopes from deadlocking. A driver that has no bulk form implements this by
|
|
180
|
+
* looping over the keys in order.
|
|
181
|
+
*
|
|
182
|
+
* **The wait must be bounded.** A caller that finds a key held waits for the
|
|
183
|
+
* holder's transaction, and an implementation that waits forever turns an
|
|
184
|
+
* ordinary mistake — a sortable operation issued without the enclosing
|
|
185
|
+
* transaction, so that it waits on a key that transaction itself holds — into
|
|
186
|
+
* a permanently wedged connection that no deadlock detector can see, because
|
|
187
|
+
* only one side is waiting. So an implementation applies the
|
|
188
|
+
* `sortableScopeLockTimeout` DreamApp option (milliseconds) to the
|
|
189
|
+
* acquisition and raises `SortableScopeLockWaitTimedOut` when it elapses. A
|
|
190
|
+
* configured `0` means no bound, and is applied just as literally: the
|
|
191
|
+
* acquisition waits without bound even where the connection carries a
|
|
192
|
+
* wait bound of its own. The bound belongs to
|
|
193
|
+
* the acquisition alone: whatever lock-wait behavior the connection was
|
|
194
|
+
* configured with has to be in force again by the time this returns, so that
|
|
195
|
+
* the row locks {@link applyRowLock} takes are unaffected.
|
|
196
|
+
*
|
|
197
|
+
* Only ever called when {@link supportsAdvisoryTransactionLocks} is `true`;
|
|
198
|
+
* the base default throws.
|
|
199
|
+
*
|
|
200
|
+
* @param txn - the transaction the locks are scoped to
|
|
201
|
+
* @param keys - the 64-bit signed lock keys, in acquisition order
|
|
202
|
+
*/
|
|
203
|
+
static acquireAdvisoryTransactionLocks(txn: DreamTransaction<Dream>, keys: bigint[]): Promise<void>;
|
|
148
204
|
static getColumnData(connectionName: string, tableName: string, allTableAssociationData: {
|
|
149
205
|
[key: string]: SchemaBuilderAssociationData;
|
|
150
206
|
}): Promise<{
|
|
@@ -391,8 +447,12 @@ export default class QueryDriverBase<DreamInstance extends Dream> {
|
|
|
391
447
|
* @param dream - the dream instance you wish to destroy
|
|
392
448
|
* @param txn - a transaction to encapsulate, consistently provided by underlying dream mechanisms
|
|
393
449
|
* @param reallyDestroy - whether or not to reallyDestroy. If false, soft delete will be attempted when relevant
|
|
450
|
+
* @returns the number of rows this call removed (or soft deleted). Zero means
|
|
451
|
+
* the row was already gone, or that a `preventDeletion` veto stopped the
|
|
452
|
+
* write — callers distinguish the two by the veto flag, and use the count to
|
|
453
|
+
* decide whether there is anything for the after-destroy hooks to act on.
|
|
394
454
|
*/
|
|
395
|
-
static destroyDream(dream: Dream, txn: DreamTransaction<Dream>, reallyDestroy: boolean): Promise<
|
|
455
|
+
static destroyDream(dream: Dream, txn: DreamTransaction<Dream>, reallyDestroy: boolean): Promise<number>;
|
|
396
456
|
static get syncDialect(): string;
|
|
397
457
|
/**
|
|
398
458
|
* Returns the sql that would be executed by this Query
|