All files / src/util/polling DbSyncEpochPollService.ts

100% Statements 38/38
88.88% Branches 8/9
100% Functions 10/10
100% Lines 31/31

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 9143x       43x   43x     43x 8x 8x 8x     8x     8x             8x 8x 8x 8x               15x 15x 15x   15x                 20x       20x         6x   5x 15x         6x         7x 7x 7x 7x 7x           9x       3x   4x      
import { Cardano } from '@cardano-sdk/core';
import { EpochModel } from '../../StakePool';
import { EpochMonitor } from './types';
import { Pool, QueryResult } from 'pg';
import { findLastEpoch } from './queries';
 
export const EPOCH_POLL_INTERVAL_DEFAULT = 10_000;
 
/** Class to handle epoch rollover through db polling */
export class DbSyncEpochPollService implements EpochMonitor {
  #timeoutId?: ReturnType<typeof setInterval>;
  #callbacks: Function[];
  #currentEpoch: Promise<Cardano.EpochNo | null>;
 
  /** Db connection */
  #db: Pool;
 
  /** Polling interval in ms */
  #interval: number;
 
  /**
   * @param db Db connection
   * @param interval Polling interval in ms
   */
  constructor(db: Pool, interval: number) {
    this.#db = db;
    this.#callbacks = [];
    this.#interval = interval;
    this.#currentEpoch = Promise.resolve(null);
  }
 
  /**
   * Poll execution to detect a new epoch rollover
   * Upon the occurrence of rollover event it executes all callbacks by registered dependant services
   */
  async #executePoll() {
    const lastEpoch = await this.#queryLastEpoch();
    const currentEpoch = await this.#currentEpoch;
    const shouldClearCache = !!(currentEpoch && lastEpoch > currentEpoch);
 
    if (shouldClearCache) this.onEpoch(lastEpoch);
  }
 
  /**
   * Query the last epoch number stored in db
   *
   * @returns {number} epoch number
   */
  async #queryLastEpoch() {
    const result: QueryResult<EpochModel> = await this.#db.query({
      name: 'current_epoch',
      text: findLastEpoch
    });
    return Cardano.EpochNo(result.rowCount ? result.rows[0].no : 0);
  }
 
  /** Starts the poll execution */
  #start() {
    if (this.#timeoutId) return;
 
    this.#currentEpoch = this.#queryLastEpoch();
    this.#timeoutId = setInterval(() => this.#executePoll(), this.#interval);
  }
 
  /** Shutdown the poll execution */
  #shutdown() {
    if (this.#timeoutId) clearInterval(this.#timeoutId);
  }
 
  /** Epoch Rollover event - subscription-based mechanism to manage starting and stopping of epoch poll service */
  onEpochRollover(cb: Function) {
    this.#callbacks.push(cb);
    if (this.#callbacks.length === 1) this.#start();
    return () => {
      this.#callbacks.splice(this.#callbacks.indexOf(cb), 1);
      if (this.#callbacks.length === 0) this.#shutdown();
    };
  }
 
  /** Get last known epoch */
  getLastKnownEpoch() {
    return this.#currentEpoch;
  }
 
  onEpoch(currentEpoch: Cardano.EpochNo) {
    this.#currentEpoch = Promise.resolve(currentEpoch);
 
    for (const cb of this.#callbacks) cb();
  }
}