Skip to content

Conversation

tantaman
Copy link
Contributor

@tantaman tantaman commented Mar 6, 2025

zeroOption is a minimal interface that Replicache will call when:

  • pullEnd
  • persist
  • refresh
  • mutate

happen. Replicache will invoke

  • advance whenever it's main head moves forward.
  • getTxData to fork IVM sources whenever it needs to rebase
  • init on construction to populate the IVM sources

Invoking the above three methods in persist.ts, refresh.ts, replicache-impl.ts::mutate, replicache-impl.ts::maybeEndPull will happen in the next PR.

Copy link

vercel bot commented Mar 6, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
replicache-docs ✅ Ready (Inspect) Visit Preview 💬 Add feedback Mar 6, 2025 3:03pm
zbugs ✅ Ready (Inspect) Visit Preview 💬 Add feedback Mar 6, 2025 3:03pm

Copy link

github-actions bot commented Mar 6, 2025

🐰 Bencher Report

Branchmlaw/rep-tx-data
Testbedlocalhost
Click to view all benchmark results
BenchmarkFile SizeBenchmark Result
kilobytes (KB)
(Result Δ%)
Upper Boundary
kilobytes (KB)
(Limit %)
zero-package.tgz📈 view plot
🚷 view threshold
1,002.65 KB
(+0.07%)Baseline: 1,001.92 KB
1,021.96 KB
(98.11%)
zero.js📈 view plot
🚷 view threshold
180.87 KB
(+0.02%)Baseline: 180.83 KB
184.45 KB
(98.06%)
zero.js.br📈 view plot
🚷 view threshold
50.44 KB
(-0.01%)Baseline: 50.44 KB
51.45 KB
(98.03%)
🐰 View full continuous benchmarking report in Bencher

Copy link

github-actions bot commented Mar 6, 2025

🐰 Bencher Report

Branchmlaw/rep-tx-data
Testbedlocalhost
Click to view all benchmark results
BenchmarkThroughputBenchmark Result
operations / second (ops/s)
(Result Δ%)
Lower Boundary
operations / second (ops/s)
(Limit %)
src/client/zero.bench.ts > basics > All 1000 rows x 10 columns (numbers)📈 view plot
🚷 view threshold
85.90 ops/s
(+12.06%)Baseline: 76.66 ops/s
70.19 ops/s
(81.71%)
src/client/zero.bench.ts > with filter > Lower rows 500 x 10 columns (numbers)📈 view plot
🚷 view threshold
108.80 ops/s
(+12.94%)Baseline: 96.34 ops/s
88.44 ops/s
(81.29%)
🐰 View full continuous benchmarking report in Bencher

export interface ReplicacheOptions<MD extends MutatorDefs> {
export interface ReplicacheOptions<
MD extends MutatorDefs,
TZeroData extends ZeroTxData = ZeroTxData,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better to create a new interface that extends ReplicacheOptions. ReplicacheImpl can use that type. That way we do not have to change the public API of replicache.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can either add one more option here:

constructor(
options: ReplicacheOptions<MD>,
implOptions: ReplicacheImplOptions = {},
) {

or you can extend ReplicacheImplOptions which is all internal stuff the Zero and Reflect use.

export interface ReplicacheOptions<MD extends MutatorDefs> {
export interface ReplicacheOptions<
MD extends MutatorDefs,
TZeroData extends ZeroTxData = ZeroTxData,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can either add one more option here:

constructor(
options: ReplicacheOptions<MD>,
implOptions: ReplicacheImplOptions = {},
) {

or you can extend ReplicacheImplOptions which is all internal stuff the Zero and Reflect use.

*/
export type ZeroOption<T extends ZeroTxData> = {
/**
* Allow Zero to initialize its IVM state from the given hash and store.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

store -> dag store for clarity

* database.
*/
export interface WriteTransaction extends ReadTransaction {
export interface WriteTransaction<TZeroData extends ZeroTxData = ZeroTxData>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have the same for Zero:

export class TransactionImpl implements ClientTransaction<Schema> {
constructor(repTx: WriteTransaction, schema: Schema) {
must(repTx.reason === 'initial' || repTx.reason === 'rebase');
this.clientID = repTx.clientID;
this.mutationID = repTx.mutationID;
this.reason = repTx.reason === 'initial' ? 'optimistic' : 'rebase';
// ~ Note: we will likely need to leverage proxies one day to create
// ~ crud mutators and queries on demand for users with very large schemas.
this.mutate = makeSchemaCRUD(schema, repTx);
this.query = {};
}

export function makeReplicacheMutator(
mutator: CustomMutatorImpl<Schema>,
schema: Schema,
) {
return (repTx: WriteTransaction, args: ReadonlyJSONValue): Promise<void> => {
const tx = new TransactionImpl(repTx, schema);
return mutator(tx, args);
};
}

but somehow we need to plumb through the correct IVM fork. Only Replicache has the information required to create that fork (base head hash, new head hash) and when to fork (when rebase begins).

}

export interface WriteTransaction extends ReplicacheWriteTransaction {
export interface WriteTransaction<T extends ZeroTxData = ZeroTxData>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought we did this... I don't yet understand why we have to change the Replicache interface.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When Replicache starts a rebase it'll ask Zero (via zeroOption) to create a fork of the IVM sources at the given head. This fork will be added to WriteTransaction so Zero can use it in it's mutators.

Previously I assumed there were a finite (or at least small) set of heads and that Zero could look them up by name rather than having to pass the fork along in the Replicache transaction. It turns out there isn't a small set of heads being rebased onto. Looking up by name also opens us up to concurrency issues.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me put up the PR that actually uses this argument.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I see the big picture better now. I still wish we could keep the Replicache public interfaces as they are.

I have a few tricks up my sleeves that would allow this to work without changing the interface. I think it is OK to add public properties (using symbols) to the implementation without changing the interface for example. Other options include using weakmaps as sidetables.

tantaman added 4 commits March 6, 2025 09:26
…cache

zeroOption is a minimal interface that Replicache will call when:

- pullEnd
- persist
- refresh
- mutate

happen.

- replicache will invoke `advance` whenever it's main head moves forward.
- replicache will invoke `getTxData` to fork IVM sources whenever it needs to rebase
- replicache will invoke `init` on construction to populate the IVM sources
When `zero` is running atop `replicache`, `zeroData` will be available.

`zeroData` is the `IVMSourceBranch` that mutations should be using in the
current transaction.

It is templated here `TZeroData = unknown` so Replicache does not need to know
about `IVMSourceBranch`.
@tantaman
Copy link
Contributor Author

tantaman commented Mar 6, 2025

@arv - I removed all changes to the Replicache public interface.

@tantaman
Copy link
Contributor Author

tantaman commented Mar 6, 2025

@arv - I removed all changes to the Replicache public interface.

well not quite. readonly zeroData: ZeroTxData | undefined; is still on interface WriteTransaction

Let me try swapping that to a symbol.

Copy link
Contributor

@arv arv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Thanks

@tantaman tantaman enabled auto-merge (rebase) March 6, 2025 18:42
@tantaman tantaman merged commit ab3459d into main Mar 6, 2025
11 checks passed
@tantaman tantaman deleted the mlaw/rep-tx-data branch March 6, 2025 18:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants