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

Added hook to lifecycle events #1150

Open
wants to merge 10 commits into
base: main
Choose a base branch
from

Conversation

akbashev
Copy link
Contributor

Ability to listen to actors lifecycles in a plugin.

Motivation:

Plugins give an option to extend system with some logic. Problem is sometimes you need to hook into actor's creation/deletion, which currently is impossible. As an example—for actor recovery we need to restore state when it's created and ready for cluster system, which can be done by some potential EventSourcingJournal.

By adding simple hook protocol we can enable this.

Modifications:

  • PluginActorLifecycleHook protocol
  • additional actorLifecycleHooks in plugins settings
  • inside ClusterSystem's actorReady and resignID are called for each hookable plugin added to the system.

Comment on lines 119 to 122
public protocol PluginActorLifecycleHook {
func actorReady<Act: DistributedActor>(_ actor: Act) where Act: DistributedActor, Act.ID == ClusterSystem.ActorID
func resignID(_ id: ClusterSystem.ActorID)
}
Copy link
Contributor Author

@akbashev akbashev Apr 16, 2024

Choose a reason for hiding this comment

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

Explicitly made non async as it's called synchronously in system.
This means though that if journal is an actor—those functions will be nonisolated. Thinking now if it's ok or not 🤔

Copy link
Member

@ktoso ktoso Apr 17, 2024

Choose a reason for hiding this comment

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

yeah those funcs being synchronous is good -- the plugin may have to use external synchronization, that's ok I guess -- we don't have a choice since the hooks are synchronous.

Naming nitpick, let's call them onActorReady and onResignID so it's easier to know which one we mean when we se this in code

@akbashev
Copy link
Contributor Author

Still not sure about PluginActorLifecycleHook name 😅

@@ -21,6 +21,7 @@ public struct PluginsSettings {
}

internal var plugins: [BoxedPlugin] = []
internal var actorLifecycleHooks: [PluginActorLifecycleHook] = []
Copy link
Member

Choose a reason for hiding this comment

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

the front of the name is good; but maybe PluginActorLifecyclePlugin?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Still not sure, maybe more like ActorLifecycleAwarePlugin? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

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

or just LifecyclePlugin... 🤔

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 see usage of ActorLifecycle term in the system, so will just call ActorLifecyclePlugin.

await fulfillment(of: [hookFulfillment])
}

actor TestClusterHookPlugin: _Plugin, PluginActorLifecycleHook {
Copy link
Member

Choose a reason for hiding this comment

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

Oh plugin was hidden still huh. About time to make it public -- maybe as we cleanup the how we use them with your work here!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🫡
Will remove _ from Plugin

@@ -114,3 +114,9 @@ internal struct AnyPluginKey: Hashable, CustomStringConvertible {
}
}
}

/// Way to hook into ClusterSystem's actor lifecycle events, specifically to `ready` and `resign`.
public protocol PluginActorLifecycleHook {
Copy link
Member

Choose a reason for hiding this comment

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

Should this extend _Plugin?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, so your idea is that it's still a plugin. Hm 🤔 I was more going into direction if you want to extend your current plugin, you can just conform it to additional protocol, like class CustomPlugin: Plugin, ActorLifecyleHookable or something.
Though also making it directly a plugin also makes sense, you basically distinguish between normal and hookable plugins 🤔

}

nonisolated func actorReady<Act>(_ actor: Act) where Act: DistributedActor, Act.ID == DistributedCluster.ClusterSystem.ActorID {
Task { try await self.onActorReady(actor) }
Copy link
Member

Choose a reason for hiding this comment

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

probably make use of a lock inside here and make it a class to avoid the racy nature of Task {}

Copy link
Contributor Author

@akbashev akbashev Apr 17, 2024

Choose a reason for hiding this comment

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

This is why I was worried about sync functions in protocol. Working with actors you'll mostly land in async anyway or your plugin could be an actor. And then you'll have nonisolated func actorReady with Task or some later stage.
Which is also ok though if you handle it inside your plugin I guess 🤔

Copy link
Member

Choose a reason for hiding this comment

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

I know this is a very old discussion, but to clarify: the actor ready must be synchronous because we support synchronous init() in actors.

Copy link
Member

@ktoso ktoso left a comment

Choose a reason for hiding this comment

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

Very good!

Minor nitpicks.

Documentation we can add once we document _Plugin itself I suppose :)

@akbashev akbashev requested a review from ktoso April 17, 2024 09:58
@ktoso
Copy link
Member

ktoso commented May 16, 2024

@swift-server-bot test this please

@ktoso
Copy link
Member

ktoso commented May 16, 2024

@swift-server-bot add to allowlist

@ktoso
Copy link
Member

ktoso commented Oct 11, 2024

@swift-server-bot test this please

1 similar comment
@akbashev
Copy link
Contributor Author

@swift-server-bot test this please

@akbashev akbashev force-pushed the plugin_lifecycle_hook branch from 1198b76 to 1679399 Compare October 31, 2024 22:35
@akbashev
Copy link
Contributor Author

@ktoso thx for merging main, fixed issue with soundness, now should be fine

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.

2 participants