From ec0ea3b8cc58b8a99a8e45c71a4cec7bf4c64c7f Mon Sep 17 00:00:00 2001 From: Praveen Gajulapalli <13733716+pkgajulapalli@users.noreply.github.com> Date: Mon, 10 Aug 2026 16:44:51 +0530 Subject: [PATCH] fix(lock): name the cause on FAILED_TO_RELEASE in StorageBasedLockProvider Three distinct failures in StorageBasedLockProvider#unlock() threw a byte-identical HoodieLockException message, so logs could not tell them apart. All three also share a single updateLockReleaseFailureMetric counter, leaving no way to attribute a release failure to a cause. Each throw now names its cause, and each logs the context needed to act on it: - HEARTBEAT_STOP_FAILED: the heartbeat task would not stop, so the lock is deliberately left un-expired (the task could still renew it after we return). Logs the interrupted flag to separate the two sub-cases in LockProviderHeartbeatManager#stopHeartbeat. - INTERRUPTED_DURING_THROTTLE_BACKOFF: interrupted mid-backoff. Now also passes the InterruptedException so the stack trace survives. - THROTTLE_RETRIES_EXHAUSTED vs EXPIRE_WRITE_FAILED: distinguishes an exhausted retry budget against a storage rate limit (e.g. the GCS 1-write/sec per-object limit) from a terminal UNKNOWN_ERROR / ACQUIRED_BY_OTHERS outcome. The four cause strings are declared as constants next to the other lock tunables, so the full set is visible in one place and both the call sites and the test assertions reference them rather than raw literals. On ACQUIRED_BY_OTHERS, also log how long ago our lease should have ended. A positive value means we overran our own lease, pointing at a starved heartbeat (long GC, thread-pool starvation); a negative value means the lease had not elapsed by our clock, pointing at clock skew between nodes instead. Those two causes are indistinguishable today and call for different fixes. Every new message carries lockFilePath, so a lock left dangling in storage can be joined back to the writer that failed to release it. Behaviour is unchanged: control flow is untouched, and each edit either adds a logger.error call or appends ", cause