Skip to content
This repository has been archived by the owner on Nov 4, 2021. It is now read-only.

Plugin Capabilities implementation #384

Merged
merged 25 commits into from
May 25, 2021
Merged

Plugin Capabilities implementation #384

merged 25 commits into from
May 25, 2021

Conversation

neilkakkar
Copy link
Contributor

@neilkakkar neilkakkar commented May 17, 2021

Changes

Adds support for Plugins Capabilities. Resolves #379

Companion PostHog/posthog PR (PostHog/posthog#4371) to be merged first.

Will write tests now, but just wanted to get a PoC out.

Checklist

  • Updated Settings section in README.md, if settings are affected
  • Jest tests

@mariusandra
Copy link
Collaborator

Having given it a quick glance, I think we should store a bit more metadata with the capabilities, mainly adding the type of the capability. Basically storing this in a JSON like: { jobs: ['runSomthing'], tasks: ['processEvent', 'setupPlugin'] } or something like that.

@neilkakkar neilkakkar changed the title WIP: Capabilities implementation Capabilities implementation May 18, 2021
@neilkakkar neilkakkar changed the title Capabilities implementation Plugin Capabilities implementation May 18, 2021
@neilkakkar neilkakkar marked this pull request as ready for review May 18, 2021 11:12
Copy link
Collaborator

@mariusandra mariusandra left a comment

Choose a reason for hiding this comment

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

Very cool! Few inline comments plus probably could use a merge from master.

tests/helpers/plugins.ts Outdated Show resolved Hide resolved
Comment on lines 65 to 71
await server.db.createPluginLogEntry(
pluginConfig,
PluginLogEntrySource.System,
PluginLogEntryType.Info,
`Set plugin capabilities (instance ID ${server.instanceId}).`,
server.instanceId
)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think if the capabilities haven't changed, there's no need to update them nor log that we set them. That would be needless logspam :)

Copy link
Collaborator

Choose a reason for hiding this comment

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

TBH I don't believe these logs are valuable user-side at all. Plugin loaded/unloaded events can be important because of plugin setup/teardown methods – while capabilities are just an internal optimization of ours.

Comment on lines 103 to 104
// infer on load implies there's no lazy loading, but all workers get
// these properties loaded
Copy link
Collaborator

Choose a reason for hiding this comment

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

We could lazy load them. We won't do anything with the loaded capabilities for the moment, and eventually set them in a separate "install" step anyway. No need to block for that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm, I could unblock them, but that still doesn't mean the VMs are lazy loaded right? since inferPluginCapabilities() will load the VM on startup, before the worker gets any event.

Maybe I should just remove this comment, just realised it's confusing w.r.t what is being lazy loaded.

Copy link
Collaborator

Choose a reason for hiding this comment

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

The funny code here is this:

void pluginConfig.vm?.initialize!(server, pluginConfig, plugin.source, pluginDigest(plugin))
await inferPluginCapabilities(server, pluginConfig, prevConfig)

The first function is run in the background, the second one we await for. The second function awaits for a promise (resolveInternalVm) that gets set when the first function that's running in the background finishes.

In essence, the plugin is not lazy anymore, but we block until the VM setup has completed.

To get around it, we could just void the inferPluginCapabilities call and let them run in the background, however that also feels slightly hacky.

A possibly better idea is to put this inferPluginCapabilities inside the LazyVM.initialize function. There you probably won't need prevConfig either. Just check if the current pluginConfig.plugin.capabilities matches reality or not. If not, update.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

that.. makes a lot of sense. Ha, would've been nice if I made this leap myself when thinking about the implementation.

Come to think of it, I don't need the prevConfig in the current setup either, for similar reasons - I already have a pluginConfig.plugin.capabilities to check.

Copy link
Collaborator

@mariusandra mariusandra left a comment

Choose a reason for hiding this comment

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

Few more flyby comments and suggestions :)

src/types.ts Outdated Show resolved Hide resolved
src/worker/plugins/loadPlugin.ts Outdated Show resolved Hide resolved
await setPluginCapabilities(server, pluginConfig, capabilities)
}

pluginConfig.plugin!.capabilities = capabilities
Copy link
Collaborator

Choose a reason for hiding this comment

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

Here again the ! could cause subtle issues. After all, we're not sure this function will always be called with pluginConfigs that contain a plugin. It might be the case when we follow the current trail that leads to this function, but it might not be the case in the future.

Often the solution is to use a guard, such as if (!pluginConfig.plugin) { throw new Error('how the hell did you get here?') }, or just return, depends on the case. Guards are best placed at the top of a function as well.

src/worker/plugins/run.ts Outdated Show resolved Hide resolved
Copy link
Collaborator

@Twixes Twixes left a comment

Choose a reason for hiding this comment

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

Nice! Just some minor comments

src/worker/plugins/loadPlugin.ts Outdated Show resolved Hide resolved
src/worker/plugins/loadPlugin.ts Outdated Show resolved Hide resolved
src/worker/plugins/loadPlugin.ts Outdated Show resolved Hide resolved
Comment on lines 65 to 71
await server.db.createPluginLogEntry(
pluginConfig,
PluginLogEntrySource.System,
PluginLogEntryType.Info,
`Set plugin capabilities (instance ID ${server.instanceId}).`,
server.instanceId
)
Copy link
Collaborator

Choose a reason for hiding this comment

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

TBH I don't believe these logs are valuable user-side at all. Plugin loaded/unloaded events can be important because of plugin setup/teardown methods – while capabilities are just an internal optimization of ours.

src/types.ts Outdated Show resolved Hide resolved
@neilkakkar neilkakkar requested a review from Twixes May 20, 2021 12:29
Copy link
Collaborator

@Twixes Twixes left a comment

Choose a reason for hiding this comment

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

Stamp of approval

expect(pluginConfig.plugin!.capabilities!.methods!.sort()).toEqual([
'onSnapshot',
'processEvent',
'processEventBatch',
Copy link
Collaborator

Choose a reason for hiding this comment

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

It seems a bit quirky that processEventBatch is listed as a capability when it's only an autogenerated version based on actually provided processEvent, but that seems OK

Copy link
Collaborator

@mariusandra mariusandra left a comment

Choose a reason for hiding this comment

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

Still some inline comments to check :)

src/worker/plugins/loadPlugin.ts Outdated Show resolved Hide resolved
Comment on lines 103 to 104
// infer on load implies there's no lazy loading, but all workers get
// these properties loaded
Copy link
Collaborator

Choose a reason for hiding this comment

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

The funny code here is this:

void pluginConfig.vm?.initialize!(server, pluginConfig, plugin.source, pluginDigest(plugin))
await inferPluginCapabilities(server, pluginConfig, prevConfig)

The first function is run in the background, the second one we await for. The second function awaits for a promise (resolveInternalVm) that gets set when the first function that's running in the background finishes.

In essence, the plugin is not lazy anymore, but we block until the VM setup has completed.

To get around it, we could just void the inferPluginCapabilities call and let them run in the background, however that also feels slightly hacky.

A possibly better idea is to put this inferPluginCapabilities inside the LazyVM.initialize function. There you probably won't need prevConfig either. Just check if the current pluginConfig.plugin.capabilities matches reality or not. If not, update.

await setupPlugins(mockServer)
const newPluginConfig = mockServer.pluginConfigs.get(39)!

expect(newPluginConfig.plugin!.capabilities).toBe(pluginConfig.plugin!.capabilities)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's also check that:

    expect(newPluginConfig.plugin).not.toBe(pluginConfig.plugin)

otherwise the capabilities are the same because it's literally the same object :)

@@ -38,7 +39,7 @@ afterEach(async () => {
})

test('setupPlugins and runProcessEvent', async () => {
getPluginRows.mockReturnValueOnce([plugin60])
getPluginRows.mockReturnValueOnce([{ ...plugin60 }])
Copy link
Contributor Author

Choose a reason for hiding this comment

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

it took me more than an hour on Friday night to figure out this was the issue with tests! :/ (since it affected plugin60 directly, and wouldn't show up if I simply ran the tests in isolation) - only when things run in sequence did this bork things up. 🤦 .

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ooh... that's a nice rabbit hole :D

Copy link
Collaborator

@mariusandra mariusandra left a comment

Choose a reason for hiding this comment

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

Looks good to me!

@mariusandra mariusandra added the bump patch Bump patch version when this PR gets merged label May 25, 2021
@mariusandra mariusandra enabled auto-merge (squash) May 25, 2021 11:20
@neilkakkar neilkakkar disabled auto-merge May 25, 2021 12:52
@neilkakkar neilkakkar enabled auto-merge (squash) May 25, 2021 12:55
@neilkakkar neilkakkar merged commit 09f1c34 into master May 25, 2021
@neilkakkar neilkakkar deleted the capabilities branch May 25, 2021 13:02
fuziontech pushed a commit to PostHog/posthog that referenced this pull request Oct 12, 2021
…rver#384)

* add tests for plugin capabilities
* address comments, update tests
* clean up
* update capabilities type definition
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bump patch Bump patch version when this PR gets merged
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Plugin Capabilities
3 participants