Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for return/ThrowBehavior of services #148

Merged
merged 4 commits into from
Aug 16, 2023

Conversation

FranzBusch
Copy link
Contributor

Motivation

Currently the ServiceGroup cancels the whole group when a service returns or throws. This is fine for most server applications but it doesn't work for CLI tools since they mostly want to use the group to orchestrate the services as long as the user command is handled and then return cleanly from the group. Furthermore, even on server setups one might want to customize the behavior of what happens when a service returns/throws e.g. one might want to shutdown the group after their HTTP server throws so that a telemetry service flushes out all the remaining data.

Modification

This PR does a few things:

  1. It creates a new ServiceConfiguration and TerminationBehavior in the ServiceGroupConfiguration. Those can be used to declare the services that are run and what happens when the service returns or throws
  2. Adds a new cancellationSignals to to the ServiceGroupConfiguration which allows cancellation to trigger based on a signal
  3. Makes sure that any given service is only retained as long as necessary i.e. when a service returns the group is not retaining it anymore. This allows freeing of resources as early as possible
  4. Breaking: Removes the Hashable conformance on the configuration structs. This was wrong in the first place and something we should just avoid doing in general.

Result

We now give the user even more control about the group's behaviors.

Comment on lines +175 to +176
let gracefulShutdownSignals = await UnixSignalsSequence(trapping: self.gracefulShutdownSignals)
let cancellationSignals = await UnixSignalsSequence(trapping: self.cancellationSignals)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Might be simpler to just create one sequence because both tasks for these are exactly the same...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried that first but it makes things a bit more complicated because right now I return from the child task on the first signal; however, we have to keep listening to cancellation signals once we get a graceful shutdown signal.

Sources/ServiceLifecycle/ServiceGroup.swift Outdated Show resolved Hide resolved
Sources/ServiceLifecycle/ServiceGroup.swift Outdated Show resolved Hide resolved
Comment on lines 76 to 78
public var returnBehaviour: TerminationBehavior
/// The behavior when the service throws from its `run()` method.
public var throwBehaviour: TerminationBehavior
Copy link
Collaborator

Choose a reason for hiding this comment

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

You need to use the US spelling for the properties here.

Copy link
Collaborator

Choose a reason for hiding this comment

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

The naming here doesn't quite sit right with me; it's very strongly tied to language keywords rather than the behaviour of the service runner.

Not sure exactly what to suggest but the idea of a 'clean exit' vs. 'unclean exit' might be worth exploring.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Adopted the US spelling. I am really struggling to come up with something better.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved to use successfulTerminationBehavior and failureTerminationBehavior

Sources/ServiceLifecycle/ServiceGroupConfiguration.swift Outdated Show resolved Hide resolved
@FranzBusch FranzBusch force-pushed the fb-return-behaviors branch from 757b3fb to 822f4f6 Compare August 14, 2023 13:09
# Motivation

Currently the `ServiceGroup` cancels the whole group when a service returns or throws. This is fine for most server applications but it doesn't work for CLI tools since they mostly want to use the group to orchestrate the services as long as the user command is handled and then return cleanly from the group. Furthermore, even on server setups one might want to customize the behavior of what happens when a service returns/throws e.g. one might want to shutdown the group after their HTTP server throws so that a telemetry service flushes out all the remaining data.

# Modification

This PR does a few things:
1. It creates a new `ServiceConfiguration` and `TerminationBehavior` in the `ServiceGroupConfiguration`. Those can be used to declare the services that are run and what happens when the service returns or throws
2. Adds a new `cancellationSignals` to to the `ServiceGroupConfiguration` which allows cancellation to trigger based on a signal
3. Makes sure that any given service is only retained as long as necessary i.e. when a service returns the group is not retaining it anymore. This allows freeing of resources as early as possible
4. Breaking: Removes the `Hashable` conformance on the configuration structs. This was wrong in the first place and something we should just avoid doing in general.

# Result
We now give the user even more control about the group's behaviors.
@FranzBusch FranzBusch force-pushed the fb-return-behaviors branch from 822f4f6 to de13697 Compare August 14, 2023 13:10
@@ -80,7 +81,8 @@ extension UnixSignalsSequence {
signal(sig.rawValue, SIG_IGN)
#endif
return .init(
dispatchSource: DispatchSource.makeSignalSource(signal: sig.rawValue, queue: UnixSignalsSequence.queue),
// This force-unwrap is safe since Dispatch always returns a `DispatchSource`
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is true under the assumption that DispatchSource is currently the only type conforming to DispatchSourceSignal, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah and if we start to crash here then we will notice it and have to see what we get returned.

Sources/ServiceLifecycle/ServiceGroupConfiguration.swift Outdated Show resolved Hide resolved
Sources/ServiceLifecycle/ServiceGroupConfiguration.swift Outdated Show resolved Hide resolved
Sources/ServiceLifecycle/ServiceGroup.swift Outdated Show resolved Hide resolved
Tests/ServiceLifecycleTests/ServiceGroupTests.swift Outdated Show resolved Hide resolved
Tests/ServiceLifecycleTests/ServiceGroupTests.swift Outdated Show resolved Hide resolved
await XCTAsyncAssertEqual(await eventIterator1.next(), .runPing)

// The first service should now receive the signal
await XCTAsyncAssertEqual(await eventIterator1.next(), .shutdownGracefully)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Shouldn't we also be getting a runCancelled event after the second service exits?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good one but no we do not since a service returning when we are gracefully shutting down is expected.

@FranzBusch FranzBusch requested review from glbrntt and gjcairo August 16, 2023 08:54
Sources/ServiceLifecycle/ServiceGroupConfiguration.swift Outdated Show resolved Hide resolved
Sources/ServiceLifecycle/ServiceGroupConfiguration.swift Outdated Show resolved Hide resolved
self.cancellationSignals = cancellationSignals
}

@available(*, deprecated)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Will this be removed before 2.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.

We probably should.

@FranzBusch FranzBusch requested a review from glbrntt August 16, 2023 10:07
Copy link
Collaborator

@glbrntt glbrntt left a comment

Choose a reason for hiding this comment

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

LGTM!

@FranzBusch FranzBusch merged commit f58dbbf into main Aug 16, 2023
@FranzBusch FranzBusch deleted the fb-return-behaviors branch August 16, 2023 12:22
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.

3 participants