orbitjs / orbit

Composable data framework for ambitious web applications.
https://orbitjs.com
MIT License
2.33k stars 133 forks source link

Introduce new fork / merge possibilities in MemorySource and MemoryCache #873

Closed dgeb closed 3 years ago

dgeb commented 3 years ago

The primary use case of MemorySource forking is to create an isolated source and cache, which can be modified and then merged back into its base, with all the associated operations coalesced.

Because the forked MemorySource is "just" another Source, updates to it are still async, which allows for those updates to be logged, interacted with through other sources, etc. through async processes. Until now, there has been no way to synchronously make changes to a fork and then merge those back to the base source, because only updates at the source level have been tracked.

Optional update tracking in MemoryCache

This PR introduces a new optional form of update tracking in the cache associated with a source. By default now, this capability will be enabled in forked caches. Any updates applied to the cache will be tracked and used to form the merge transform when merge is invoked. This means that synchronous changes could be made to a forked cache directly rather than going through the associated source. Changes could also still be applied via updates to the forked source, since those will internally also invoke update on the cache and be tracked.

// fork a base source
let fork = source.fork();

// add jupiter synchronously to the forked source's cache
fork.cache.update((t) => t.addRecord(jupiter));

// merge changes from the fork back to its base
await source.merge(fork);

// jupiter should now be in the base source
source.cache.getRecordSync({ type: 'planet', id: 'jupiter' }); // returns jupiter

If you want to continue to track changes only at the source-level and have merge work only with those changes, pass the following config when you fork a source:

let fork = source.fork({ cacheSettings: { trackUpdateOperations: false } });

This will prevent update tracking at the cache level and will signal to merge that only transforms applied at the source-level should be merged.

Other new MemoryCache capabilities

Along with change tracking at the cache-level, this PR also introduces the following methods to MemoryCache:

MemoryCache forking / merging / rebasing is a lighter-weight way of "branching" changes, that can ultimately be merged back into a source. Cache-level forking can be paired with source-level forking for a lot of flexibility and power.