You are reading content from Scuttlebutt
@mix %CbtPAt85S1THR19smmB/wcCvQni/CRKuOypAn1EI/M8=.sha256

Light cache invalidation

Here's my latest iteration on how to do light live-updating. (Avoiding the problem of monolithic observeable cache).

The strategy :

  1. if you want something to live update, subscribe to a minimal stream which will "ping" you if there's an update
  2. trigger and async get + re-render

This means you can have a very light cache somewhere which has keys of things you're subscribing to, and values which are emitters you can bump

Example

This is from my gatherings refactor.

  • about/pull/updates.js (the central cache) :

    const cache = {}
    var listening = false
    
    function updates (key) {
      if (!(isMsg(key) || isFeed(key))) throw new Error(`about.pull.updates expects a valid message/ feed key, got ${key}`)
      startListening()
    
      cache[key] = cache[key] || Notify()
      return cache[key].listen()
    }
    
    function startListening () {
      if (listening) return
    
      const opts = {
        live: true,
        old: false,
        query: [{
          $filter: {
            value: {
              timestamp: { $gt: 0 },
              content: {
                type: 'about',
                about: { $truthy: true }
              }
            }
          }
        }, {
          $map: {
            about: ['value', 'content', 'about']
          }
        }]
      }
      pull(
        api.sbot.pull.stream(server => server.query.read(opts)),
        pull.filter(m => !m.sync),
        pull.drain(
          ({ about }) => {
            if (!cache[about]) return
    
            cache[about](1) // emit a minimal update!
          },
          (err) => console.error(err)
        )
      )
    
      listening = true
    }
    

in my view:

const state = Value()
fetchState()

const updateStream = api.about.pull.stream(gathering.key)  // this is patchore style code, I'm just calling that update fn
pull(updateStream, pull.drain(m => fetchState()))

return h('GatheringShow', computed(state, state => {
  return [
    h('h1', state.title),
    h('div.description', state.description)
     // etc
   ]
})

function fetchState () {
  scuttle.get(gaterhing.key, (err, data) => {      // scuttle = require('scuttle-gathering')(sbotConnection)
    if (err) return console.error('AHHHH')

    state.set(data)
  })
}

cc @matt (it works!) @regular @christianbundy

@mix %1EZYJBlhGULGH2VC1xira+BCQjURYGUhfcB6PiA5u6Y=.sha256

p.s. Notify is pull-notify

@Christian Bundy %8DDAHCoqi1B5RUpZGTHxwgouE+AGAiYmdGxNIy1noFI=.sha256

I love it, cache invalidations is an hard problem (please don't make me say it), but I think that's a solid way to go about it. It reminds me of bumping the index in a flume view: it's a minimal flag to communicate "hey it's time to recalculate things" without having to pass any new data with the message. I love it. :heart: Thanks for mentioning me, this was fun to read through.

@Dominic %wuBjuDg22jpNIGfzBt410mo2zb/0FJokJ/m7r4qYsrk=.sha256

you could take this even further, drop the computed observable and just use querySelector to find the gathering in the DOM, and the update it with morphdom. (you'd just need to put the gathering's id in on the main element of the gathering html)

Join Scuttlebutt now