Updating SharedTree's public transaction docs#26564
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates the documentation for SharedTree's transaction API, removing the old TODO and adding comprehensive documentation for the new alpha-level transaction APIs. The changes introduce examples and explanations for the TreeBranchAlpha.runTransaction and TreeViewAlpha.runTransaction methods, which provide more control over error handling compared to the legacy Tree.runTransaction API.
Changes:
- Removed the TODO comment about updating the transaction API documentation
- Added detailed documentation for the new alpha transaction APIs including synchronous and asynchronous forms
- Documented transaction nesting behavior and constraints functionality
|
|
||
| The APIs described in this section are currently `@alpha` and may change in future releases. | ||
|
|
||
| Unlike `Tree.runTransaction`, the Alpha `runTransaction` does **not** automatically roll back the transaction if the callback throws an error. If your callback may throw, you should handle errors explicitly and return { rollback: true } when required |
There was a problem hiding this comment.
Missing period at the end of the sentence. The sentence should end with a period for consistency with documentation style.
| Unlike `Tree.runTransaction`, the Alpha `runTransaction` does **not** automatically roll back the transaction if the callback throws an error. If your callback may throw, you should handle errors explicitly and return { rollback: true } when required | |
| Unlike `Tree.runTransaction`, the Alpha `runTransaction` does **not** automatically roll back the transaction if the callback throws an error. If your callback may throw, you should handle errors explicitly and return { rollback: true } when required. |
| ``` | ||
|
|
||
| ### The `transaction` Callback Function | ||
| The callback function passed to runTransaction is the actual implementation of the transaction to apply. It can optionally return a status object indicating success or failure and include a user-defined value. |
There was a problem hiding this comment.
The sentence is grammatically awkward. Consider rephrasing to: "The callback function passed to runTransaction implements the transaction logic. It can optionally return a status object indicating success or failure, and include a user-defined value."
| The callback function passed to runTransaction is the actual implementation of the transaction to apply. It can optionally return a status object indicating success or failure and include a user-defined value. | |
| The callback function passed to runTransaction implements the transaction logic. It can optionally return a status object indicating success or failure, and include a user-defined value. |
|
|
||
| ## Constraints | ||
|
|
||
| Constraints let you opt in to stricter validation when your application needs it. A violated constraint causes the **entire transaction** to be dropped. Constraints can be applied as preconditions to a transaction (or as preconditions on revert, to be applied if the transaction is reverted), thereby ensuring that its effect will only apply if some specific conditions are met. |
There was a problem hiding this comment.
The phrase "its effect will only apply" could be clearer. Consider rephrasing to "the transaction will only be applied" or "the transaction's changes will only be applied" for better clarity.
| Constraints let you opt in to stricter validation when your application needs it. A violated constraint causes the **entire transaction** to be dropped. Constraints can be applied as preconditions to a transaction (or as preconditions on revert, to be applied if the transaction is reverted), thereby ensuring that its effect will only apply if some specific conditions are met. | |
| Constraints let you opt in to stricter validation when your application needs it. A violated constraint causes the **entire transaction** to be dropped. Constraints can be applied as preconditions to a transaction (or as preconditions on revert, to be applied if the transaction is reverted), thereby ensuring that the transaction's changes will only be applied if some specific conditions are met. |
|
|
||
| ::: | ||
|
|
||
| The TreeBranchAlpha API introduces `TreeBranchAlpha.runTransaction` as a replacement for `Tree.runTransaction`. This new API exists to solve correctness issues where errors being thrown in runTransaction could result in an undefined state. This new API enables developers to customize how their application handles this error case. |
There was a problem hiding this comment.
The phrasing "TreeBranchAlpha API introduces" is technically imprecise. TreeBranchAlpha is an interface type, not an API namespace. The APIs are introduced on both the TreeBranchAlpha interface (obtained via TreeAlpha.branch(node)) and the TreeViewAlpha interface. Consider rephrasing to: "The alpha APIs introduce runTransaction methods on TreeBranchAlpha and TreeViewAlpha as a replacement for Tree.runTransaction." This would more accurately describe where the APIs are available.
| The TreeBranchAlpha API introduces `TreeBranchAlpha.runTransaction` as a replacement for `Tree.runTransaction`. This new API exists to solve correctness issues where errors being thrown in runTransaction could result in an undefined state. This new API enables developers to customize how their application handles this error case. | |
| The alpha APIs introduce `runTransaction` methods on `TreeBranchAlpha` and `TreeViewAlpha` as a replacement for `Tree.runTransaction`. This new API exists to solve correctness issues where errors being thrown in `runTransaction` could result in an undefined state. This new API enables developers to customize how their application handles this error case. |
|
|
||
| ### Supported Constraint Types | ||
|
|
||
| // TODO:#59981: Document new constraint types as they are implemented. Consider breaking this section into its own page when we have enough constraint types to warrant it. |
There was a problem hiding this comment.
The TODO comment is visible in the published documentation. Consider moving this TODO to a code comment in the relevant source file, or converting it to a note/admonition if users need to be aware that more constraint types are coming. If this is meant for maintainers only, it should not appear in user-facing documentation.
| // TODO:#59981: Document new constraint types as they are implemented. Consider breaking this section into its own page when we have enough constraint types to warrant it. | |
| :::note | |
| Additional constraint types may be added in future versions of `SharedTree`. This section will be updated as new constraint types become available. | |
| ::: |
There was a problem hiding this comment.
Yeah, we'll want to use mdx comment syntax here to avoid this appearing in the user-facing docs.
| // TODO:#59981: Document new constraint types as they are implemented. Consider breaking this section into its own page when we have enough constraint types to warrant it. | |
| {/* TODO:#59981: Document new constraint types as they are implemented. Consider breaking this section into its own page when we have enough constraint types to warrant it. */} |
|
|
||
| #### `noChange` (Alpha) | ||
|
|
||
| Requires that the document is in the exact same state when the transaction is applied as it was when the transaction was authored. If _any_ concurrent edit is sequenced before the transaction, the constraint is violated and the transaction is rolled back. |
There was a problem hiding this comment.
The phrase "when the transaction is authored" might be unclear to readers. Consider using more precise terminology like "when the transaction is created/started" or "when the transaction callback begins executing" to clarify what "authored" means in this context.
| Requires that the document is in the exact same state when the transaction is applied as it was when the transaction was authored. If _any_ concurrent edit is sequenced before the transaction, the constraint is violated and the transaction is rolled back. | |
| Requires that the document is in the exact same state when the transaction is applied as it was when the transaction callback begins executing. If _any_ concurrent edit is sequenced before the transaction, the constraint is violated and the transaction is rolled back. |
| #### `nodeInDocument` | ||
|
|
||
| Requires that a specific node exists in the document. | ||
|
|
||
| ```typescript | ||
| const importantNode = view.root; | ||
|
|
||
| view.runTransaction( | ||
| () => { | ||
| view.root.value = 42; | ||
| }, | ||
| { |
There was a problem hiding this comment.
Question for reviewers: In theory, the old transaction API supports constraints, but in practice the old transaction API only supports nodeInDocument. I didn't document it because by my understanding we're not planning to invest in the old API due to its correctness issues and want to replace it. Should I add documentation on the old API specifically for nodeInDocument?
There was a problem hiding this comment.
IMO, it would probably still be worth at least a quick note. I'm not sure how quickly we will stabilize these new APIs (we have not really been bumping many things to public recently, so I fear it might be a while). But I'm curious how others feel.
|
🔗 No broken links found! ✅ Your attention to detail is admirable. linkcheck output |
| sidebar_position: 6 | ||
| --- | ||
|
|
||
| TODO:#27374: update this page to describe the new transaction API |
There was a problem hiding this comment.
Oof. We should avoid comments like this in our website docs. Users would see this. Glad to see it removed.
|
|
||
| The APIs described in this section are currently `@alpha` and may change in future releases. | ||
|
|
||
| Unlike `Tree.runTransaction`, the Alpha `runTransaction` does **not** automatically roll back the transaction if the callback throws an error. If your callback may throw, you should handle errors explicitly and return `{ rollback: true }` when required |
There was a problem hiding this comment.
Nit: Our Markdown best practices generally recommend line breaking along sentence boundaries. This helps make future changes a bit easier to review / reason about.
|
|
||
| ::: | ||
|
|
||
| The TreeBranchAlpha API introduces `TreeBranchAlpha.runTransaction` as a replacement for `Tree.runTransaction`. This new API exists to solve correctness issues where errors being thrown in runTransaction could result in an undefined state. This new API enables developers to customize how their application handles this error case. |
There was a problem hiding this comment.
Nit: I would recommend linking to the API docs for the referenced APIs here (assuming they've been published).
| ); | ||
| ``` | ||
|
|
||
| If transactions are nested, only the outermost transaction's label is used. |
There was a problem hiding this comment.
Nit: this makes sense, but it took me a minute to reason out why. I wonder if it would be worth noting the why.
| `runTransaction` and `runTransactionAsync` can be called from within the callback of another transaction, with some limitations. Nested transactions have the following behavior: | ||
|
|
||
| - If the inner transaction rolls back, only the inner transaction's changes are discarded. The outer transaction continues. | ||
| - Constraints are applied to the outermost transaction. A single commit is generated for the outermost transaction, encompassing all inner transactions. |
There was a problem hiding this comment.
What does this mean for constraints applied to inner transactions? Are they ignored? Or does this just mean that any constraints within the transaction (including nested transactions) are applied to the entire outer-most transaction?
|
|
||
| :::warning | ||
|
|
||
| An asynchronous transaction cannot be started inside a synchronous transaction. The other three nesting combinations (sync-in-sync, sync-in-async, async-in-async) are all supported. |
There was a problem hiding this comment.
What is the error mode that a user will see if they try to do async in sync?
| }, | ||
| { | ||
| preconditions: [ | ||
| { type: "nodeInDocument", node: someNode }, |
There was a problem hiding this comment.
This could probably use an inline comment explaining what the precondition does.
| const result = view.runTransaction(() => { | ||
| const oldName = view.root.name; | ||
| view.root.name = "Bob"; | ||
| return { rollback: false, value: oldName }; |
There was a problem hiding this comment.
Does rollback have a default? Or are users required to return something?
| () => { | ||
| view.root.name = "Dave"; | ||
| }, | ||
| { label: "rename-user" }, |
There was a problem hiding this comment.
An example of how this will affect the change metadata (either as an inline comment, or as notes following the codeblock) would probably be useful.
|
|
||
| ### Nested Transactions | ||
|
|
||
| `runTransaction` and `runTransactionAsync` can be called from within the callback of another transaction, with some limitations. Nested transactions have the following behavior: |
There was a problem hiding this comment.
These are obviously existing APIs, so this isn't feedback for this PR or anything, but I'm curious: would it be possible to make the async variation of this be an overload of runTransaction? Do we actually need 2 different function names?
| view.root.content = "new value"; | ||
| }, | ||
| { | ||
| preconditions: [ |
There was a problem hiding this comment.
Nit: A quick comment above this indicating that preconditions applies to the application of the transaction itself might be useful (to help identify how this example is different from the next one).
| view.root.content = "updated"; | ||
| return { | ||
| rollback: false, | ||
| preconditionsOnRevert: [ |
There was a problem hiding this comment.
Nit: A quick comment above this line indicating that the preconditionsOnRevert apply to if/when the associated transaction is reverted might be useful (to help identify how this example is different from the previous one).
This PR updates the docs for SharedTree's transaction API. It adds: