Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEATURE (performance): compress SetSerializedNodeProperties together; allow experiments in codebase #4263

Draft
wants to merge 6 commits into
base: 9.0
Choose a base branch
from

Conversation

skurfuerst
Copy link
Member

@skurfuerst skurfuerst commented May 5, 2023

When people write texts, the Neos UI will send many SetSerializedNodeProperties commands touching the same node.

This change compacts this to a single SetSerializedNodeProperties command, so that projection rebase is sped up.

TODO: This is not yet well tested, and I do not like the code for the compaction algorithm so far.

Related: #4387 #4388

When people write texts, the Neos UI will send many
SetSerializedNodeProperties commands touching the same node.

This change compacts this to a single SetSerializedNodeProperties command,
so that projection rebase is sped up.

TODO: This is not yet well tested, and I do not like the code for
the compaction algorithm so far.
@bwaidelich
Copy link
Member

I like the idea, but I have the feeling that we should go for a more generic approach here (I know the caveats of generic approaches, but IMO in this case it's worth it).

Also I wonder, if that "compraction" should be done on the events instead (as we won't have to replay commands at some point hopefully).
Maybe we can also combine this logic with #4284 (i.e. two disabled events can be compracted to one)

@skurfuerst
Copy link
Member Author

Yeah this PR is merely a draft; although speeding up SetNodeProperties is particularly performance relevant because the UI sends LOTS of these commands during normal editing (and right now we preserve them until live).

So I am not fully sure on a totally generic solution, because this solution actually solves 95% of the Neos UI pain :)

All the best,
Sebastian

@skurfuerst skurfuerst marked this pull request as ready for review August 3, 2023 18:52
@skurfuerst skurfuerst changed the title FEATURE (performance): compress SetSerializedNodeProperties together FEATURE (performance): compress SetSerializedNodeProperties together; allow experiments in codebase Aug 3, 2023
Copy link
Member

@bwaidelich bwaidelich left a comment

Choose a reason for hiding this comment

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

Looking great, just some minor remarks.

Apart from those I am not sure whether we need the experiment "cateogories" (at this point in time):
i.e. you added the "compress-simple" experiment as some kind of sub-category of "compactCommands" but I am not sure if it makes things easier or more cumbersome actually.
If we go for a hierarchy, I would suggest to rename "compactCommands" to something slightly more general because other compression mechanism might not fit in this scheme (and thinking about it, we don't really compact commands but events, right?)

| text | "content 3" |

# THIS IS THE MODIFICATION
Then I expect exactly 8 events to be published on stream "ContentStream:cs-identifier"
Copy link
Member

Choose a reason for hiding this comment

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

clever way to test this and great job with the comments!

@@ -506,6 +510,7 @@ private function handlePublishIndividualNodesFromWorkspace(
);

$originalCommands = $this->extractCommandsFromContentStreamMetadata($workspaceContentStreamName);
$originalCommands = $this->compactCommands($originalCommands);
Copy link
Member

Choose a reason for hiding this comment

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

Just a minor issue of course, but I would suggest to move the "feature flag" check out here, e.g.:

Suggested change
$originalCommands = $this->compactCommands($originalCommands);
if ($this->experiments->compactCommands_compressSimple()) {
$originalCommands = $this->compactCommands($originalCommands);
}

@@ -627,6 +632,7 @@ private function handleDiscardIndividualNodesFromWorkspace(
);

$originalCommands = $this->extractCommandsFromContentStreamMetadata($workspaceContentStreamName);
$originalCommands = $this->compactCommands($originalCommands);
Copy link
Member

Choose a reason for hiding this comment

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

same here

Comment on lines +931 to +973
if ($this->experiments->compactCommands_compressSimple()) {
$compressedCommands = [];
/* @var $lastSetSerializedNodePropertiesCommand \Neos\ContentRepository\Core\Feature\NodeModification\Command\SetSerializedNodeProperties */
$lastSetSerializedNodePropertiesCommand = null;
foreach ($commands as $command) {
if (
$command instanceof SetSerializedNodeProperties
&& $lastSetSerializedNodePropertiesCommand !== null
&& $lastSetSerializedNodePropertiesCommand->appliesToSameNode($command)
) {
// COMPATIBLE: we can merge with a previous command, and reduce the command count.
$lastSetSerializedNodePropertiesCommand = $lastSetSerializedNodePropertiesCommand->mergeWith($command);
} elseif ($command instanceof SetSerializedNodeProperties) {
// Incompatible SetSerializedNodeProperties command => store the
// last compressed command if any
if ($lastSetSerializedNodePropertiesCommand !== null) {
$compressedCommands[] = $lastSetSerializedNodePropertiesCommand;
$lastSetSerializedNodePropertiesCommand = null;
}

$lastSetSerializedNodePropertiesCommand = $command;
} else {
// different command type
// so store the compacted command
if ($lastSetSerializedNodePropertiesCommand !== null) {
$compressedCommands[] = $lastSetSerializedNodePropertiesCommand;
$lastSetSerializedNodePropertiesCommand = null;
}

$compressedCommands[] = $command;
}
}

// we are not allowed to lose commands
if ($lastSetSerializedNodePropertiesCommand !== null) {
$compressedCommands[] = $lastSetSerializedNodePropertiesCommand;
$lastSetSerializedNodePropertiesCommand = null;
}

return $compressedCommands;
} else {
return $commands;
}
Copy link
Member

Choose a reason for hiding this comment

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

as a result to the above I would suggest to remove the feature flag check here:

Suggested change
if ($this->experiments->compactCommands_compressSimple()) {
$compressedCommands = [];
/* @var $lastSetSerializedNodePropertiesCommand \Neos\ContentRepository\Core\Feature\NodeModification\Command\SetSerializedNodeProperties */
$lastSetSerializedNodePropertiesCommand = null;
foreach ($commands as $command) {
if (
$command instanceof SetSerializedNodeProperties
&& $lastSetSerializedNodePropertiesCommand !== null
&& $lastSetSerializedNodePropertiesCommand->appliesToSameNode($command)
) {
// COMPATIBLE: we can merge with a previous command, and reduce the command count.
$lastSetSerializedNodePropertiesCommand = $lastSetSerializedNodePropertiesCommand->mergeWith($command);
} elseif ($command instanceof SetSerializedNodeProperties) {
// Incompatible SetSerializedNodeProperties command => store the
// last compressed command if any
if ($lastSetSerializedNodePropertiesCommand !== null) {
$compressedCommands[] = $lastSetSerializedNodePropertiesCommand;
$lastSetSerializedNodePropertiesCommand = null;
}
$lastSetSerializedNodePropertiesCommand = $command;
} else {
// different command type
// so store the compacted command
if ($lastSetSerializedNodePropertiesCommand !== null) {
$compressedCommands[] = $lastSetSerializedNodePropertiesCommand;
$lastSetSerializedNodePropertiesCommand = null;
}
$compressedCommands[] = $command;
}
}
// we are not allowed to lose commands
if ($lastSetSerializedNodePropertiesCommand !== null) {
$compressedCommands[] = $lastSetSerializedNodePropertiesCommand;
$lastSetSerializedNodePropertiesCommand = null;
}
return $compressedCommands;
} else {
return $commands;
}
$compressedCommands = [];
/* @var $lastSetSerializedNodePropertiesCommand \Neos\ContentRepository\Core\Feature\NodeModification\Command\SetSerializedNodeProperties */
$lastSetSerializedNodePropertiesCommand = null;
foreach ($commands as $command) {
if (
$command instanceof SetSerializedNodeProperties
&& $lastSetSerializedNodePropertiesCommand !== null
&& $lastSetSerializedNodePropertiesCommand->appliesToSameNode($command)
) {
// COMPATIBLE: we can merge with a previous command, and reduce the command count.
$lastSetSerializedNodePropertiesCommand = $lastSetSerializedNodePropertiesCommand->mergeWith($command);
} elseif ($command instanceof SetSerializedNodeProperties) {
// Incompatible SetSerializedNodeProperties command => store the
// last compressed command if any
if ($lastSetSerializedNodePropertiesCommand !== null) {
$compressedCommands[] = $lastSetSerializedNodePropertiesCommand;
$lastSetSerializedNodePropertiesCommand = null;
}
$lastSetSerializedNodePropertiesCommand = $command;
} else {
// different command type
// so store the compacted command
if ($lastSetSerializedNodePropertiesCommand !== null) {
$compressedCommands[] = $lastSetSerializedNodePropertiesCommand;
$lastSetSerializedNodePropertiesCommand = null;
}
$compressedCommands[] = $command;
}
}
// we are not allowed to lose commands
if ($lastSetSerializedNodePropertiesCommand !== null) {
$compressedCommands[] = $lastSetSerializedNodePropertiesCommand;
$lastSetSerializedNodePropertiesCommand = null;
}
return $compressedCommands;

* @param array<string,mixed> $in
* @api
*/
public static function fromArray(array $in): self
Copy link
Member

Choose a reason for hiding this comment

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

$in is an unusual array name :)

@bwaidelich
Copy link
Member

one more reservation towards this approach:
When trying to visualize the events of a publishing process I realized that we are missing a way to correlate published events to their original representation in the published content stream (see #3887 (comment)).
By merging commits that possibility will be gone

@mhsdesign
Copy link
Member

With the new publishing v3 - as publish workspace behaves like publish individual nodes - i could imagine having this as a core feature. We already lowered the update interval of ckeditor neos/neos-ui#3751

The debounce timer has been extended from 500ms to 1500ms

though it might be sensible to apply both solutions?

And regarding the mixed up correlation to the original events ... maybe we should just compact commands that are directly send after another.

@mhsdesign mhsdesign marked this pull request as draft November 26, 2024 09:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants