components

package
v0.0.0-...-8c998e4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 2, 2023 License: BSD-3-Clause Imports: 12 Imported by: 0

README

superwatcher components

This public package provides functions for initializing superwatcher components.

The internals of superwatcher is not stable yet, so we provide a separate and more stable package for creating new instances of the core components.

Why are there so many New.. functions?

There are currently 2-3 styles for creating new superwatcher components with the reason being no one uses it yet, and that leave us with too little feedback as to which style we should officially adapt.

We also experiment with meta packages like poller, emitter, emitterclient engine. See demo code here for different usage.

So this is why we have so many functions for creating new components, and most of them can be used interchangably, as there is no essential difference between any.

  1. Classic factory function call This is currently the preferred way to create new superwatcher components, because it is the safest bet for new users, especially with wrapped functions like NewEmitterWithPoller.

    Examples include functions like the factories NewEmitter, NewPoller, and other higher-level (wrapped) factories like NewDefault, NewEmitterWithPoller, or NewEngineWithEmitterClient.

    Because they have multiple types in the arguments, this can help prevent users from passing invalid/insufficient parameters.

    The test code use these types of functions.

  2. Variadic (spread) Option pattern This is a new way to init superwatcher components, for better DX. It is experimental.

    Examples from this package includes NewSuperWatcherOptions, NewEmitterOptions, [NewPollerOptions](./poller.go), although more examples exist in poller, emitter, emitterclient engine experimental packages.

    Unlike the classic method, this prettier style is more prone to user errors when calling, since these functions use spread variable of type Option.

  3. (proposed) Builder pattern This is a requested feature. By using builder pattern, we can chain a lot of method calls, which may feel more comfortable to some users.

Each style has its own goodies, hence why we still keep them around to see which one smells best all-around. We intend to keep the classic factories around because it offers the best customizability.

The builder patterned functions may never be implemented, because it would result in more complexity, on top of the 4 components' own complex structure. The experimental top-level meta-packages like [poller] may also be considered noisy, dupicate and thus removed,

Preferred factory functions (Jan 2023)

NewDefault

The preferred way to use this package is to call NewDefault, which returns a full, default superwatcher.Emitter and superwatcher.Engine.

The function creates required channels, as well as secondary components like superwatcher.Poller and superwatcher.EmitterClient for caller in the background, while only returning the superwatcher.Emitter, and superwatcher.Engine, hiding away other advanced types involved to avoid cluttering.

If you know what you are doing, then you can create each individual component manually. Make sure to connect the all components together before you start calling Loop on both Emitter and Engine.

NewSuperWatcherDefault and NewSuperWatcher

This package also defines type superWatcher, which implements superwatcher.SuperWatcher. This type encapsulates all other internal types' methods in *superWatcher.Run method, which starts superwatcher.Emitter and superwatcher.Engine concurrently.

To use type superWatcher, call either NewSuperWatcherDefault or NewSuperWatcher.

Initializing only parts of superwatcher

The 4 components

In fact, there're 4 components of superwatcher working together if you create superwatcher components with NewDefault, although most users will most likely interact with just 2 components, superwatcher.Emitter and superwatcher.Engine, so let's call these 2 preferred components.

The other components, namely, superwatcher.EmitterPoller and superwatcher.EmitterEngine, are usually left alone as they are embedded in Emitter and Engine respectively. Let's call these components secondary components.

If your code wants to take full advantage with superwatcher, then you're likely to create all 2 major components with secondary components embedded.

But if you want to minimize your code base to superwatcher coupling, then you might want to only use a few types here and there.

To know which types to use, let's first have a look at these 4 interface types and its implementations:

  1. superwatcher.EmitterPoller (poller.poller, embedded in emitter.emitter) The poller polls event logs from blockchain in [poller.go(../internal/poller/poll.go)], processing the block hashes to detect chain reorgs, and returns to caller.

  2. superwatcher.Emitter (emitter.emitter) The emitter emits result of poller.Poll to Go channels, and decides which block range the poller should poll.

  3. superwatcher.EmitterClient (emitterclient.emitterClient, embedded in engine.engine) The emitter client receives result emitted from the emitter, and checks if the result was properly handled/sent, and it syncs the emitter with engine running concurrently.

  4. superwatcher.Engine (engine.engine and thinengine.thinEngine) The engine consumes data from the emitter via the emitter client, and, depending on the concrete type, may manage the result metadata for service code so that everything is handled correctly and efficiently.

As you can see, each has their own responsibility, and by separating emitter and poller, we can better test emitter-specific logic.

Another benefit is that users can just use poller (which to me is the highlight for superwatcher) for their code if they are not into the other components, or if embedding it into the emitter seems too complex for certain tasks.

If you only need some code that would filter event logs and detect chain reorg for you, you can just initialize the poller, and call poller.Poll to get PollerResult.

If you only want some code that would perform poller's tasks, but also have superwatcher manage and progresses fromBlock and toBlock, then you'd only need to initialize emitter (with the poller), and received the results manually with channels while ditching the engine altogether.

And if you don't want to receive from channels manually, you can add EmitterClient to the equation.

And finally, if you only want to write code that would process logs, but you don't want to write any other code, then you can create full, superwatcher with all 4 components, but with EmitterPoller and EmitterClient hidden.

The modularity should make superwatcher useful for small or large, simple or complex programs.

Below is a simple diagram that describes how these components work together.

                                           Full superwatcher (via components.NewDefault) diagram


                                                                                                            Engine
                                                                              ┌───────────────────────────────┬────────────────────────────────┐
                                                                              │   superwatcher.EmitterClient  │   superwatcher.ServiceEngine   │
                               Emitter                                        │                               │                                │
┌────────────────────────────────────────────────────────────────────┐        │         ┌──── error ──────────┼───────────► HandleEmitterError │
│                                      superwatcher.Emitter          │        │         │                     │                                │
│                                       (*emitter.emitter) ──────────┼────────┼─────────┼──── PollerResult    │                                │
│                                          │    ▲                    │        │         │               │     │                                │
│                                          │    │                    │        │         └──── sync ─────┤     │      ┌────► HandleReorgedLogs  │
│                                fromBlock │    │                    │        │                         │     │      │                         │
│                                  toBlock │    │ PollerResult       │        ├─────────────────────────┼─────┤      │                         │
│                                          │    │                    │        │   superwatcher.Engine   │     │      ├────► HandleGoodLogs     │
│                                          ▼    │                    │        │                         │     │      │                         │
├─────────────────────────────────────  superwatcher.Poller  ────────┤        │                         │     │      │                         │
│                                        (*poller.poller)            │        │                         ▼     │      │                         │
│                                                                    │        │      engine.handleResults  ───┼──────┘                         │
│  blockTracker    ──────────────────────►  Poller.Poll              │        │               ▲               │                                │
│                   previous blockHashes                             │        │               │ metadata      │                                │
│                                               ▲                    │        │               │               │                                │
│                                               │ New []types.Log    │        │                               │                                │
│                                               │                    │        │    engine.blockMetadataTracker│                                │
└───────────────────────────────────────────────┼────────────────────┘        │                               │                                │
                                                │                             └───────────────────────────────┴────────────────────────────────┘
                                                │
                                                │
                                                │
                                                │
                                                │
                                                │

                                         blockchain node

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewDefault

func NewDefault(
	conf *superwatcher.Config,
	ethClient superwatcher.EthClient,
	getStateDataGateway superwatcher.GetStateDataGateway,
	setStateDataGateway superwatcher.SetStateDataGateway,
	serviceEngine superwatcher.ServiceEngine,
	addresses []common.Address,
	topics [][]common.Hash,
) (
	superwatcher.Emitter,
	superwatcher.Engine,
)

NewDefault returns default implementations of Emitter and Engine. The EmitterClient is initialized and embedded to the returned engine within this function. This is the preferred way for initializing superwatcher components. If you don't need to interact with these components, you can use `NewSuperWatcherDefault` instead.

func NewEmitter

func NewEmitter(
	conf *superwatcher.Config,
	client superwatcher.EthClient,
	stateDataGateway superwatcher.GetStateDataGateway,
	poller superwatcher.EmitterPoller,
	syncChan <-chan struct{},
	pollResultChan chan<- *superwatcher.PollerResult,
	errChan chan<- error,
) superwatcher.Emitter

func NewEmitterClient

func NewEmitterClient(
	conf *superwatcher.Config,
	syncChan chan<- struct{},
	pollResultChan <-chan *superwatcher.PollerResult,
	errChan <-chan error,
) superwatcher.EmitterClient

func NewEmitterClientOptions

func NewEmitterClientOptions(options ...Option) superwatcher.EmitterClient

func NewEmitterOptions

func NewEmitterOptions(options ...Option) superwatcher.Emitter

func NewEmitterWithPoller

func NewEmitterWithPoller(
	conf *superwatcher.Config,
	client superwatcher.EthClient,
	stateDataGateway superwatcher.GetStateDataGateway,
	addresses []common.Address,
	topics [][]common.Hash,
	syncChan <-chan struct{},
	pollResultChan chan<- *superwatcher.PollerResult,
	errChan chan<- error,
) superwatcher.Emitter

NewWithPoller returns a new, default Emitter, with a default WatcherPoller. It is the preferred way to init a Emitter if you have not implement WatcherPoller yet yourself.

func NewEngine

func NewEngine(
	emitterClient superwatcher.EmitterClient,
	serviceEngine superwatcher.ServiceEngine,
	stateDataGateway superwatcher.SetStateDataGateway,
	logLevel uint8,
) superwatcher.Engine

func NewEngineOptions

func NewEngineOptions(options ...Option) superwatcher.Engine

func NewEngineWithEmitterClient

func NewEngineWithEmitterClient(
	conf *superwatcher.Config,
	serviceEngine superwatcher.ServiceEngine,
	stateDataGateway superwatcher.SetStateDataGateway,
	syncChan chan<- struct{},
	pollResultChan <-chan *superwatcher.PollerResult,
	errChan <-chan error,
) superwatcher.Engine

NewEngineWithEmitterClient creates a new superwatcher.Engine, and pair it with an superwatcher.EmitterClient. This is the preferred way of creating a new superwatcher.Engine

func NewPoller

func NewPoller(
	addresses []common.Address,
	topics [][]common.Hash,
	doReorg bool,
	doHeader bool,
	filterRange uint64,
	client superwatcher.EthClient,
	logLevel uint8,
	policy superwatcher.Policy,
) superwatcher.EmitterPoller

func NewPollerOptions

func NewPollerOptions(options ...Option) superwatcher.EmitterPoller

func NewSuperWatcher

func NewSuperWatcher(
	emitter superwatcher.Emitter,
	engine superwatcher.Engine,
	logLevel uint8,
) superwatcher.SuperWatcher

func NewSuperWatcherDefault

func NewSuperWatcherDefault(
	conf *superwatcher.Config,
	ethClient superwatcher.EthClient,
	getStateDataGateway superwatcher.GetStateDataGateway,
	setStateDataGateway superwatcher.SetStateDataGateway,
	serviceEngine superwatcher.ServiceEngine,
	addresses []common.Address,
	topics [][]common.Hash,
) superwatcher.SuperWatcher

func NewSuperWatcherOptions

func NewSuperWatcherOptions(options ...Option) superwatcher.SuperWatcher

func NewThinEngine

func NewThinEngine(
	emitterClient superwatcher.EmitterClient,
	serviceEngine superwatcher.ThinServiceEngine,
	stateDataGateway superwatcher.SetStateDataGateway,
	logLevel uint8,
) superwatcher.Engine

NewThinEngine returns thinEngine implementation of superwatcher.Engine

func NewThinEngineWithEmitter

func NewThinEngineWithEmitter(
	conf *superwatcher.Config,
	getStateDataGateway superwatcher.GetStateDataGateway,
	setStateDataGateway superwatcher.SetStateDataGateway,
	addresses []common.Address,
	topics [][]common.Hash,
	client superwatcher.EthClient,
	policy superwatcher.Policy,
	serviceEngine superwatcher.ThinServiceEngine,
) (superwatcher.Emitter, superwatcher.Engine)

NewThinEngine creates an EmitterPoller and and an EmitterClient, before using the created objects to create new thinEngine implementation of superwatcher.Engine. The channels shared by emitterClient and thinEngine are created within the functions and kept private by the implementation.

Types

type Option

type Option func(*componentConfig)

func WithAddresses

func WithAddresses(addresses ...common.Address) Option

func WithConfig

func WithConfig(conf *superwatcher.Config) Option

func WithDoHeader

func WithDoHeader(doHeader bool) Option

func WithDoReorg

func WithDoReorg(doReorg bool) Option

func WithErrChan

func WithErrChan(errChan chan error) Option

func WithEthClient

func WithEthClient(client superwatcher.EthClient) Option

func WithFilterRange

func WithFilterRange(filterRange uint64) Option

func WithFilterResultChan

func WithFilterResultChan(resultChan chan *superwatcher.PollerResult) Option

func WithGetStateDataGateway

func WithGetStateDataGateway(gateway superwatcher.GetStateDataGateway) Option

func WithLogLevel

func WithLogLevel(level uint8) Option

func WithPolicy

func WithPolicy(level superwatcher.Policy) Option

func WithServiceEngine

func WithServiceEngine(service superwatcher.ServiceEngine) Option

func WithSetStateDataGateway

func WithSetStateDataGateway(gateway superwatcher.SetStateDataGateway) Option

func WithSyncChan

func WithSyncChan(syncChan chan struct{}) Option

func WithTopics

func WithTopics(topics ...[]common.Hash) Option

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL