@ssb-graphql/whakapapa
Version:
GraphQL types and resolvers for the ssb-whakapapa plugin
118 lines (100 loc) • 3.37 kB
JavaScript
const Cache = require('hashlru')
const pull = require('pull-stream')
const paraMap = require('pull-paramap')
module.exports = function linksCache (sbot) {
const cache = {
'link/profile-profile/child': Cache(6000),
'link/profile-profile/partner': Cache(4000)
}
const API = {
/* set */
setParent (type, recordId, links) {
if (!cache[type]) cache[type] = Cache(2000)
cache[type].set(cacheKey({ parent: recordId }), links)
},
setChild (type, recordId, links) {
if (!cache[type]) cache[type] = Cache(2000)
cache[type].set(cacheKey({ child: recordId }), links)
},
/* remove */
removeParent (type, recordId) {
if (!cache[type]) return
cache[type].remove(cacheKey({ parent: recordId }))
},
removeChild (type, recordId) {
if (!cache[type]) return
cache[type].remove(cacheKey({ child: recordId }))
},
has (type, { parent, child }) {
const typeCache = cache[type]
if (!typeCache) return false
if (!parent && !child) throw new Error('must provide parent AND/OR child')
const results = []
if (parent) results.push(typeCache.has(cacheKey({ parent })))
if (child) results.push(typeCache.has(cacheKey({ child })))
return results.every(bool => bool === true)
},
get (type, { parent, child }) {
const typeCache = cache[type]
if (!typeCache) return
if (!parent && !child) throw new Error('must provide parent AND/OR child')
if (!this.has(type, { parent, child })) return
return [
...(parent ? typeCache.get(cacheKey({ parent })) : []),
...(child ? typeCache.get(cacheKey({ child })) : [])
]
}
}
// invalidate type cache if any new message comes in for a particular pair
// this is naive!
sbot.post(m => {
sbot.get({ id: m.key, meta: true, private: true }, (err, m) => {
if (err) return
if (typeof m.value.content === 'string') return
pull(
pull.values([m]),
invalidateSink()
)
})
})
// NOTE - this should work, but for some reason does not :(
// pull(
// sbot.createLogStream({ live: true, old: false, private: true }),
// invalidateSink()
// )
function invalidateSink () {
return pull(
pull.map(m => m.value.content),
pull.filter(content => content.type in cache),
pull.filter(content => typeof content?.tangles?.link === 'object'),
paraMap(
(content, cb) => {
if (content.tangles.link.root === null) return cb(null, content)
sbot.get({ id: content.tangles.link.root, private: true }, (err, rootVal) => {
if (err) cb(null, null) // swallow error
else cb(null, rootVal.content)
})
},
6
),
pull.filter(Boolean),
pull.drain(({ type, parent, child }) => {
API.removeParent(type, parent)
API.removeChild(type, child)
// some of these are directed links, some are not
API.removeParent(type, child)
API.removeChild(type, parent)
},
(err) => {
if (err) console.error('Error in linksCache:', err)
})
)
}
return API
}
function cacheKey ({ parent, child }) {
if (parent && child) throw new Error('can only store EITHER child OR parent links')
return parent
? parent + '_'
: '_' + child
}