Restart mechanics in GunSmith for once()s

This commit is contained in:
Daniel Lugo 2021-09-15 12:24:59 -04:00
parent 549f1ba4c4
commit 051d5aa5df
3 changed files with 63 additions and 15 deletions

View file

@ -2274,6 +2274,7 @@ module.exports = async (
* @prop {string=} publicKey * @prop {string=} publicKey
* @prop {string=} publicKeyForDecryption * @prop {string=} publicKeyForDecryption
* @prop {string=} epubForDecryption * @prop {string=} epubForDecryption
* @prop {boolean=} mustBePopulated
*/ */
/** /**
* @param {HandleGunFetchParams} args0 * @param {HandleGunFetchParams} args0
@ -2285,7 +2286,8 @@ module.exports = async (
path, path,
publicKey, publicKey,
publicKeyForDecryption, publicKeyForDecryption,
epubForDecryption epubForDecryption,
mustBePopulated
}) => { }) => {
const keys = path.split('>') const keys = path.split('>')
const { gun, user } = require('../services/gunDB/Mediator') const { gun, user } = require('../services/gunDB/Mediator')
@ -2315,7 +2317,7 @@ module.exports = async (
} }
} }
if (type === 'once') node.once(listener) if (type === 'once') node.once(listener, { mustBePopulated })
if (type === 'load') node.load(listener) if (type === 'load') node.load(listener)
}) })
} }
@ -2340,7 +2342,8 @@ module.exports = async (
startFromUserGraph: false, startFromUserGraph: false,
type: 'once', type: 'once',
publicKeyForDecryption, publicKeyForDecryption,
epubForDecryption epubForDecryption,
mustBePopulated: !!req.header('must-be-populated')
}) })
res.status(200).json({ res.status(200).json({
data data
@ -2388,7 +2391,8 @@ module.exports = async (
startFromUserGraph: true, startFromUserGraph: true,
type: 'once', type: 'once',
publicKeyForDecryption, publicKeyForDecryption,
epubForDecryption epubForDecryption,
mustBePopulated: !!req.header('must-be-populated')
}) })
res.status(200).json({ res.status(200).json({
data data

View file

@ -13,7 +13,7 @@ const { fork } = require('child_process')
const logger = require('../../config/log') const logger = require('../../config/log')
const { mergePuts } = require('./misc') const { mergePuts, isPopulated } = require('./misc')
const gunUUID = () => { const gunUUID = () => {
const RG = /** @type {any} */ (RealGun) const RG = /** @type {any} */ (RealGun)
@ -366,14 +366,29 @@ function createReplica(path, afterMap = false) {
mapListeners.delete(l) mapListeners.delete(l)
} }
}, },
on(cb) { on(cb, { mustBePopulated } = {}) {
listenersForThisRef.push(cb) listenersForThisRef.push(cb)
let canaryPeep = false
const canary = () => {
canaryPeep = true
}
listenersForThisRef.push(canary)
const checkCanary = () =>
setTimeout(() => {
if (!canaryPeep && mustBePopulated) {
forge()
checkCanary()
}
}, 5000)
if (afterMap) { if (afterMap) {
// eslint-disable-next-line no-multi-assign // eslint-disable-next-line no-multi-assign
const listeners = const listeners =
pathToMapListeners[path] || (pathToMapListeners[path] = new Set()) pathToMapListeners[path] || (pathToMapListeners[path] = new Set())
listeners.add(cb) listeners.add(cb)
listeners.add(canary)
/** @type {Smith.SmithMsgMapOn} */ /** @type {Smith.SmithMsgMapOn} */
const msg = { const msg = {
@ -387,6 +402,7 @@ function createReplica(path, afterMap = false) {
pathToListeners[path] || (pathToListeners[path] = new Set()) pathToListeners[path] || (pathToListeners[path] = new Set())
listeners.add(cb) listeners.add(cb)
listeners.add(canary)
/** @type {Smith.SmithMsgOn} */ /** @type {Smith.SmithMsgOn} */
const msg = { const msg = {
@ -398,7 +414,8 @@ function createReplica(path, afterMap = false) {
return this return this
}, },
once(cb, opts = { wait: 500 }) { once(cb, _opts) {
const opts = { ...{ mustBePopulated: false, wait: 500 }, ..._opts }
// We could use this.on() but then we couldn't call .off() // We could use this.on() but then we couldn't call .off()
const tmp = createReplica(path, afterMap) const tmp = createReplica(path, afterMap)
if (afterMap) { if (afterMap) {
@ -412,15 +429,21 @@ function createReplica(path, afterMap = false) {
}) })
setTimeout(() => { setTimeout(() => {
if (cb) {
cb(lastVal, path.split('>')[path.split('>').length - 1])
}
tmp.off() tmp.off()
if (cb) {
if (opts.mustBePopulated && !isPopulated(lastVal)) {
forge()
this.once(cb, { ...opts, wait: 5000, mustBePopulated: false })
} else {
cb(lastVal, path.split('>')[path.split('>').length - 1])
}
}
}, opts.wait) }, opts.wait)
return this return this
}, },
put(data, cb) { put(data, cb) {
logger.info('put()')
const id = uuid() const id = uuid()
const pendingPutsForPath = pendingPuts[path] || (pendingPuts[path] = []) const pendingPutsForPath = pendingPuts[path] || (pendingPuts[path] = [])
@ -502,11 +525,11 @@ function createReplica(path, afterMap = false) {
} }
} }
}, },
then() { then(opts) {
return new Promise(res => { return new Promise(res => {
this.once(data => { this.once(data => {
res(data) res(data)
}) }, opts)
}) })
} }
} }

View file

@ -58,6 +58,14 @@ namespace GunT {
export type LoadListener = (data: LoadListenerData, key: string) => void export type LoadListener = (data: LoadListenerData, key: string) => void
export interface GunSmithFetchOpts {
/**
* GunSmith exclusive. If set to true, gun will be restarted to force
* replication of this data.
*/
mustBePopulated?: boolean
}
export interface GUNNode { export interface GUNNode {
_: Soul _: Soul
/** /**
@ -81,13 +89,26 @@ namespace GunT {
load(this: GUNNode, cb?: LoadListener): GUNNode load(this: GUNNode, cb?: LoadListener): GUNNode
map(): GUNNode map(): GUNNode
off(): void off(): void
on(this: GUNNode, cb: Listener): void on(
once(this: GUNNode, cb?: Listener, opts?: { wait: number }): GUNNode this: GUNNode,
cb: Listener,
opts?: {
change?: boolean
} & GunSmithFetchOpts
): void
once(
this: GUNNode,
cb?: Listener,
opts?: { wait?: number } & GunSmithFetchOpts
): GUNNode
user(): UserGUNNode user(): UserGUNNode
user(pub: string): GUNNode user(pub: string): GUNNode
put(data: ValidDataValue, cb?: Callback): GUNNode put(data: ValidDataValue, cb?: Callback): GUNNode
set(data: ValidDataValue, cb?: Callback): GUNNode set(data: ValidDataValue, cb?: Callback): GUNNode
then(): Promise<ListenerData> /**
* @param options Gunsmith only.
*/
then(opts?: GunSmithFetchOpts): Promise<ListenerData>
} }
export interface CreateAck { export interface CreateAck {