-
-
Notifications
You must be signed in to change notification settings - Fork 223
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
base: 9.0
Are you sure you want to change the base?
Conversation
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.
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). |
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, |
There was a problem hiding this 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" |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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.:
$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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here
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; | ||
} |
There was a problem hiding this comment.
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:
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 |
There was a problem hiding this comment.
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 :)
one more reservation towards this approach: |
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
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. |
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