migesok commented on code in PR #2027:
URL: https://github.com/apache/pekko/pull/2027#discussion_r2272743628


##########
persistence/src/main/scala/org/apache/pekko/persistence/journal/AsyncWriteJournal.scala:
##########
@@ -67,9 +67,21 @@ trait AsyncWriteJournal extends Actor with WriteJournalBase 
with AsyncRecovery {
   final val receiveWriteJournal: Actor.Receive = {
     // cannot be a val in the trait due to binary compatibility
     val replayDebugEnabled: Boolean = config.getBoolean("replay-filter.debug")
+    val enableGlobalWriteResponseOrder: Boolean = 
config.getBoolean("write-response-global-order")
+
     val eventStream = context.system.eventStream // used from Future callbacks
     implicit val ec: ExecutionContext = context.dispatcher
 
+    // should be a private method in the trait, but it needs the 
enableGlobalWriteResponseOrder field which can't be
+    // moved to the trait level because adding any fields there breaks 
bincompat
+    def sendWriteResponse(msg: Any, snr: Long, target: ActorRef, sender: 
ActorRef): Unit = {

Review Comment:
   Unfortunately, no amount of annotations would save me from runtime class 
errors here.
   
   This method needs access to the "memoized" config option, which has to be a 
val/var/lazy val - enableGlobalWriteResponseOrder. Which I currently put under 
` final val receiveWriteJournal: Actor.Receive = {...}` scope. My first version 
had the field on the trait level as a private val - right under here:
   
https://github.com/apache/pekko/blob/e7e71a3adffdd8b19d800b22843780b54f56d4b8/persistence/src/main/scala/org/apache/pekko/persistence/journal/AsyncWriteJournal.scala#L60
   
   Then it lead to runtime issues:
   - you have a concrete plugin class extending AsyncWriteJournal built against 
the old version, without the new private val
   - you try to run it against the new AsyncWriteJournal version
   - adding a new private/packag-private/private[this] val to trait results in 
new abstract methods in the generated interface
   - classloading of the plugin class fails in runtime because an interface it 
implements has new abstract methods which the concrete class does not implement
   
   It seems I was not the first one to hit this issue in this trait:
   ```
     final val receiveWriteJournal: Actor.Receive = {
       // cannot be a val in the trait due to binary compatibility
       val replayDebugEnabled: Boolean = 
config.getBoolean("replay-filter.debug")
   ```
   
   TBH before this ordeal I wasn't aware that adding private fields to a trait 
breaks bincompat but I was able to confirm it on small examples:
   https://gist.github.com/migesok/71c5e5feb1ba4fff8c6515f42922df11
   
   Please let me know if there is a better way to structure it without breaking 
bincompat.



-- 
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