{"record":{"id":"ae75bb0e389f695a","repo":"jaegertracing/jaeger","slug":"failed-to-forfeit-resource-lock-w","errorCode":null,"errorMessage":"failed to forfeit resource lock: %w","messagePattern":"failed to forfeit resource lock: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/storage/distributedlock/cassandra/lock.go","lineNumber":76,"sourceCode":"\t\t\treturn false, fmt.Errorf(\"failed to extend lease on resource lock: %w\", err)\n\t\t}\n\t\treturn true, nil\n\t}\n\treturn false, nil\n}\n\n// Forfeit forfeits an existing lease around a given resource.\nfunc (l *Lock) Forfeit(resource string) (bool, error) {\n\tvar name, owner string\n\tapplied, err := l.session.Query(cqlDeleteLock, resource, l.tenantID).ScanCAS(&name, &owner)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"failed to forfeit resource lock due to cassandra error: %w\", err)\n\t}\n\tif applied {\n\t\t// The lock was successfully deleted\n\t\treturn true, nil\n\t}\n\treturn false, fmt.Errorf(\"failed to forfeit resource lock: %w\", errLockOwnership)\n}\n\n// extendLease will attempt to extend the lease of an existing lock on a given resource.\nfunc (l *Lock) extendLease(resource string, ttl time.Duration) error {\n\tttlSec := int(ttl.Seconds())\n\tvar owner string\n\tapplied, err := l.session.Query(cqlUpdateLock, ttlSec, l.tenantID, resource, l.tenantID).ScanCAS(&owner)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif applied {\n\t\treturn nil\n\t}\n\treturn errLockOwnership\n}\n","sourceCodeStart":58,"sourceCodeEnd":92,"githubUrl":"https://github.com/jaegertracing/jaeger/blob/806f4447841ecdb60519f408b004a599d515f437/internal/storage/distributedlock/cassandra/lock.go#L58-L92","documentation":"Lock.Forfeit returns this when the conditional DELETE was applied=false, i.e. the lock exists but is owned by a different tenant. It always wraps errLockOwnership ('this host does not own the resource lock'), so errors.Is(err, errLockOwnership) matches.","triggerScenarios":"Calling Forfeit(resource) when another tenantID currently holds the lease on that resource (or the row's owner value doesn't match this Lock's tenantID). Not a Cassandra failure — the CAS simply didn't match.","commonSituations":"Two jobs using different tenant configs contending for the same resource name; a stale lock held by a crashed peer that had a different tenantID; caller attempting to release a lock it never acquired.","solutions":["Check errors.Is(err, errLockOwnership) and treat as 'not owner' — don't crash, just skip release.","Ensure all instances that contend for the same resource use the same tenantID.","Wait for the lease TTL to expire so the lock becomes free, then reacquire.","Verify you only call Forfeit for resources you successfully acquired in the same process/config.","Use unique resource names per logical job to avoid cross-tenant contention."],"exampleFix":"// before\nif _, err := lock.Forfeit(\"index-cleaner\"); err != nil {\n    return err // fails when another peer holds the lock\n}\n// after\nif _, err := lock.Forfeit(\"index-cleaner\"); err != nil {\n    if errors.Is(err, errLockOwnership) {\n        log.Info(\"lock held by another peer; skipping forfeit\")\n        return nil\n    }\n    return err\n}","handlingStrategy":"type-guard","validationCode":"// only forfeit resources this process acquired and still owns\nif ownedResources[resource] != l.tenantID {\n    return nil // skip forfeit entirely\n}","typeGuard":"func isNotOwner(err error) bool {\n    return errors.Is(err, errLockOwnership) // wraps 'failed to forfeit resource lock: %w'\n}","tryCatchPattern":"_, err := lock.Forfeit(resource)\nswitch {\ncase err == nil:\n    // released\ncase errors.Is(err, errLockOwnership):\n    // held by another peer — expected, log and continue\ndefault:\n    return fmt.Errorf(\"forfeit failed: %w\", err)\n}","preventionTips":["Use a consistent tenantID across all instances contending for the same resource.","Track which resources you acquired and only forfeit those.","Treat 'not owner' as a normal outcome, not an error.","Let the lease TTL expire rather than force-releasing foreign locks."],"tags":["distributed-lock","lock-ownership","cassandra","contention"],"backgroundTag":"lock-not-owned","analyzedSha":"806f4447841ecdb60519f408b004a599d515f437","analyzedAt":"2026-09-01T02:39:22.140Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}