ContainmentAwareResourceStore
michielbdejong opened this issue · 5 comments
The diagram doesn't explicitly state what e.g. deleteResource
should do in practice, but the method name suggests its task is to delete the resource identified by the ResourceIdentifier.
That means that if we want to use that method in the LdpDeleteOperation to implement the LDP DELETE verb, then we also need to call modifyResource
to remove a containment triple from the LDP-BC of which the resource is a member.
That means we would again have a situation where we would need a transaction.
Would it be an idea to define a TreeAwareResourceStore which decorates a generic ResourceStore to take care of containment triple additions and removals?
The diagram doesn't explicitly state what e.g.
deleteResource
should do in practice, but the method name suggests its task is to delete the resource identified by the ResourceIdentifier.
Correct.
That means that if we want to use that method in the LdpDeleteOperation to implement the LDP DELETE verb, then we also need to call
modifyResource
to remove a containment triple from the LDP-BC of which the resource is a member.
No, it would be the store's responsibility to handle this. The reason for this is that it is up to the back-end to determine how containment triples are generated. They might be explicitly materialized (in which case the store would need to do the deletion) or indirectly (such as in the filesystem, where they are derived from a file listing, and hence happen automatically).
So it's a matter of knowledge containment: we don't want callers to know how stores work internally.
That means we would again have a situation where we would need a transaction.
It would instead be part of the atomicity of the deleteResource
method. (But indeed, that is not explicitly visible from the method name in the architectural diagram.)
Would it be an idea to define a TreeAwareResourceStore which decorates a generic ResourceStore to take care of containment triple additions and removals?
An issue with that is that it necessitates the caller to know about the different kinds of storage and what they need, which is the kind of knowledge we'd like to keep contained in a store itself.
OK, so IIUC, all ResourceStores (or at least all the ones we will use) are containment aware?
I think that can lead to some complicated concurrency issues in the CompositeResourceStore. getResource('/foo/') is easy: the CompositeResourceStore just does getResource('/foo/') on all its sources, and merges the result. For setResource('/foo/bar'), if the container '/foo/' exists in all sources, then the CompositeResourceStore looks at the content-type or similar to determine in which of its sources /foo/bar should live. If /foo/ doesn't exist in some sources, then you create it there. And if a non-container resource /foo exists in any of the sources, then you fail the request. But to make that thread-safe, you need to lock all source stores for /foo before creating /foo/ in all of them and creating /foo/bar in one of them. But we just decided that we don't want to require stores to support locking, right?
The way Trellis solves this is that there are two resource stores, one for LDP-RS and one for LDP-NR, and the LDP-RS one takes care of the containment triples for both. For an LDP-NR resource some triples are stored, including a reference to where the binary data contents can be found on the blob storage.
I guess the question of how a CompositeResourceStore would deal with containment triples in a thread-safe way is a complex one that we don't need to answer.
All we need in the short term is the NSS-compatible (filesystem-based) ResourceStore, and my original question in this issue has been answered, so great! We can open a separate issue about thread-safe CompositeResourceStore if we want to, but it doesn't need to block Operation Treasure Trove. :)
But to make that thread-safe, you need to lock all source stores for /foo before creating /foo/ in all of them and creating /foo/bar in one of them.
Exactly.
And the good news is that CompositeResourceStore
does not need to (but could) do that locking itself. Just wrap it with LockingResourceStore
, and it becomes thread-safe.
But we just decided that we don't want to require stores to support locking, right?
Indeed, they don't need to. They can if they want. And if they don't, we provide it for them.
all ResourceStores (or at least all the ones we will use) are containment aware?
Not all of them need to be.
They are edge cases, but there might be stores that only support a limited number of resources. Thinking of a real case now, where a certain back-end was a temperature sensor. It only had a single RDF resource, but no containers whatsoever.
The way Trellis solves this is that there are two resource stores, one for LDP-RS and one for LDP-NR, and the LDP-RS one takes care of the containment triples for both.
We can have a similar mechanism, where one store provides containment triples for others. That's the beauty of the decorator and composite patterns.
the CompositeResourceStore just does getResource('/foo/') on all its sources, and merges the result.
That would indeed be the case if CompositeResourceStore
would support one folder to be distributed across multiple sources. It might or might not.
If /foo/ doesn't exist in some sources, then you create it there.
That would not be necessary; it could just not exist.