OverviewFilesCommitsIssuesDocsReleasesPeers

keet-cli / keet-core-4.14.4-pretty / lib / core / swarm / worker.js

235 lines · javascript ref:
const s = require("ready-resource"),
  e = require("hypercore-id-encoding"),
  t = require("scope-lock"),
  {
    local: {
      hyperschema: { getEnum: i },
    },
  } = require("../../../schema/spec"),
  r = i("@local/keet-state"),
  a = 8,
  o = 1e3,
  n = 3e4,
  c = 6e4,
  l = 1e4,
  h = 5e3,
  m = 5e3
async function d(s) {
  ;(await s.swarm.flushed(),
    await s.swarm.destroy(),
    (s.swarmStatus.isClient = !1),
    (s.swarmStatus.isServer = !1),
    (s.swarm = null))
}
module.exports = class extends s {
  constructor(s, e = {}) {
    super()
    const {
      gcDefaultDiscoveryTimeout: i = c,
      gcUpdatedDiscoveryTimeout: r = l,
      gcStartDiscoverySwarmingDelay: d = h,
      gcStartFallbackSwarmingDelay: u = m,
      refreshInterval: w = o,
      fallbackSwarmingLimit: S = a,
      gcIdleTime: g = n,
    } = e
    ;((this.keet = s),
      (this.swarm = null),
      (this.sessions = new Map()),
      (this.sessionsByRoomId = new Map()),
      (this._refreshLock = new t({ debounce: !0 })),
      (this._refreshTimer = null),
      (this._gcEnabled = !1 !== e.gc),
      (this._deferSwarming = !1 !== e.deferSwarming),
      (this._fallbackSwarmingEnabled = !1 !== e.fallbackSwarming),
      (this._fallbackSwarmingLimit = S),
      (this._gcIdleTime = g),
      (this._refreshInterval = w),
      (this._gcDefaultDiscoveryTimeout = i),
      (this._gcUpdatedDiscoveryTimeout = r),
      (this._gcStartDiscoverySwarmingDelay = d),
      (this._gcStartFallbackSwarmingDelay = u),
      (this._serverModeAllowed = !0),
      (this._backgrounded = !1))
  }
  _runOpen() {
    ;(this._refreshTimer ||
      (this._refreshTimer = setInterval(this.refresh.bind(this), this._refreshInterval)),
      this.refresh().catch(this.keet.onerror))
  }
  async _runClose() {
    this._refreshTimer && (clearInterval(this._refreshTimer), (this._refreshTimer = null))
    const s = []
    for (const e of this.sessions.values()) e.swarm && s.push(e.swarm.destroy())
    ;(this.sessions.clear(), await Promise.allSettled(s))
  }
  _open() {
    ;((this.swarm = this.keet.swarm), this._runOpen())
  }
  _close() {
    return this._runClose()
  }
  async _refreshFallbackSwarming() {
    if (!this._fallbackSwarmingEnabled) return
    const s = await this.keet.local
        .find("@local/rooms-by-timestamp", { limit: this._fallbackSwarmingLimit, reverse: !0 })
        .toArray(),
      t = new Set()
    for (const i of s) {
      const s = e.encode(i.capabilities.swarmTopic),
        r = this._createSession(s, i.capabilities.swarmTopic)
      if (!r.status.isFallback) {
        r.status.isFallback = !0
        const s = Math.floor(0.5 * Math.random() * this._gcStartFallbackSwarmingDelay),
          e = this._gcStartFallbackSwarmingDelay + s
        r.fallbackSwarmingStartTime = Date.now() + e
      }
      t.add(s)
    }
    for (const s of this.sessions.values()) t.has(s.id) || (s.status.isFallback = !1)
  }
  async _refreshSessions() {
    const s = [],
      e = []
    for (const t of this.sessions.values())
      this._checkInactivity(t) ? s.push(t) : e.push(this._refreshSession(t))
    await Promise.allSettled(e)
    const t = []
    for (const e of s)
      (this.sessions.delete(e.id),
        e.roomId && this.sessionsByRoomId.delete(e.roomId),
        e.swarm && t.push(e.swarm.destroy()))
    await Promise.allSettled(t)
  }
  _checkInactivity(s) {
    if (!this._gcEnabled) return !1
    let e = !1
    s.status.isDiscovering && (e = Date.now() - s.discoveryStartTime > s.discoveryTimeout)
    const t = s.status.isFallback || s.status.isActive
    let i = s.status.isDiscovering && !e
    return (
      s.status.isDiscovering &&
        e &&
        (this.keet.internalDiscoveryCompleted(s.discoveryKey, s.roomId),
        (s.status.isDiscovering = !1),
        (i = !1)),
      !(
        t ||
        i ||
        (0 === s.idleStartTime && (s.idleStartTime = Date.now()),
        (s.idleDuration = Date.now() - s.idleStartTime),
        s.idleDuration < this._gcIdleTime)
      )
    )
  }
  async _refreshSession(s) {
    let e = !1,
      t = !1
    ;(s.status.isFallback &&
      (t = Date.now() >= s.fallbackSwarmingStartTime || s.swarmStatus.isServer),
      s.status.isDiscovering &&
        (e = Date.now() >= s.discoverySwarmingStartTime || s.swarmStatus.isClient),
      s.status.isActive && ((e = !0), (t = !0)))
    const i = e === s.swarmStatus.isClient,
      a = t === s.swarmStatus.isServer
    if (i && a) return
    const o = s.swarm
    ;(this._serverModeAllowed || (t = !1),
      (s.swarmStatus.isClient = e),
      (s.swarmStatus.isServer = t),
      this.keet.debug.disableSwarming ||
        this.keet.state === r.WAKEUP ||
        ((s.swarm = this.swarm.join(s.discoveryKey, { client: e, server: t, limit: s.limit })),
        o && (await o.destroy())))
  }
  async refresh() {
    if (!this.keet.closing && (await this._refreshLock.lock()))
      try {
        ;(await this._refreshFallbackSwarming(), await this._refreshSessions())
      } catch (s) {
        this.keet.onerror(s)
      } finally {
        this._refreshLock.unlock()
      }
  }
  _createSession(s, e, { roomId: t, limit: i } = {}) {
    let r = this.sessions.get(s)
    return r
      ? (t && !this.sessionsByRoomId.has(t) && ((r.roomId = t), this.sessionsByRoomId.set(t, r)), r)
      : ((r = {
          id: s,
          roomId: t,
          discoveryKey: e,
          swarm: null,
          lastActiveTime: 0,
          discoveryStartTime: 0,
          discoveryTimeout: 0,
          discoverySwarmingStartTime: 0,
          fallbackSwarmingStartTime: 0,
          idleStartTime: 0,
          idleDuration: 0,
          status: { isActive: !1, isFallback: !1, isDiscovering: !1 },
          limit: i,
          swarmStatus: { isClient: !1, isServer: !1 },
        }),
        this.sessions.set(s, r),
        t && this.sessionsByRoomId.set(t, r),
        r)
  }
  setServerMode(s) {
    this._serverModeAllowed = s
  }
  startActiveSwarming(s) {
    const t = e.encode(s),
      i = this._createSession(t, s)
    ;((i.status.isActive = !0), (i.lastActiveTime = Date.now()), this.refresh())
  }
  stopActiveSwarming(s) {
    const t = e.encode(s),
      i = this.sessions.get(t)
    i && ((i.status.isActive = !1), this.refresh())
  }
  startDiscoverySwarming(s, t) {
    const i = e.encode(s),
      r = this._createSession(i, s, t)
    if (
      ((r.status.isDiscovering = !0),
      (r.discoveryStartTime = Date.now()),
      (r.discoveryTimeout = this._gcDefaultDiscoveryTimeout),
      !1 !== t.deferSwarming && this._deferSwarming)
    ) {
      const s = Math.floor(0.5 * Math.random() * this._gcStartDiscoverySwarmingDelay),
        e = this._gcStartDiscoverySwarmingDelay + s
      r.discoverySwarmingStartTime = Date.now() + e
    } else r.discoverySwarmingStartTime = r.discoveryStartTime
    this.refresh()
  }
  notifyDiscoveryUpdate(s) {
    const e = this.sessionsByRoomId.get(s)
    e &&
      e.status.isDiscovering &&
      ((e.discoveryStartTime = Date.now()),
      (e.discoveryTimeout = this._gcUpdatedDiscoveryTimeout),
      this.refresh())
  }
  async background() {
    if (this._backgrounded) return
    this._backgrounded = !0
    const s = []
    for (const e of this.sessions.values()) e.swarm && s.push(d(e))
    await Promise.allSettled(s)
  }
  async unbackground() {
    this._backgrounded && ((this._backgrounded = !1), await this._runOpen())
  }
  async suspend() {
    await this._runClose()
  }
  async resume() {
    await this._runOpen()
  }
  async flush() {
    ;(await this.refresh(), await this.swarm.flush())
  }
}