mozilla / datomish-user-agent-service

Mozilla Public License 2.0
4 stars 6 forks source link

Vocabulary and API for preferences storage #26

Open rnewman opened 7 years ago

rnewman commented 7 years ago

Open questions:

@jsantell, what are you thinking?

jsantell commented 7 years ago

Initial thought is we should make sure that any choice here will work atleast as good or better for Firefox, as that seems like it'd still be the biggest impact for the UAS.

rnewman commented 7 years ago

Capturing some notes from Slack discussion:

The likely best way to build prefs is with an in-memory write-around cache in each window. Each write to the UAS is generic, can succeed or fail, and by default updates the cache afterwards. Callers can optionally optimistically write to the cache immediately if they wish. Reads are fast after init.

The simplest UAS implementation of this is very little more than a generic write API and vocabulary, which is nice: that proves out a concept!

If we ever need non-local optimism — that is, a change is visible to other windows before it has been committed to disk — then we would need to introduce a cache or a different storage backend in the UAS. (This would be very relevant to Marco Bonardo's interests.)

Steps:

  1. Vocabulary, scoping out how we model prefs.
  2. UAS read and write API exposed to Tofino. We'll file a separate issue for that.
  3. Exposing change listeners. (See https://github.com/mozilla/datomish/issues/61)
  4. Tofino prefs UAC.
  5. Tofino prefs cache.
  6. Optional: synchronous prefs reads. (There are some sharp edges here around init.)
  7. Optional: UAS-side prefs cache if needed for write-back or read perf. This is just a special case of the kinds of materialized views we want in Datomish anyway…
rnewman commented 7 years ago

Global seems like the safer option, as we can always model in scoped prefs via that, like branching: window.dimensions.width = '{"windowA": 1200, "windowB": 1000}', which we do in some places in Firefox IIRC.

I think I agree with you in principle, but in practice you're exposed to read-write atomicity races. We would need to expose alter! and swap! operations on prefs if we chose this kind of nested modeling.

Another way to do it is:

window.A.dimensions.width

which is safer…

rnewman commented 7 years ago

A perhaps more Datomishey way of doing this is:

101 :window/id "A"
101 :prefs/prefs 102
102 :prefs/window.dimensions.width 1200

with the query to find A's width:

[:find ?width . in $ :where
  [?window :window/id "A"]
  [?window :prefs/prefs ?prefs]
  [?prefs :prefs/window.dimensions.width ?width]]
jsantell commented 7 years ago

The likely best way to build prefs is with an in-memory write-around cache in each window. Each write to the UAS is generic, can succeed or fail, and by default updates the cache afterwards. Callers can optionally optimistically write to the cache immediately if they wish. Reads are fast after init.

This sounds great to me -- as a follow up/clarity of our discussion, we can flag writes as write-back for more cosmetic preferences, like write(key, value, { optimistic: true }) if we need speed and less of an issue of failure. As each write can succeed or fail, if an 'important operation' occurs, we can catch that write's failure and decide how to handle it on per-write basis (assuming after the client does due diligence and reattempts if possible).

Another way to do it is:

Agreed on your comments on scoping -- if we can have dynamic branches (so a window ID as a substring of the preference name), this sounds great. In terms of window.A.dimensions.width versus Datomishey-way, I have no preference (and a Datomish newb).

If we ever need non-local optimism — that is, a change is visible to other windows before it has been committed to disk — then we would need to introduce a cache or a different storage backend in the UAS. (This would be very relevant to Marco Bonardo's interests.)

This seems significantly more complicated -- hopefully we won't have a use for this anytime soon!

rnewman commented 7 years ago

This seems significantly more complicated -- hopefully we won't have a use for this anytime soon!

Yeah, that's one reason I'm defaulting to just using the Datomish store — it's less complicated, it gets us a general API, and it allows us to do things like blur the lines of what's a "preference" versus "data", have queries match against preferences, and include pref changes in the transaction log.

In terms of window.A.dimensions.width versus Datomishey-way, I have no preference (and a Datomish newb).

One reason to do this with fixed keys (and hierarchically so) is that it allows us to model preferences as data. That is: they would have a schema, the schema would be in the store, you could mark individual preferences as noHistory, you could full-text query preferences… at that point, "preferences" simply means "the data that windows keep in memory for simple lookups", and it's really no different to any other cache — it's just like the set of toolbar bookmarks, for example.

rnewman commented 7 years ago

The downside of using fixed keys is that you need to define prefs before you use them, and you can't use computed keys.

That said, computed keys (and often JSON values) are really just a hack that we use to store structured data in a non-structured key-value store, so I suspect we won't need them.