Skip to content

Conversation

@mdedetrich
Copy link
Contributor

@mdedetrich mdedetrich commented Nov 12, 2025

Adds the close method on ActorSystem (classic and typed) as well as implementing the AutoCloseable interface, work taken from #2479. Regardless of what happens with #2093, the changes in this PR are always valid (close is blocking and returns Unit/Void so its valid for both javadsl and scaladsl).

The PR also cleans up test code by removing the Await.result(system.close,...) pattern that is used everywhere.

An overloaded close(duration: Duration) method can be added in Pekko 2.0.0 if #2479 works out, as the scaladsl will use the Scala Duration and the javadsl will use the Java Duration.

@mdedetrich mdedetrich force-pushed the add-close-on-actorsystem branch 4 times, most recently from c370223 to 075b049 Compare November 12, 2025 15:39
@mdedetrich mdedetrich added this to the 1.3.0 milestone Nov 12, 2025
@mdedetrich mdedetrich force-pushed the add-close-on-actorsystem branch from 075b049 to 8188c2d Compare November 12, 2025 15:51
@raboof
Copy link
Member

raboof commented Nov 12, 2025

close is blocking and returns Unit/Void

Is there a requirement for close to block until the closing is 'complete'?

The PR also cleans up test code by removing the Await.result(system.close,...) pattern that is used everywhere.

I can see the appeal, though blocking is a bit 'uncharacteristic' for us. I guess because you're stopping the ActorSystem and thus are anyway 'on your way out' it might be fine?

@mdedetrich
Copy link
Contributor Author

mdedetrich commented Nov 12, 2025

Is there a requirement for close to block until the closing is 'complete'?

I believe so otherwise you can get weird behaviour but I will do research to confirm. From what I gather (and also when learning Kotlin and using stuff like Arrow Resource), AutoCloseable is kind of pointless if the close method is asynchronous, which is why you have stuff like https://docs.datastax.com/en/latest-java-driver-api/com/datastax/oss/driver/api/core/AsyncAutoCloseable.html

I can see the appeal, though blocking is a bit 'uncharacteristic' for us. I guess because you're stopping the ActorSystem and thus are anyway 'on your way out' it might be fine?

To clarify, if I replace Await.result with close its because the behaviour of blocking is exactly the same. In those cases, the only thing that would have changed would be the timeout (i.e. sometimes it was 15 seconds, sometimes 20 but these are all tests so it shouldn't be an issue anyways).

@mdedetrich mdedetrich force-pushed the add-close-on-actorsystem branch from 8188c2d to c873612 Compare November 12, 2025 16:15
@raboof
Copy link
Member

raboof commented Nov 12, 2025

Is there a requirement for close to block until the closing is 'complete'?

I believe so otherwise you can get weird behaviour but I will do research to confirm. From what I gather (and also when learning Kotlin and using stuff like Arrow Resource), AutoCloseable is kind of pointless if the close method is asynchronous

Why? When I want to make sure the cleanup is triggered but don't care too much when it happens, it might be fine?

which is why you have stuff like https://docs.datastax.com/en/latest-java-driver-api/com/datastax/oss/driver/api/core/AsyncAutoCloseable.html

It's an interesting data point that the close of that one is also blocking.

I can see the appeal, though blocking is a bit 'uncharacteristic' for us. I guess because you're stopping the ActorSystem and thus are anyway 'on your way out' it might be fine?

To clarify, if I replace Await.result with close its because the behaviour of blocking is exactly the same. In those cases, the only thing that would have changed would be the timeout (i.e. sometimes it was 15 seconds, sometimes 20 but these are all tests so it shouldn't be an issue anyways).

Sure, I meant blocking in the 'production' code is a bit uncharacteristic - in tests it's indeed definitely fine.

@mdedetrich
Copy link
Contributor Author

Why? When I want to make sure the cleanup is triggered but don't care too much when it happens, it might be fine?

From what I recall, if there is an error in cleaning up on close() its expected that an exception be thrown which is not possible if its asynchronous but I will look into it.

I would say personally that having close() cleaning up resources in an asynchronous manner would be surprising behaviour

@mdedetrich mdedetrich force-pushed the add-close-on-actorsystem branch from c873612 to 103cb63 Compare November 12, 2025 16:46
@mdedetrich
Copy link
Contributor Author

mdedetrich commented Nov 12, 2025

So regarding blocking on close(), the official documentation from https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html i.e.

See the Javadoc of the AutoCloseable and Closeable interfaces for a list of classes that implement either of these interfaces. The Closeable interface extends the AutoCloseable interface. The close method of the Closeable interface throws exceptions of type IOException while the close method of the AutoCloseable interface throws exceptions of type Exception. Consequently, subclasses of the AutoCloseable interface can override this behavior of the close method to throw specialized exceptions, such as IOException, or no exception at all.

And more directly from https://docs.oracle.com/javase/8/docs/api/java/lang/AutoCloseable.html

While this interface method is declared to throw Exception, implementers are strongly encouraged to declare concrete implementations of the close method to throw more specific exceptions, or to throw no exception at all if the close operation cannot fail.

Cases where the close operation may fail require careful attention by implementers. It is strongly advised to relinquish the underlying resources and to internally mark the resource as closed, prior to throwing the exception. The close method is unlikely to be invoked more than once and so this ensures that the resources are released in a timely manner. Furthermore it reduces problems that could arise when the resource wraps, or is wrapped, by another resource.

Implies that it is expected that if the close() method fails that an exception should be thrown. It is possible for ActorSystem.terminate() to fail and the only way to throw an exception in that case is to block on it.

Personally I was also apprehensive of implementing close() for this exact reason, blocking isn't entirely desirable but to me it seems expected that close() methods throw an exception if the closing of the resource fails.

@pjfanning
Copy link
Member

pjfanning commented Nov 12, 2025

Would it be acceptable to not put this on ActorSystem but create a factory method like ActorSystem.createWithBlockingClose() or ActorSystem.apply(withBlockingClose: Boolean = false)? The returned ActorSystem would mix in a close method.

We could also support mixing in a non-blocking close.

@mdedetrich
Copy link
Contributor Author

mdedetrich commented Nov 12, 2025

Would it be acceptable to not put this on ActorSystem but create a factory method like ActorSystem.createWithBlockingClose() or ActorSystem.apply(withBlockingClose: Boolean = false)?

Well I would still have to put it on ActorSystem otherwise AutoCloseable won't work, but I can make the blocking behaviour configurable (alternately it can go in reference.conf though given that close should block by default)

I think the point is, if you are going to use close() and don't want it to block, at that point you are already rolling your own resource cleaning mechanism and so close() + AutoCloseable is kind of pointless. There are also already methods that do close in an async manner (thats what .terminate() does).

This really does seem to be one of those cases where either we implement close as blocking by default, or we don't implement it at all as to "force"/encourage users to not block on ActorSystem termination. I still fall into the latter camp, but I do admit I haven't seen a case where a blocking ActorSystem.close() could cause issues in the common case (it can create deadlocks if you have blocking code in actors, which you shouldn't, but thats why the timeout is there).

@pjfanning
Copy link
Member

Would it be acceptable to not put this on ActorSystem but create a factory method like ActorSystem.createWithBlockingClose() or ActorSystem.apply(withBlockingClose: Boolean = false)?

Well I would still have to put it on ActorSystem otherwise AutoCloseable won't work, but I can make the blocking behaviour configurable (alternately it can go in reference.conf though given that close should block by default)>

I think the point is, if you are going to use close() and don't want it to block, at that point you are already rolling your own resource cleaning mechanism at which point close() + AutoCloseable is kind of pointless. There are also already methods that do close in an async manner (thats what .terminate() does).

A config option to decide on whether the close is blocking alongside a config to control the timeout for blocking seems ok.

@mdedetrich
Copy link
Contributor Author

A config option to decide on whether the close is blocking alongside a config to control the timeout for blocking seems ok.

Cool, Ill see feedback from others as well

@mdedetrich mdedetrich force-pushed the add-close-on-actorsystem branch 2 times, most recently from b4685cb to 306bc9c Compare November 12, 2025 20:25
@He-Pin
Copy link
Member

He-Pin commented Nov 13, 2025

Once get merged, this should be ported to 2.0.0

btw, @mdedetrich what's the status of kotlin dsl of Pekko?

override lazy val getWhenTerminated: CompletionStage[pekko.Done] =
whenTerminated.asJava

override def close(): Unit = system.close()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add @since 1.3.0

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as #2486 (comment), this is not part of public API

# Terminate the ActorSystem in the last phase actor-system-terminate.
terminate-actor-system = on

# The timeout that will be used when calling .close on an ActorSystem
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add since 1.3.0

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have an example of what this is meant to look like?

@mdedetrich
Copy link
Contributor Author

btw, @mdedetrich what's the status of kotlin dsl of Pekko?

I have some good news on this front, there is an official sbt kotlin plugin by jetbrains so it should be largely trivial https://github.com/JetBrains/sbt-kotlin-plugin

The thing is that its blocked by #2093 as kotlion also has its own way of handling async IO (it has suspend functions)

@mdedetrich
Copy link
Contributor Author

I will wait a few days to see if @raboof responds and also his views on making the blocking nature configurable

@mdedetrich mdedetrich force-pushed the add-close-on-actorsystem branch from 306bc9c to eca63a0 Compare November 13, 2025 08:29
@mdedetrich
Copy link
Contributor Author

I have just updated the PR to actually add tests to make sure the AutoCloseable works as expected. There is a Java test that uses try-with-resources and there is a Scala test using scala.util.Using (which automatically uses AutoCloseable).

One note is that when I cherry pick this into Pekko 1.3.x, the scala.util.Using tests will only work for Scala 2.13/Scala 3 so I will need to modify the tests there.

@mdedetrich mdedetrich force-pushed the add-close-on-actorsystem branch 2 times, most recently from 55746f1 to 46dd388 Compare November 13, 2025 09:00
@mdedetrich mdedetrich force-pushed the add-close-on-actorsystem branch from 46dd388 to 7df9fb8 Compare November 13, 2025 09:01
@pjfanning pjfanning modified the milestones: 1.3.0, 2.0.0-M1 Nov 13, 2025
Copy link
Member

@pjfanning pjfanning left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@He-Pin
Copy link
Member

He-Pin commented Nov 15, 2025

@mdedetrich feel free to merge this and send a backport to 1.3.x

@He-Pin He-Pin merged commit d5ee8a6 into apache:main Nov 15, 2025
9 checks passed
@He-Pin
Copy link
Member

He-Pin commented Nov 15, 2025

I will do the backporting

@mdedetrich mdedetrich deleted the add-close-on-actorsystem branch November 15, 2025 17:34
@mdedetrich
Copy link
Contributor Author

I will do the backporting

Please let me do it as I have to modify a test

@He-Pin
Copy link
Member

He-Pin commented Nov 15, 2025

@mdedetrich Nice you are online, hands over~

mdedetrich added a commit to mdedetrich/pekko that referenced this pull request Nov 15, 2025
mdedetrich added a commit to mdedetrich/pekko that referenced this pull request Nov 15, 2025
mdedetrich added a commit to mdedetrich/pekko that referenced this pull request Nov 15, 2025
mdedetrich added a commit to mdedetrich/pekko that referenced this pull request Nov 15, 2025
mdedetrich added a commit to mdedetrich/pekko that referenced this pull request Nov 15, 2025
mdedetrich added a commit to mdedetrich/pekko that referenced this pull request Nov 15, 2025
mdedetrich added a commit to mdedetrich/pekko that referenced this pull request Nov 15, 2025
mdedetrich added a commit that referenced this pull request Nov 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants