|
| 1 | +# Neptunus Plugins Model |
| 2 | + |
| 3 | +This section is for developers who want to create a new plugin. |
| 4 | + |
| 5 | +There a nine types of plugins and some of it works directly with channels (we call it streaming plugins), and some of it not (callable or child plugins). Start by looking at the interfaces that plugins must conform to and some base structs that must be embedded - [here](../core/plugin.go) and [here](../core/base.go). |
| 6 | + |
| 7 | +## Plugin lifecycle |
| 8 | + |
| 9 | +Any plugin lifecycle is `create` -> `init` -> `set channels (for streaming plugins)` -> `run/call` -> `close`, and the engine takes care of some stages. |
| 10 | + |
| 11 | +### Create |
| 12 | + |
| 13 | +So, at the creation stage engine do some work: |
| 14 | + - initialize and set embedded base plugin (already with logger, metric observer and other fields, except channels); |
| 15 | + - if required, set child plugins, already created and initialized; |
| 16 | + - decode configuration to plugin struct. |
| 17 | + |
| 18 | +Configuration decoder uses old good [mapstructure](https://github.com/mitchellh/mapstructure) with custom decode hooks - for time, duration and [datasize](https://pkg.go.dev/kythe.io/kythe/go/util/datasize#Size). See [kafka](../plugins/inputs/kafka/) as an example of datasize usage. |
| 19 | + |
| 20 | +### Init and set channels |
| 21 | + |
| 22 | +If creation stage completed successfully, engine call `Init() error` plugin method - and it is the place where your plugin MUST create and check all needed resources (e.g. database connection) and validate provided config. If something goes wrong, you MUST free all resources and return error. If no error returned, plugin is considered ready to data processing. |
| 23 | + |
| 24 | +After that, the engine will create and set up channels. In most cases you don't need to think about it - base plugin handles `SetChannels(....)` call. |
| 25 | + |
| 26 | +### Run |
| 27 | + |
| 28 | +Then, if it is a streaming plugin, engine call `Run()` method and this method MUST be blocking. If it is a callable plugin, it will be called by it parent. |
| 29 | + |
| 30 | +Inside `Run()` loop: |
| 31 | + - if your plugin is an `input`, you need to write events into `Out` channel; |
| 32 | + - if it is a `filter`, plugin read incoming events from `In` channel, do some calculations, then write it to `Acc` if condition satisfied, or write to `Rej` if it's not; |
| 33 | + - if it is a `processor`, plugin read incoming events from `In`, do some work, then write it to `Out` or to `Drop`, if event no longer needed; |
| 34 | + - finally, if it is an `output`, you read events from `In`, write it to target, and when you done with it, write event into `Done` channel. |
| 35 | + |
| 36 | +So, the basic rule here is **any event MUST be sent to some channel in the end** because of [delivery control](DATA_MODEL.md#delivery-control). |
| 37 | + |
| 38 | +Also, it is your responsibility to write plugin metrics. The base struct contains `Observe()` func which accepts status (Accepted, Failed or Rejected - the last one should be used in filters only) and duration taken to process the event. |
| 39 | + |
| 40 | +You can find some heplers in [plugins/common/](../plugins/common/) dir, such as [Batcher](../plugins/common/batcher/), [Retryer](../plugins/common/retryer/) or [Pool](../plugins/common/pool/). |
| 41 | + |
| 42 | +### Close |
| 43 | + |
| 44 | +When the engine receives a signal to stop a pipeline, it calls inputs `Close() error` method. |
| 45 | + |
| 46 | +If your plugin is an `input`, you need to handle this call, free all resources and break the `Run()` loop. Do not close the output channel! Engine will do this automatically. |
| 47 | + |
| 48 | +If your plugin is a `filter`, `processor` or `output`, you must break the loop when plugin input channel closes. After that, the engine will call the `Close() error` method itself. |
| 49 | + |
| 50 | +There is no guarantee that the close method will be called exactly once, so it MUST be idempotent. |
| 51 | + |
| 52 | +If your streaming plugin uses some callable plugins, you need to close it in `Close() error`. |
0 commit comments