microfleet/core

View on GitHub
packages/plugin-redis-sentinel/src/redis-sentinel.ts

Summary

Maintainability
A
3 hrs
Test Coverage
import type * as _ from '@microfleet/plugin-validator'
import { strict as assert } from 'node:assert'
import Bluebird from 'bluebird'
import _debug from 'debug'
import type { PluginInterface } from '@microfleet/core-types'
import type { Microfleet } from '@microfleet/core'
import { PluginTypes } from '@microfleet/utils'
import { NotFoundError } from 'common-errors'
import { resolve } from 'path'
import {
  REDIS_TYPE_SENTINEL,
  performMigration,
  hasConnection,
  isStarted,
  loadLuaScripts,
} from '@microfleet/plugin-redis-core'
import Redis from 'ioredis'

const debug = _debug('mservice:redisSentinel')

declare module '@microfleet/core-types' {
  interface Microfleet {
    redis: Redis.Redis;
    redisType: 'redisSentinel';
  }

  interface ConfigurationOptional {
    redis: Config
  }
}

export interface Config {
  name: string
  sentinels: Redis.RedisOptions['sentinels']
  options: Omit<Redis.RedisOptions, 'sentinels'>
  luaScripts: string | string[]
}

/**
 * Plugin name.
 */
export const name = 'redis'

/**
 * Plugin type.
 */
export const type = PluginTypes.database

/**
 * Relative priority inside the same plugin group type
 */
export const priority = 10

/**
 * Attaches Redis Sentinel plugin.
 * @param  [conf={}] - Configuration for Redis Sentinel Connection.
 * @returns Connections and Destructors.
 */
export async function attach(this: Microfleet, opts: Partial<Config> = {}): Promise<PluginInterface> {
  assert(this.hasPlugin('validator'), new NotFoundError('validator module must be included'))
  await this.validator.addLocation(resolve(__dirname, '../schemas'))

  // @ts-expect-error - promise not defined, but can be used
  Redis.Promise = Bluebird
  const isRedisStarted = isStarted(this, Redis)
  const conf = this.validator.ifError<Config>('redisSentinel', opts)

  this.redisType = REDIS_TYPE_SENTINEL
  this.redis = new Redis({
    ...conf.options,
    lazyConnect: true,
    name: conf.name,
    sentinels: conf.sentinels,
  })

  return {

    /**
     * @private
     * @returns Opens connection to Redis.
     */
    async connect(this: Microfleet) {
      this.log.debug('redis connecting')
      await this.redis.connect()
      this.log.debug('redis connected')

      // attach to this.redis right away
      if (conf.luaScripts) {
        debug('attaching lua')
        await loadLuaScripts(this, conf.luaScripts, this.redis)
      }

      this.addMigrator('redis', performMigration, this.redis, this)
      this.emit('plugin:connect:redisSentinel', this.redis)

      return this.redis
    },

    /**
     * @returns Returns current status of redis sentinel.
     */
    status: hasConnection.bind(this, isRedisStarted),

    /**
     * @returns Closes redis connection.
     */
    async close(this: Microfleet) {
      try {
        await this.redis.quit()
      } catch (e: any) {
        if (e.message === 'Connection is closed.') {
          return
        }

        throw e
      }

      this.emit('plugin:close:redisSentinel')
    },
  }
}