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: ContentGraphAdapter for write side access #4979

Closed
wants to merge 20 commits into from

Conversation

kitsunet
Copy link
Member

@kitsunet kitsunet commented Apr 5, 2024

The write side no longer uses any regular finders
for checks but only accesses projection data via
the new ContentGraphAdapterInterface.

Fixes: #4973

@kitsunet
Copy link
Member Author

kitsunet commented Apr 5, 2024

Few notes here for starters:

  • I omitted visibility constraints everywhere as we can just assume "see everything" given that that was hardcoded before.
  • I added csId + workspaceName to all the methods that need it, a tuple might really make this nicer to read, but it works
  • I originally thought we expose the factory and not the implementation and you can grab an implementation bound to csId + workspace but there is a bunch of methods in there that do not rely on it and so it felt strange as you wouldn't have either information if you need methods that do not need it.
  • it's huuuuuuge, I haven't gone over and tried to reduce it by merging similar functionality, but maybe that's an option.
  • The interface and implementation should probably move to another place, I will snatch that from bastis PR.

@kitsunet
Copy link
Member Author

kitsunet commented Apr 5, 2024

Also I realized too late but why is e.g. the workspace projection in CR Core? it should be in the storage adapter implementations (so DBAL, postgres) etc. ? Same for the contentStreams...

@bwaidelich
Copy link
Member

Also I realized too late but why is e.g. the workspace projection in CR Core? it should be in the storage adapter implementations

Is that related?
But for the record: Those projections work with MySQL and PostgreSQL – If it was part of the storage adapter we would need to copy all of the implementations to both adapters.
But I agree that it should be in separate packages

@kitsunet
Copy link
Member Author

kitsunet commented Apr 5, 2024

More note.... I guess the adapter needs a bunch more dependencies to actually be able to provide Node(s) results, we'll see how that works out, they are obviously all available when building it, but I would rather want to avoid adding them...

@kitsunet
Copy link
Member Author

kitsunet commented Apr 5, 2024

Also I realized too late but why is e.g. the workspace projection in CR Core? it should be in the storage adapter implementations

Is that related?

I tried to get rid of ALL access to finders within the handlers, so now I also took the worksapce and contentstream related methods into the adapter, which doesn't work because the DBAL adapter implementation then doesn't know about those :D

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.

Thanks so much for your efforts!
I just did a first quick pass as preparation for our weekly.

Apart from the nitpicky comments, I think the adapter should always act on a workspace/contentstream

use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName;

/**
*
Copy link
Member

Choose a reason for hiding this comment

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

This needs some thorough documentation and an @internal annotation

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep, documentation is missing everywhere just wanted to get this out here for now. Important though, so thanks!

// TODO: Implement rootNodeAggregateWithTypeExists() method.
}

public function findParentNodeAggregates(ContentStreamId $contentStreamId, WorkspaceName $workspaceName, NodeAggregateId $childNodeAggregateId): iterable
Copy link
Member

Choose a reason for hiding this comment

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

Not directly related but we should really introduce a NodeAggregates DTO

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, can do maybe

The write side no longer uses any regular finders
for checks but only accesses projection data via
the new `ContentGraphAdapterInterface`.

Fixes: neos#4973
This also replaces the ContentStreamIdOverride
@kitsunet kitsunet force-pushed the feature/content-graph-adapter-4973 branch from d0f89bd to c7480fe Compare April 7, 2024 10:42
Adds the working DBAL implementation as well as some fixes.
Also new Factory/Builder concept
@kitsunet kitsunet marked this pull request as ready for review April 13, 2024 14:48
@kitsunet kitsunet marked this pull request as draft April 13, 2024 14:50
@kitsunet
Copy link
Member Author

kitsunet commented Apr 13, 2024

Now come the hard(er) parts in a way. The nitty gritty. Some of the missing methods (see linter errors) were removed by me because phpstorm didn't detect usage. The interesting question is which of those use cases do we consider valid "read" use cases that should use the content graph and which should use the new ContentGraphAdapter. I think the two controllers are probably real use cases and should be re-instated to work. the ContentCacheFlusher for example I would rather solve with the ContentGraphAdapter as well as the SiteServiceInternals...

All resolved for now, but I will probably poke a few other corners. Also now need to deduplicate the queries.

* Builder to combine injected dependencies and ProjectionFActoryDependencies into a ContentGraphAdapterFactory
* @internal
*/
class ContentGraphAdapterFactoryBuilder implements ContentGraphAdapterFactoryBuilderInterface
Copy link
Member Author

Choose a reason for hiding this comment

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

I wouldn't mind getting around having this, and am happy for suggestions, I don't really see it though. An ugly option would be to to have something along the lines of:

ContentGraphAdapterFactory::injectProjectionDependencies(ProjectionFactoryDependencies $projectionFactoryDependencies)

and the ContentGraphAdapterFactory::__construct just gets the adapter dependencies injected (eg. DBALClient), but then all methods in the ContentGraphAdapterFactory need to throw a "TooEarly" Exception if the projection dependencies were not injected yet.

Copy link
Member

Choose a reason for hiding this comment

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

AFAIS we only need it to pass in ContentRepositoryId, NodeTypeManager and PropertyConverter – Maybe we can make these explicit dependencies somehow

Copy link
Member

Choose a reason for hiding this comment

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

..or even make the NodeFactory an explicit dependency (via some NodeFactoryInterface) but I don't know... Let's discuss

I think the idea is good, just where we create it
and how we pass it along needs refinement, given
that this PR will not stay as it is anyways, just
putting it in to keep the idea intact.
@kitsunet
Copy link
Member Author

In our weekly 2024-04-19 we talked about how this adapter is maybe not worth the trouble and if we can solve the ugly contentStreamIdOverride differently without this. And it is indeed difficult to make assumptions for after introduction of node identity, but it is very clear to me that at a higher level commands and API will use workspaceName the queries (at least for the DBAL Adapter) will need a contentStreamId to work, therefore this translation will have to happen somewhere, IMHO the changes for NodeIdentity will just push this deeper into the read side making it even more annoying (e.g. due toContentSubgraphWithRuntimeCaches) to override for the few caess that we have. And those are valid cases as we apply commands with $some workspaceName to a contentStream that has (not yet got) this workspaceName.

This together with the (from the POV of the write side) convoluted API to read stuff lets me think that this change could have merit even IF we cannot at this point avoid using read models (IMHO this could be a later improvement though) and much easier to just change the write side interface for this.

Convoluted because we apply filters, visibility and a lot of other stuff.

I am still not in love with the triple factory thing, but the separation of APIs seems very sensible still.

@kitsunet kitsunet requested a review from mhsdesign April 23, 2024 12:37
Comment on lines 8 to +11
'Neos.ContentRepository:ContentGraph':
factoryObjectName: Neos\ContentGraph\DoctrineDbalAdapter\DoctrineDbalContentGraphProjectionFactory
contentGraphAdapterFactory:
factoryObjectName: Neos\ContentGraph\DoctrineDbalAdapter\ContentGraphAdapterFactoryBuilder
Copy link
Member

Choose a reason for hiding this comment

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

optional followup would be to simplify this config

if (!$contentGraphAdapterFactoryBuilder instanceof ContentGraphAdapterFactoryBuilderInterface) {
throw InvalidConfigurationException::fromMessage('contentGraphAdapterFactory.factoryObjectName for content repository "%s" is not an instance of %s but %s.', $contentRepositoryIdentifier->value, ContentGraphAdapterFactoryBuilderInterface::class, get_debug_type($contentGraphAdapterFactoryBuilder));
}
// TODO: Do we want to add options here? Would then have to be a setter I guess....
Copy link
Member

Choose a reason for hiding this comment

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

todo over here ;)

Copy link
Member

Choose a reason for hiding this comment

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

We should refine this naming

Comment on lines +483 to +485
public function getWorkspace(): Workspace
{
$query = $this->dbalConnection->prepare('SELECT * FROM cr_default_p_workspace WHERE workspacename LIKE :workspaceName');
Copy link
Member

Choose a reason for hiding this comment

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

illegal select, and the Workspace tdo is too big

Comment on lines +470 to +471
// TODO: This table is not allowed here...
$query = $this->dbalConnection->prepare('SELECT currentcontentstreamid FROM cr_default_p_workspace WHERE workspacename LIKE :workspaceName');
Copy link
Member

Choose a reason for hiding this comment

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

todo illegal

@bwaidelich bwaidelich mentioned this pull request Apr 26, 2024
6 tasks
@bwaidelich
Copy link
Member

This is to be replaced by #5028 and I suggest to close the PR (we can still keep the branch for reference if needed)

@kitsunet kitsunet closed this May 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

TASK: Introduce (internal) low level content graph api for constraint checks and write side
3 participants