pjfanning commented on code in PR #3699:
URL: https://github.com/apache/pekko/pull/3699#discussion_r4367733151


##########
cluster-tools/src/test/scala/org/apache/pekko/cluster/singleton/ClusterSingletonLeaseSpec.scala:
##########
@@ -217,6 +219,28 @@ class ClusterSingletonLeaseSpec extends 
PekkoSpec(ConfigFactory.parseString("""
       }
     }
 
+    "log error when lease release fails leaving oldest" in {
+      val lifecycleProbe = TestProbe()
+      val settings = nextSettings()
+      system.actorOf(
+        ClusterSingletonManager.props(Props(new 
ImportantSingleton(lifecycleProbe.ref)), PoisonPill, settings),
+        settings.singletonName)
+      val testLease = awaitAssert {
+        testLeaseExt.getTestLease(leaseNameFor(settings))
+      } // allow singleton manager to create the lease
+      testLease.probe.expectMsg(AcquireReq(leaseOwner))
+      testLease.initialPromise.complete(Success(true))
+      lifecycleProbe.expectMsg("preStart")
+      testLease.setNextReleaseResult(Future.failed(TestException("no release 
for you")))

Review Comment:
   This leaves the `TestLease` set to fail every subsequent `release()`, and 
the manager keeps running: after the singleton terminates it re-acquires with 
the already-completed `true` result and goes back to `Oldest`. Any later 
release, e.g. during spec shutdown, would then log an unexpected 
`TestException` error outside the `EventFilter`. Resetting it with 
`testLease.setNextReleaseResult(Future.successful(true))`, or stopping the 
manager at the end of the test, would keep it isolated.



##########
cluster-tools/src/main/scala/org/apache/pekko/cluster/singleton/ClusterSingletonManager.scala:
##########
@@ -1236,7 +1236,9 @@ class ClusterSingletonManager(singletonProps: Props, 
terminationMessage: Any, se
       lease.foreach { l =>
         logInfo("Releasing lease as leaving Oldest")
         import context.dispatcher
-        pipe(l.release().map(ReleaseLeaseResult(_))).to(self)
+        pipe(l.release().map[Any](ReleaseLeaseResult(_)).recover {

Review Comment:
   The release result still goes to `self`, so it can be lost on the 
self-downed path. `Oldest` -> `Stopping` fires this release; if the singleton 
terminates quickly, `Stopping` calls `stop()` before the lease backend's future 
fails. `ReleaseLeaseFailure` then goes to dead letters, and because it extends 
`DeadLetterSuppression` nothing is logged. That is the downing/hand-over case 
where this diagnostic matters most. Could the failure be logged directly in the 
future's `onComplete`, using a thread-safe `Logging(context.system, ...)` 
adapter, rather than routed through the actor?



##########
cluster-tools/src/main/scala/org/apache/pekko/cluster/singleton/ClusterSingletonManager.scala:
##########
@@ -1236,7 +1236,9 @@ class ClusterSingletonManager(singletonProps: Props, 
terminationMessage: Any, se
       lease.foreach { l =>
         logInfo("Releasing lease as leaving Oldest")
         import context.dispatcher
-        pipe(l.release().map(ReleaseLeaseResult(_))).to(self)
+        pipe(l.release().map[Any](ReleaseLeaseResult(_)).recover {

Review Comment:
   `.recover` only handles a failed `Future`. A `Lease` implementation whose 
`release()` throws synchronously (e.g. its client is not initialised) still 
throws inside `onTransition` and restarts the manager mid-hand-over, without 
this log line. Something like `Future.fromTry(Try(l.release())).flatten` would 
route that case to `ReleaseLeaseFailure` too.



##########
cluster-tools/src/main/scala/org/apache/pekko/cluster/singleton/ClusterSingletonManager.scala:
##########
@@ -1236,7 +1236,9 @@ class ClusterSingletonManager(singletonProps: Props, 
terminationMessage: Any, se
       lease.foreach { l =>
         logInfo("Releasing lease as leaving Oldest")
         import context.dispatcher
-        pipe(l.release().map(ReleaseLeaseResult(_))).to(self)
+        pipe(l.release().map[Any](ReleaseLeaseResult(_)).recover {
+          case t => ReleaseLeaseFailure(t)

Review Comment:
   This release/map/recover/pipe chain is now duplicated word for word in the 
`AcquiringLease` transition just above (lines 1227-1229). The two copies 
drifting apart is what caused this bug, so a small `releaseLease()` helper next 
to `tryAcquireLease` would keep them in sync, and the points in the other 
comments would then only need fixing once.
   
   Also, `tryAcquireLease` (line 848) uses `case NonFatal(t) =>` while both 
release paths use a catch-all `case t =>`. It would be good to use `NonFatal` 
consistently.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to