cloudreve/cloudreve · error
failed to decrease reference count for entities %v: %w
Error message
failed to decrease reference count for entities %v: %w
What it means
Raised inside File.Delete step 1: after collecting how many times each entity is referenced by the files being deleted, it runs grouped UPDATE entities SET reference_count = reference_count - N statements. Failure means one of these atomic decrements did not execute (driver error, constraint, deadlock).
Source
Thrown at inventory/file.go:547
entities[e.ID]++
storageReduced[fi.OwnerID] -= e.Size
}
}
// Group entities by their reference count.
uniqueEntities := lo.Keys(entities)
entitiesGrouped := lo.GroupBy(uniqueEntities, func(e int) int {
return entities[e]
})
for ref, entityGroup := range entitiesGrouped {
entityPageGroup, _ := f.batchInConditionEntityID(intsets.MaxInt, 10, 1, entityGroup)
for _, group := range entityPageGroup {
if err := f.client.Entity.Update().
Where(group).
AddReferenceCount(-1 * ref).
Exec(ctx); err != nil {
return nil, nil, fmt.Errorf("failed to decrease reference count for entities %v: %w", group, err)
}
}
}
// 2. Filter out entities with <=0 reference count, Update recycle options for above entities;
entityGroup, _ := f.batchInConditionEntityID(intsets.MaxInt, 10, 1, uniqueEntities)
toBeRecycled := make([]*ent.Entity, 0, len(entities))
for _, group := range entityGroup {
e, err := f.client.Entity.Query().Where(group).Where(entity.ReferenceCountLTE(0)).All(ctx)
if err != nil {
return nil, nil, fmt.Errorf("failed to query orphan entities %v: %w", group, err)
}
toBeRecycled = append(toBeRecycled, e...)
}
// 3. Update recycle options for above entities;
pageSize := capPageSize(f.maxSQlParam, intsets.MaxInt, 10)View on GitHub (pinned to 20c95ad73f)
Solutions
- Unwrap and classify: retry on deadlock/lock-wait errors, fix connectivity otherwise
- Serialize large deletes per entity or run them in a transaction so decrements are consistent
- Make the GC job tolerate reference counts that briefly lag (it re-scans next cycle)
- Ensure the entities table PK/indexes are healthy so the UPDATE IN (...) is fast
Example fix
// before
recycled, diff, err := inv.Delete(ctx, files, props)
// after
var recycled []*ent.Entity
var diff inventory.StorageDiff
err := retry.OnDeadlock(func() error {
recycled, diff, err = inv.Delete(ctx, files, props)
return err
}) Defensive patterns
Strategy: retry
Try / catch
if _, _, err := inv.Delete(ctx, files, props); err != nil {
if isDeadlock(err) {
time.Sleep(50 * time.Millisecond)
_, _, err = inv.Delete(ctx, files, props)
}
if err != nil { return err }
} Prevention
- Avoid concurrent Delete/Copy of files sharing deduplicated entities
- Wrap multi-file Delete in a transaction for consistent decrements
- Keep the GC sweep from hard-deleting entities mid-delete (coordinate schedules)
When it happens
Trigger: Concurrent Delete/Copy operations touching the same entity rows cause InnoDB lock wait timeout or deadlock; the entities row was hard-deleted by the GC between the read and the update (writes affect 0 rows, not an error, but FK errors can surface); connection/context failure mid-batch.
Common situations: Two users deleting files that share a deduplicated entity simultaneously; delete racing the recycle/GC job that purges stale entities; large batch crossing maxSQL param grouping with a dropped connection.
Related errors
- failed to soft delete file %d: %w
- failed to query stale entities: %w
- failed to query orphan entities %v: %w
- failed to copy entity: %w
- failed to create group %q: %w
AI-assisted analysis of cloudreve/cloudreve@20c95ad73f (2026-08-16).
Data as JSON: /api/errors/08489d74ca2a37b8.
Report an issue: GitHub.