{"record":{"id":"08489d74ca2a37b8","repo":"cloudreve/cloudreve","slug":"failed-to-decrease-reference-count-for-entities-v","errorCode":null,"errorMessage":"failed to decrease reference count for entities %v: %w","messagePattern":"failed to decrease reference count for entities (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"inventory/file.go","lineNumber":547,"sourceCode":"\t\t\tentities[e.ID]++\n\t\t\tstorageReduced[fi.OwnerID] -= e.Size\n\t\t}\n\t}\n\n\t// Group entities by their reference count.\n\tuniqueEntities := lo.Keys(entities)\n\tentitiesGrouped := lo.GroupBy(uniqueEntities, func(e int) int {\n\t\treturn entities[e]\n\t})\n\n\tfor ref, entityGroup := range entitiesGrouped {\n\t\tentityPageGroup, _ := f.batchInConditionEntityID(intsets.MaxInt, 10, 1, entityGroup)\n\t\tfor _, group := range entityPageGroup {\n\t\t\tif err := f.client.Entity.Update().\n\t\t\t\tWhere(group).\n\t\t\t\tAddReferenceCount(-1 * ref).\n\t\t\t\tExec(ctx); err != nil {\n\t\t\t\treturn nil, nil, fmt.Errorf(\"failed to decrease reference count for entities %v: %w\", group, err)\n\t\t\t}\n\t\t}\n\t}\n\n\t// 2. Filter out entities with <=0 reference count, Update recycle options for above entities;\n\tentityGroup, _ := f.batchInConditionEntityID(intsets.MaxInt, 10, 1, uniqueEntities)\n\ttoBeRecycled := make([]*ent.Entity, 0, len(entities))\n\tfor _, group := range entityGroup {\n\t\te, err := f.client.Entity.Query().Where(group).Where(entity.ReferenceCountLTE(0)).All(ctx)\n\t\tif err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"failed to query orphan entities %v: %w\", group, err)\n\t\t}\n\n\t\ttoBeRecycled = append(toBeRecycled, e...)\n\t}\n\n\t// 3. Update recycle options for above entities;\n\tpageSize := capPageSize(f.maxSQlParam, intsets.MaxInt, 10)","sourceCodeStart":529,"sourceCodeEnd":565,"githubUrl":"https://github.com/cloudreve/cloudreve/blob/20c95ad73f3a8bcb72887fea91ff31ab24fa1011/inventory/file.go#L529-L565","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nrecycled, diff, err := inv.Delete(ctx, files, props)\n\n// after\nvar recycled []*ent.Entity\nvar diff inventory.StorageDiff\nerr := retry.OnDeadlock(func() error {\n    recycled, diff, err = inv.Delete(ctx, files, props)\n    return err\n})","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if _, _, err := inv.Delete(ctx, files, props); err != nil {\n    if isDeadlock(err) {\n        time.Sleep(50 * time.Millisecond)\n        _, _, err = inv.Delete(ctx, files, props)\n    }\n    if err != nil { return err }\n}","preventionTips":["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)"],"tags":["go","ent","database","concurrency","reference-count"],"backgroundTag":null,"analyzedSha":"20c95ad73f3a8bcb72887fea91ff31ab24fa1011","analyzedAt":"2026-08-16T01:42:55.403Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}