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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] 馃З Plugins #15

Closed
jodydonetti opened this issue Apr 25, 2021 · 3 comments
Closed

[FEATURE] 馃З Plugins #15

jodydonetti opened this issue Apr 25, 2021 · 3 comments
Assignees
Labels
enhancement New feature or request

Comments

@jodydonetti
Copy link
Collaborator

jodydonetti commented Apr 25, 2021

Scenario

While playing with the implementation of the backplane (see #11) and talking about adding metrics (see #9) the need emerged to be able to add functionalities around FusionCache via external code.

In some cases a specific interface is needed because it is a core part of the system (like the already existing IFusionCacheSerializer), but in a lot of other cases a more generic one would probably suffice.

The objective is to:

  • let other people extend FusionCache with custom needs for their projects
  • open the door to more contributions by the community for common functionalities not present in the core packages

Proposal

The idea is to create a plugin subsystem where multiple plugins can be implemented using a common interface that would allow the coordination with a FusionCache instance.

As a first draft I'm thinking about something like this:

public interface IFusionCachePlugin
{
    void Start(IFusionCache cache);
    void Stop(IFusionCache cache);
}

In the Start method a plugin implementer will receive a cache instance to then, for example, subscribe to the events they'd like (see #14) or do something else, while in the Stop they can remove the events subscriptions they've created before, to keep a system clean.

A plugin will be added to a cache with a method like IFusioCache.AddPlugin(IFusionCachePlugin plugin), similarly to the one for the distributed cache which would add the plugin instance to an internal list of plugins, and call its Start method. In the same vein, a method IFusioCache.RemovePlugin(IFusionCachePlugin plugin) can be called to remove a plugin (which in turn will call the Stop method on the plugin) for housekeeping purposes.

Dependency Injection

In a DI scenario the method will be called automatically for all the registered services implementing the IFusionCachePlugin type, like what is already happening for the IDistributedCache type here, but with potentially multiple implementations.

The code may be something like this:

[...]
var plugins = serviceProvider.GetServices<IFusionCachePlugin>();
foreach (var plugin in plugins)
{
    cache.AddPlugin(plugin);
}
[...]

Specialized plugin types

In case specific functionalities may be needed by FusionCache, a more specialized plugin type may be created simply inheriting from the base IFusionCachePlugin type and adding the specific apis needed.

If, for example, the metrics plugins need a special metrics-related method to be called for metrics-related things, we would have something like this:

public interface IFusionCacheMetricsPlugin:
    : IFusionCachePlugin
{
    Task<bool> DoMetricsStuffAsync(int param1, string param2, [etc...]);
}

NOTE: I'm still playing with the backplane impl, and it could very well fit into this as a "normal" plugin.

Other stuff to decide

Should there be a way to identify a plugin, apart from its clr type? Something like a string Id { get; set; }? It may be useful in some contexts (eg: logging).

Thoughts?

Any suggestion is more than welcome.

@jodydonetti
Copy link
Collaborator Author

I just published a new branch with the plugins subsystem here.

How to create a plugin

Write a class that implements the IFusionCachePlugin interface, basically just the Start and Stop methods, like this.

Of course it can also accept its own set of options, typically modelled via IOptions<MyPluginType> and friends.

How to use a plugin via DI

Register your plugin type in the DI container (better before registering FusionCache itself) to respond to the IFusionCachePlugin service as a singleton, like here: when a FusionCache instance will be requested, all plugin types registered will be auto-discovered, added (and started) automatically.

Of course you can also define your own custom ext method (like this one) to be a better .NET citizen.

Example:

[...]
services.AddSingleton<IFusionCachePlugin, MyPlugin>();
services.AddFusionCache();
[...]

or with a custom ext method + custom options:

[...]
services.AddMyFusionCachePlugin(options => {
  options.Whatever = 42;
});
services.AddFusionCache();
[...]

How to use a plugin without DI

Just create an instance of your plugin and pass it to the cache.AddPlugin method, like here.

Remember that, in case the Start method of your plugin throws and exception, an InvalidOperationException would also be thrown by the AddPlugin method itself (with the original exception as the inner one).

Removing a plugin

If you need continue using a FusionCache instance but want to remove a plugin you've previously added, you can call the cache.RemovePlugin method: it will automatically call the Stop method of the plugin and then remove it.

If instead you just want to clean things up "at the end" (and you should do it), you don't have to do anything because when the FusionCache instance will be disposed, all added plugins will be stopped and removed automatically.

Opinions?

Anything would be appreciated, thanks!

@jodydonetti
Copy link
Collaborator Author

Pinging @JoeShook because of course 馃槃

@jodydonetti jodydonetti unpinned this issue Jul 18, 2021
@jodydonetti
Copy link
Collaborator Author

The plugins subsystem has been released with v0.1.5 馃帀

@jodydonetti jodydonetti changed the title 馃З Plugins [FEATURE] 馃З Plugins Dec 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant