Skip to content

LeaderElectionManager invokes System.exit() directly #2496

Closed
@adriansuarez

Description

@adriansuarez

Bug Report

The LeaderElectionManager has a hardcoded invocation of System.exit(1) on stopLeading(), which is also a private method that cannot be overridden. This prevents me from managing the lifecycle of my own application, by installing a shutdown hook that invokes Operator.stop(), because a deadlock occurs when System.exit() is invoked by the LeaderElectionManager.

Even if I wasn't installing a shutdown hook, this would be undesirable behavior, because it prevents me from enabling leader election in unit tests.

What did you do?

I enabled leader election in my operator, where I also have a shutdown hook installed that invokes Operator.stop().

What did you expect to see?

I expected the Operator to shutdown cleanly.

What did you see instead? Under which circumstances?

The Operator hangs on shutdown.

Environment

Docker Desktop / Kubernetes 1.29.2

Java Operator SDK 4.9.2

$ java -version
openjdk version "17.0.10" 2024-01-16
IBM Semeru Runtime Open Edition 17.0.10.0 (build 17.0.10+7)
Eclipse OpenJ9 VM 17.0.10.0 (build openj9-0.43.0, JRE 17 Mac OS X amd64-64-Bit Compressed References 20240116_636 (JIT enabled, AOT enabled)
OpenJ9   - 2c3d78b48
OMR      - ea8124dbc
JCL      - 2aad089841f based on jdk-17.0.10+7)
$ kubectl version
Client Version: v1.29.2
Kustomize Version: v5.0.4-0.20230601165947-6ce0bf390ce3
Server Version: v1.29.2

Possible Solution

I worked around this by adding LeaderCallbacks to the leader election configuration that throw an exception before the System.exit() call can happen.

        var operator = new Operator(overrider -> {
            var callbacks = new LeaderCallbacks(() -> {
            }, () -> {
                // throw an exception to prevent hardcoded System.exit() invocation from
                // happening
                throw new RuntimeException(
                        "Preventing LeaderElectionManager.stopLeading() from invoking System.exit()");
            }, s -> {
            });
            var leaderElectionConfig = LeaderElectionConfigurationBuilder
                    .aLeaderElectionConfiguration(LEASE_NAME)
                    .withLeaderCallbacks(callbacks)
                    .build();
            overrider.withLeaderElectionConfiguration(leaderElectionConfig);
        });

This seems like a hack, but it works.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions