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

RFC: Redux on Plugin Selection (Routing) and Caching #539

Closed
pittma opened this issue Nov 24, 2015 · 5 comments
Closed

RFC: Redux on Plugin Selection (Routing) and Caching #539

pittma opened this issue Nov 24, 2015 · 5 comments
Labels

Comments

@pittma
Copy link
Contributor

pittma commented Nov 24, 2015

Introduction

When a task is started in snap, it begins by subscribing to a plugin pool. This pool holds references to running instances of a given plugin. If a subscription results in the need for a new plugin to be started, Snap's plugin runner starts one. We call this need eligibility. How plugins are chosen inside this pool we call routing.

In this specification I propose a redux on the routing module in Snap. This rethinking is predicated on the use cases enumerated in this document. The existing implementation of plugin routing in Snap is not well suited for these use cases, so the result would be an almost complete overhaul of plugin selection in Snap.

Prerequisite Changes

Snap

Right now, the apPool type is the first class object when selecting a plugin. I propose that a new type (router?, selector?), be the top-level type, and maintain the responsibilities of the current apPool: pooling, subscriptions, eligibility, and routing. Modularizing the entire pool rather than just the router provides the necessary tools needed for a good implementation. I propose that this implementation requires all of this state to make intelligent selection decisions.

Plugins

A plugin writer adds a new plugin.Meta key to their plugin's metadata called routing. This key contains a value from an enum defined in the plugin package. When a plugin is loaded its routing metadatum is applied to the selector for this plugin.

In order for the router to have access to the state it needs to correctly select the target plugin, new information will also have to be passed into the calls for Collect, Process, and Publish. I propose that a new parameter be added to these function signatures: taskId string.

Cache

Related to plugin routing is caching. Right now, the caching is contained in the client and caches metrics on a per metric namespace and version basis with a global expiration.

In order for caching to be correct and effective it should be moved within the new type described here. Further the call to this new type should expose CollectMetrics and not expose the running plugin or cache. With the cache being moved to this new proposed type a plugin author could expose a minimum cache TTL and snap will instantiate the appropriate cache type based on the routing strategy described here (per plugin type and version, per running plugin or per config).

Use cases

Case 1: The one-to-one (sticky)

Description

A task needs to return to the same plugin each time, say a processor which is computing moving averages. These MAs should not be polluted by other tasks data. Since the implementation of the plugin is not capable of differentiating between one task and another, that task should have exclusive access to this plugin.

Proposed Implementation

A plugin writer sets their routing value to plugin.StickyRouting. The task's ID can be used as a key for the plugin. If the task is new, a new plugin is started. If the task is new and it would cause the max pool size to be exceeded, an error is returned on task creation. This error should come from an eligible call to the routing implementation, and bubble all the way back up to API caller.

Limitations

Snap's goal in maintaining the smallest possible footprint has many facets, one of which is the upper bound on the number of plugin instances in a given pool. With a sticky router, the implementation must choose to either fail the creation of a task if this upper bound is exceeded, or exempt a selector which is of type sticky from this upper bound all together. I propose the former.

Case 2: The config based router

Description

Take a publisher which opens, and then maintains a connection to RabbitMQ. Tasks which share configuration data, like RabbitMQ node, credentials, and even queue/exchange data could share a plugin instance.

Proposed Implementation

A plugin writer sets their routing value to plugin.ConfigBasedRouting. This could be achieved by hashing the config data, and then using that hash as a key for selecting a plugin instance. This results in quite similar behavior to the sticky router described above, however it adds the advantage of avoiding the special exceptions for exceeding the max pool size.

Limitations

If a plugin does require differentiating between tasks, the config-based router puts the onus on the plugin author to implement this logic.

@pittma pittma changed the title RFC: Placeholder for Routing RFC: Redux on Plugin Selection (Routing) Dec 3, 2015
@jcooklin
Copy link
Collaborator

jcooklin commented Dec 3, 2015

Looks great @danielscottt.

As a related item, how do you feel about a separate feature that allows overriding max-running-plugin per plugin through the global config?

@tiffanyfay
Copy link
Contributor

This is really good.

@jcooklin
Copy link
Collaborator

Added Cache section to the spec.

@jcooklin jcooklin changed the title RFC: Redux on Plugin Selection (Routing) RFC: Redux on Plugin Selection (Routing and Cache) Dec 11, 2015
@jcooklin jcooklin changed the title RFC: Redux on Plugin Selection (Routing and Cache) RFC: Redux on Plugin Selection (Routing) and Caching Dec 11, 2015
jcooklin added a commit to jcooklin/snap that referenced this issue Dec 17, 2015
* Moves caching to the plugin pool (from plugin client)
* Moves routing strategy to the plugin pool (from availableplugins)

Resolves intelsdi-x#601
Partially addresses intelsdi-x#539
jcooklin added a commit to jcooklin/snap that referenced this issue Dec 17, 2015
* Moves caching to the plugin pool (from plugin client)
* Moves routing strategy to the plugin pool (from availableplugins)

Resolves intelsdi-x#601
Partially addresses intelsdi-x#539
jcooklin added a commit to jcooklin/snap that referenced this issue Dec 17, 2015
* Moves caching to the plugin pool (from plugin client)
* Moves routing strategy to the plugin pool (from availableplugins)

Resolves intelsdi-x#601
Partially addresses intelsdi-x#539
jcooklin added a commit to jcooklin/snap that referenced this issue Dec 18, 2015
* Moves caching to the plugin pool (from plugin client)
* Moves routing strategy to the plugin pool (from availableplugins)

Resolves intelsdi-x#601
Partially addresses intelsdi-x#539
jcooklin added a commit to jcooklin/snap that referenced this issue Dec 18, 2015
* Moves caching to the plugin pool (from plugin client)
* Moves routing strategy to the plugin pool (from availableplugins)

Resolves intelsdi-x#601
Partially addresses intelsdi-x#539
jcooklin added a commit to jcooklin/snap that referenced this issue Dec 18, 2015
* Moves caching to the plugin pool (from plugin client)
* Moves routing strategy to the plugin pool (from availableplugins)

Resolves intelsdi-x#601
Partially addresses intelsdi-x#539
jcooklin added a commit to jcooklin/snap that referenced this issue Dec 18, 2015
* Moves caching to the plugin pool (from plugin client)
* Moves routing strategy to the plugin pool (from availableplugins)

Resolves intelsdi-x#601
Partially addresses intelsdi-x#539
@marcin-krolik
Copy link
Collaborator

I would like to implement Case 2: The config based router. I analyzed current source code and tried to figure out how to implement that, but I think I don't quite understand one thing. @danielscottt suggested to hash config data to achieve proper config based routing but I can't figure out easy way to get config data when inserting new plugin to pool.
Pools method Insert(a AvailablePlugin)operates on AvailablePlugin type which does not store config data. Is there any way to achieve that without modifying AvailablePlugin interface? Or maybe I'm missing something trivial here?

@pittma
Copy link
Contributor Author

pittma commented Mar 29, 2016

Hi Marcin,

You could use incoming config on either collect, process, or publish calls. Process and Publish take config now, and collect would need to be updated to take a config.

The table inside the config-based router could be a map with where, on the left (the keys) could be the whole config, gob encoded, and then converted into a string, and on the right, the plugin which matches that config. Looking at the strategy as it exists now, I think an abstraction to the []SelectablePlugin type might be nice; something that is essentially iterable, so it could either be k/v or indexed.

Given that abstraction, I think the config-based strategy would also need a custom pool, which is building and storing the table as plugins are added, mainly because pool eligibility (does the pool need a new running plugin for this task) is dependent on whether or not there is a plugin available to serve the config-based request.

This change is fairly sweeping to the strategy package and the pieces of available_plugin.go which touch that package, but I think it's an awesome idea to go ahead and start work on this. It'll force us to think about the right abstractions for strategies, as this'll be the first one that will really have a custom implementation of these abstractions!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

4 participants