monitor

package module
v0.0.0-...-f0a0ce0 Latest Latest
Warning

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

Go to latest
Published: Jun 4, 2020 License: MIT Imports: 4 Imported by: 1

README

Factom Monitor

A library to listen to height events coming from a factom node, either minute events or block events.

Motivation

I found myself writing similar code over and over again: a polling loop that reacts when the height or minute event in factomd moves. This library aims to be a lightweight implementation of that polling cycle so it can be imported in other projects.

Polling

The polling algorithm looks at the time between events and will try to minimize requests by looking at the time between the last two successful minute events. If the interval was close to the expected time (59-61 seconds), the library will not poll for a full minute. It will poll at the specified interval otherwise. Unfortunately, since the API uses the internal time of only one node, it's not easy to tell if a 75 second minute means the node lagged behind 15 seconds or the network's minute ran late.

If you need a high precision or non-polling solution, use the Factomd Live Feed API.

Usage

Event Object


The Event object contains two heights: Height and DBHeight. Height is the most recently completed block in the network, whereas DBHeight is the height of the factom node's database. Most of the time, they are the same. During minute 0, DBHeight will be Height - 1. At the end of minute 0, the factom node verifies all the signatures on the network and save the completely block in the database.

The minutes are not for Height but rather the block that is currently being worked on. An Event response with a Height of N and minute M is currently working on minute M of block N+1.

Importing the Monitor
import (
	monitor "github.com/WhoSoup/factom-monitor"
)
Start and Stop the Monitor
	monitor, err := monitor.NewMonitor("... url of factomd api endpoint ...")
	if err != nil {
		// ... handle error
    }
    
    monitor.Stop()
Listen to Minutes

The listener receives an Event object.

    listener := monitor.NewMinuteListener()
    for event := range listener {
        // process minute event
    }

More than one minute listener can be created. Each goroutine should have its own listener, two goroutines cannot read from the same listener.

Listen to Heights

This listener returns an int64 representing the height of the most recently completed block in the network.

    listener := monitor.NewHeightListener()
    for height := range listener {
        // process height
    }

Alternatively, you can listen to just DB Heights

    listener := monitor.NewDBHeightListener()
    for height := range listener {
        // process db height
    }

More than one height listener can be created. Each goroutine should have its own listener, two goroutines cannot read from the same listener.

Example

package main

import (
	"fmt"
	"log"

	monitor "github.com/WhoSoup/factom-monitor"
)

func main() {
	// create a new monitor using the open node
	mon, err := monitor.NewMonitor("https://api.factomd.net/v2")
	if err != nil {
		log.Fatalf("unable to start monitor: %v", err)
	}

	height, dbheight, minute := mon.GetCurrentMinute()
	fmt.Printf("Monitor initialized with Height=%d, DBHeight=%d, Minute=%d\n", height, dbheight, minute)

	// prints out all errors
	go func() {
		for err := range mon.NewErrorListener() {
			log.Printf("An error occurred: %v", err)
		}
	}()

	// only listen to new db heights
	go func() {
		height := int64(0)
		for h := range mon.NewDBHeightListener() {
			if h > height {
				fmt.Printf("Height %d is saved in the database\n", h)
				height = h
			}
		}
	}()

	// listen to new heights
	go func() {
		height := int64(0)
		for h := range mon.NewHeightListener() {
			if h > height {
				fmt.Printf("The network finished block %d and is now working on block %d\n", h, h+1)
				height = h
			}
		}
	}()

	// listen to all minute events
	for event := range mon.NewMinuteListener() {
		fmt.Printf("The network is working on Block %d Minute %d\n", event.Height+1, event.Minute)
	}
}

Documentation

Index

Constants

This section is empty.

Variables

Interval specifies the minimum time spent between API requests

View Source
var Timeout time.Duration = time.Second * 5

Timeout specifies the maximum time an API request can take

Functions

This section is empty.

Types

type Event

type Event struct {
	// The most recent block saved in the node's database
	DBHeight int64
	// The most recently completed block in the network
	Height int64
	// The minute the network is currently working on
	Minute int64
}

Event contains the data sent to minute listeners.

type MinuteResponse

type MinuteResponse struct {
	DBHeight      int64 `json:"directoryblockheight"`
	LeaderHeight  int64 `json:"leaderheight"`
	Minute        int64 `json:"minute"`
	DBlockSeconds int64 `json:"directoryblockinseconds"`
}

MinuteResponse is a struct formed after the response from the factomd API. Only contains relevant information. See: https://github.com/FactomProject/factomd/blob/0ff77090ab055d4c069612ca1a6814bde88155ab/wsapi/wsapiStructs.go#L68-L79

type Monitor

type Monitor struct {
	// contains filtered or unexported fields
}

Monitor is responsible for polling the factom node and managing listeners

func NewMonitor

func NewMonitor(url string) (*Monitor, error)

NewMonitor creates a new monitor that begins polling the provided url immediately. If the initial request does not work, an error is returned. Starts a goroutine that can be stopped via monitor.Stop().

func (*Monitor) FactomdRequest

func (m *Monitor) FactomdRequest(ctx context.Context) (*MinuteResponse, error)

FactomdRequest sends a "current-minute" API request to the configured node.

func (*Monitor) GetCurrentMinute

func (m *Monitor) GetCurrentMinute() (int64, int64, int64)

GetCurrentMinute returns the most recent Height and Minute the monitor has received

func (*Monitor) NewDBHeightListener

func (m *Monitor) NewDBHeightListener() <-chan int64

NewDBHeightListener spawns a new listener that receives events every time a new DBHeight is attained. Each reader must have its own listener.

func (*Monitor) NewErrorListener

func (m *Monitor) NewErrorListener() <-chan error

NewErrorListener spawns a new listener that receives error events from malfunctioning API requests. Single errors are usually recoverable and the monitor will continue to poll. A high frequency of errors means the monitor is unable to reach the node. Each reader must have its own listener.

func (*Monitor) NewHeightListener

func (m *Monitor) NewHeightListener() <-chan int64

NewHeightListener spawns a new listener that receives events every time a new height is attained. Each reader must have its own listener.

func (*Monitor) NewMinuteListener

func (m *Monitor) NewMinuteListener() <-chan Event

NewMinuteListener spawns a new listener that receives events for every minute. Each reader must have its own listener.

func (*Monitor) Stop

func (m *Monitor) Stop()

Stop will shut down the monitor and halt all polling. A monitor that has been stopped cannot be started again.

Jump to

Keyboard shortcuts

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