Core: Decompose BasePersistence into disjoint SPIs#4397
Conversation
|
@dennishuo @dimas-b @flyingImer @singhpk234 |
|
|
||
| @Override | ||
| public BasePersistence getOrCreateSession(RealmContext realmContext) { | ||
| public BasePersistence getOrCreateBasePersistence(RealmContext realmContext) { |
There was a problem hiding this comment.
nit: this rename causes insignificant changes in the context of this PR, which complicates review. It would be nicer to do the renaming before or after this PR 😅
There was a problem hiding this comment.
Happy to split if you'd prefer. I kept it in this PR because Dennis asked for the rename in his mailing-list reply.
There was a problem hiding this comment.
If you do not mind, let's do the rename separately and before this PR - it is a non-controversial rename - easy to review and approve :) then this (more difficult) PR will have a smaller diff (after rebasing).
There was a problem hiding this comment.
However... I personally already reviewed those rename changes 😅 so from my POV they are ok to keep in this PR.
| * implements {@link PolicyMappingPersistence}. | ||
| */ | ||
| default PolicyMappingPersistence getOrCreatePolicyMappingPersistence(RealmContext realmContext) { | ||
| return cast(getOrCreateBasePersistence(realmContext), PolicyMappingPersistence.class); |
There was a problem hiding this comment.
TBH, I do not really understand the purpose behind this design. We're still forcing implementation classes to support all the same interfaces that used to be extended by BasePersistence. IMHO, this make the overall design more awkward than before.
I believe the idea is/was to inject PolicyMappingPersistence into callers independently of BasePersistence. If they happen to be implemented by the same class, it should just work. However, we should not assume that (and use casts).
There was a problem hiding this comment.
You comment makes sense. I have updated the PR accordingly. PTAL
|
Thanks for working on this, @huaxingao ! Still, I believe this PR needs more work. It is moving in the right direction, though, from my POV 👍 |
| PolicyMappingPersistence getOrCreatePolicyMappingPersistence(RealmContext realmContext); | ||
|
|
||
| /** Returns the per-realm {@link MetricsPersistence}. */ | ||
| MetricsPersistence getOrCreateMetricsPersistence(RealmContext realmContext); |
There was a problem hiding this comment.
MetricsPersistence is very different from "meta store" persistence... both functionally and in java SPI parameters. I believe it makes sense to use a separate factory for it.
That said, current changes are net positive, IMHO, and are moving in the right direction.
I think we can deal with the MetricsPersistence factory in a separate PR.
|
|
||
| @Override | ||
| public BasePersistence getOrCreateSession(RealmContext realmContext) { | ||
| public BasePersistence getOrCreateBasePersistence(RealmContext realmContext) { |
There was a problem hiding this comment.
However... I personally already reviewed those rename changes 😅 so from my POV they are ok to keep in this PR.
|
|
||
| @Override | ||
| public MetricsPersistence getOrCreateMetricsPersistence(RealmContext realmContext) { | ||
| return newNoSqlMetaStore(realmContext); |
There was a problem hiding this comment.
I think, this should return a no-op new MetricsPersistence() {} for the sake of clarity - NoSQL persistence does not implement it anyway.
dimas-b
left a comment
There was a problem hiding this comment.
Thanks again for working on this, @huaxingao !
Sorry for pivoting, but I think we're making positive progress overall 😅
Let's concentrate on isolating MetricsPersistence and IdempotencyPersistence from BasePersistence for now. Old use cases may be too cumbersome and can remain "as is" as far as #4269 is concerned.
| abstract class NonFunctionalBasePersistence | ||
| implements BasePersistence, | ||
| IntegrationPersistence, | ||
| MetricsPersistence, |
There was a problem hiding this comment.
Please remove MetricsPersistence from this list - it's not really implemented.
| schemaVersion); | ||
| } | ||
|
|
||
| private JdbcBasePersistenceImpl getOrCreateJdbcPersistence(RealmContext realmContext) { |
There was a problem hiding this comment.
there's no "get" logic in this method, it always "creates". Please consider renaming to createJdbcPersistence() for clarity.
| ms.deleteAllEntityPolicyMappingRecords(callCtx, entity, mappingOnTarget, mappingOnPolicy); | ||
| : policyMappingPersistence.loadAllPoliciesOnTarget( | ||
| callCtx, entity.getCatalogId(), entity.getId()); | ||
| policyMappingPersistence.deleteAllEntityPolicyMappingRecords( |
There was a problem hiding this comment.
I think this goes against the grain of AtomicOperationMetaStoreManager javadoc, which says the class operates on one BasePersistence instance (logically).
IMHO, even the old code is not "atomic" with respect to ms.deleteAllEntityGrantRecords (line 213) and deleteAllEntityPolicyMappingRecords (line 236), but let's not aggravate this situation by introducing different objects. The old code at least invoked both methods on the same java object instance.
Sorry for missing this aspect when I initially suggested breaking BasePersistece down into all individual sub-interfaces. This code is not super easy to comprehend and keep in mind all the time 😅
With this code in mind, I think it makes sense to isolate only MetricsPersistence in this PR (and isolate idempotency persistence in #4269).
I think we need to keep this general difficulty (atomic operations) in mind for future BasePersistece refactorings, but for now I think it makes sense to keep "old" functionality intact and only isolate "new" functionality into purpose-based interfaces (MetricsPersistence, IdempotencyPersistence, etc.)
dimas-b
left a comment
There was a problem hiding this comment.
Thanks for bearing with me, @huaxingao 😅
|
Thanks so much for the careful review, @dimas-b! Appreciate the guidance on this PR! |
flyingImer
left a comment
There was a problem hiding this comment.
Nice first step toward making persistence SPIs independently implementable. Two small suggestions below to smooth the path for out-of-tree integrators.
| * BasePersistence} so backends that do not implement metrics persistence can simply return a | ||
| * no-op instance. | ||
| */ | ||
| MetricsPersistence getOrCreateMetricsPersistence(RealmContext realmContext); |
There was a problem hiding this comment.
Consider giving this a default implementation:
default MetricsPersistence getOrCreateMetricsPersistence(RealmContext realmContext) {
return new MetricsPersistence() {};
}MetricsPersistence methods are all no-op by default, so the factory should be too. Backends that don't implement metrics won't need to touch their factory class. They get silent no-op for free, and the source-breaking surface shrinks.
There was a problem hiding this comment.
Thanks for the suggestion! I'd prefer to keep this abstract. @dimas-b 's earlier comment on the NoSQL override asked for an explicit new MetricsPersistence() {} "for the sake of clarity", my read is we should avoid a factory-level default.
There was a problem hiding this comment.
+1 to avoid factory-level defaults.
Also, I suggested making a separate factory for MetricsPersistence as a follow-up. If we go this way, implementation will always be complete (no need for default methods).
| } | ||
|
|
||
| @Override | ||
| public MetricsPersistence getOrCreateMetricsPersistence(RealmContext realmContext) { |
There was a problem hiding this comment.
Nit: stateless, so a private static final singleton avoids per-call allocation.
Refactor the persistence SPIs so
BasePersistence,PolicyMappingPersistence,MetricsPersistence, andIntegrationPersistenceare disjoint top-level interfaces instead ofBasePersistencebeing an aggregator. Concrete backends (JDBC, NoSQL) multi-inherit the ones they support, andTransactionalPersistenceaggregates them where atomic semantics are needed.Changes
BasePersistenceno longer extendsPolicyMappingPersistenceorMetricsPersistence.TransactionalPersistencenow extends all four SPIs directly.JdbcBasePersistenceImplandNonFunctionalBasePersistenceimplement all four SPIs.MetaStoreManagerFactory: renamegetOrCreateSession→getOrCreateBasePersistence; addgetOrCreate{PolicyMapping,Metrics,Integration}Persistence.PolarisCallContext: renamegetMetaStore→getBasePersistence; addget{PolicyMapping,Metrics,Integration}Persistence.((IntegrationPersistence) ms)blind casts inAtomicOperationMetaStoreManagerwithcallCtx.getIntegrationPersistence().Checklist
CHANGELOG.md(if needed)site/content/in-dev/unreleased(if needed)