abpframework / abpframework/abp
Low-Code: fixed per-mutation overhead — distributed lock + blob manifest read, post-mutation re-read, uncached descriptor hash
Open
@salihozkara is already working on this.
Since Aug 31, 2026.
- Dominant language
- C#
- Stars
- 14.4k
- Forks
- 3.7k
- Avg merge
- 15h 32m
- Merged PRs (30d)
- 106
Description
Description
Every Low-Code create/update/delete carries fixed overhead that is paid even in the common case where none of it is needed:
- Distributed lock + blob read per mutation.
DynamicEntityMutationGate.ExecuteAsyncwraps every mutation and (a) reads the entity-blob-cleanup manifest from the blob container (EntityBlobCleanupManifestStore.GetIntentAsync— a blob read + JSON parse), then (b) acquiresIAbpDistributedLock.TryAcquireAsync. With a Redis lock provider and remote blob storage this is 2+ extra network round-trips on every mutation of every dynamic entity — including entities that have no file/image properties at all, which is what the manifest/gate machinery exists for. - Full re-read to build the response. After a create or update, the pipeline re-reads the record via a full
SingleEntityQuery(with relation joins and collection hydrators) to build the returnedDynamicEntityDto— an extra SELECT (plus hydrator queries) per mutation, where a conventionalCrudAppServicemaps the tracked entity it already has. - Uncached descriptor-version hash per SaveChanges.
DynamicEntityStaleModelSaveChangesInterceptorcompares model versions on everySaveChangesthat touches dynamic entities; computing the current descriptor version (DynamicModelManager.GetEntityDescriptorVersion) serializes the descriptor to JSON and hashes it on every call — the result is deterministic for a given modelVersionand could be cached until the model changes. (The DB round-trip correctly happens only on version mismatch, andSuppressStaleModelCheck()exists — this point is only about the recomputed hash.)
Configuration
- ABP 10.7.0-rc.3 (
Volo.Abp.LowCode.Application/.EntityFrameworkCore/.Domain), Low-Code module - .NET 10, PostgreSQL,
app-nolayersmodular monolith
Regression?
Not known to be a regression.
Data
Static inspection of the shipped assemblies (decompiled for analysis), not yet profiled. The gate path (manifest read + TryAcquireAsync), the post-mutation SingleEntityQuery re-read, and the per-call serialize+hash in GetEntityDescriptorVersion are directly visible in the mutation pipeline.
Analysis
Suggested directions:
- Skip the mutation gate (manifest read + distributed lock) entirely for entity types whose descriptor declares no file/image/blob-backed properties — the descriptor is available before the gate runs.
- Build the response DTO from the tracked entity plus the already-known FK display values where possible, falling back to the re-read only when deferred/computed projections require it.
- Cache the descriptor-version hash keyed by (entity name, model
Version), invalidated by the existing model-changed notification.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.