Copilot commented on code in PR #8351:
URL: https://github.com/apache/texera/pull/8351#discussion_r4118029876
##########
frontend/src/app/workspace/service/workflow-graph/model/workflow-action.service.ts:
##########
@@ -797,25 +876,125 @@ export class WorkflowActionService {
}
public setWorkflowSettings(workflowSettings: WorkflowSettings | undefined):
void {
- if (this.workflowSettings === workflowSettings) {
+ const newSettings = workflowSettings === undefined ?
this.getDefaultSettings() : workflowSettings;
+ // Skip a redundant write: setting the same value would still cut a Yjs
update and observer
+ // churn for every collaborator.
+ if (isEqual(this.getWorkflowSettings(), newSettings)) {
return;
}
-
- const newSettings = workflowSettings === undefined ?
this.getDefaultSettings() : workflowSettings;
- this.workflowSettings = newSettings;
+ this.texeraGraph.sharedModel.contentMetaMap.set("settings", newSettings);
}
public getWorkflowSettings(): WorkflowSettings {
- return this.workflowSettings;
+ return (this.readContentMeta("settings") as WorkflowSettings) ??
this.getDefaultSettings();
}
/**
- * Load a definition without announcing an edit. Used while opening a
workflow, so
- * that merely reading one does not look like a change and trigger a save.
+ * Seed workflowSettings into the shared model while opening a workflow (see
seedContentMeta).
+ * Called under the reloading flag, so the seed is not announced as an edit.
+ */
+ public hydrateSettings(workflowSettings: WorkflowSettings | undefined): void
{
+ this.seedContentMeta("settings", workflowSettings);
+ }
+
+ /**
+ * Load a definition into the shared model while opening a workflow (see
seedContentMeta).
+ * Called under the reloading flag, so the shared-map observer skips this
seed and opening a
+ * workflow is not announced as an edit.
*/
public hydrateFormBinding(formBinding: FormBindingConfig | undefined): void {
- this.formBindingLoaded = formBinding !== undefined;
- this.formBinding = formBinding ?? getDefaultFormBinding();
+ this.seedContentMeta("formBinding", formBinding);
+ }
+
+ /**
+ * Put the database's copy of a content-meta value into the shared model as
a workflow opens,
+ * without taking a co-editor's newer one away.
+ *
+ * The document is created and connected just before this runs, so the
provider has usually not
+ * synced yet. Writing straight away is a concurrent whole-value write, and
Yjs resolves those
+ * by client id rather than by recency: the copy the database handed us
could win over an edit a
+ * co-editor has not saved yet -- the lost update this all exists to
prevent. So wait for the
+ * first sync, then write only when the key is still absent; a value that
came from the room is
+ * authoritative, whether it is a co-editor's edit or our own from another
tab.
+ *
+ * Meanwhile the value is not lost: reads fall back to the copy held here,
so the page shows it
+ * and an autosave that fires before the sync carries it rather than an
empty one. The wait is
+ * not bounded: a copy published before the sync would be the concurrent
write above, on a room
+ * that is slow to answer as much as on one that never does. For as long as
the room stays out
+ * of reach the copy is read from here and every save carries it, and it
goes into the document
+ * when the sync eventually comes -- or never, which loses nothing, since
nobody else was in that
+ * document either.
+ *
+ * A local edit of the key while the seed waits cancels it (see
observeContentMeta): the document
+ * then carries the author's value, which the seed must neither write over
nor delete.
+ *
+ * The room's precedence is for the first seed of a key on a document, the
one a workflow is
+ * opened with. A later reload into the same document -- a version shown, or
returned from; an
+ * agent's rewrite -- is the intent, and its seed replaces whatever the
document holds, a value
+ * the room put there included; `undefined` then clears the key, so a
version that carries no
+ * settings or no form definition does not keep the open workflow's.
+ */
+ private seedContentMeta(key: ContentMetaKey, value: ContentMetaValue |
undefined): void {
+ // This seed replaces any the key was still waiting on (and stops its
listening); it has an
+ // identity of its own, since a cleared key (undefined) has no value to be
told apart by.
+ this.cancelContentMetaSeed(key);
+ const seed: ContentMetaSeed = {};
+ this.contentMetaSeeds.set(key, seed);
+ if (value !== undefined) {
+ this.pendingContentMeta.set(key, value);
+ }
+ const shared = this.texeraGraph.sharedModel;
+ // Runs at once or when the sync comes, and then for this seed only: a
later seed of the key, a
+ // local edit of it and the loading of another document all detach the
listening before they
+ // would make this seed stale (see cancelContentMetaSeed,
observeContentMeta).
+ const land = () => {
+ this.cancelContentMetaSeed(key);
+ const first = !this.seededContentMetaKeys.has(key);
+ this.seededContentMetaKeys.add(key);
+ // Over the first seed the room's value wins, whether it is a
co-editor's edit or this
+ // client's from another tab; a later reload replaces it.
+ if (first && this.roomOwnedContentMeta.has(key)) {
+ return;
Review Comment:
A room-side deletion cannot reliably win this first-seed check.
`roomOwnedContentMeta` is populated only from changed keys, but after a
collaborator reloads content without `formBinding`, the room's final map state
is simply “key absent”; a newly joining client can fetch the still-stale
database copy, observe no present room value during sync, and then seed that
copy here, resurrecting the deleted definition. Represent absence explicitly in
the shared document (for example, with a typed tombstone/presence wrapper) so a
synchronized deletion is distinguishable from a room that has never initialized
this key.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]