CIP-104: Add temporal ACS store for doing asOf lookups#4126
CIP-104: Add temporal ACS store for doing asOf lookups#4126dfordivam wants to merge 26 commits intodfordivam/feat-cip-104-activity-recordsfrom
Conversation
|
@meiersi-da This is the basic implementation of ScanTcsStore for a quick round of review. |
| ); | ||
|
|
||
| create index acs_store_archived_template_temporal | ||
| on acs_store_archived_template (store_id, migration_id, template_id_qualified_name, created_at, archived_at); |
There was a problem hiding this comment.
| on acs_store_archived_template (store_id, migration_id, template_id_qualified_name, created_at, archived_at); | |
| on acs_store_archived_template (store_id, migration_id, template_id_qualified_name, created_at) include (archived_at); |
probably the better option to have cheaper internal btree nodes and statistics
There was a problem hiding this comment.
actually I just realized that we will typically be looking up recent timestamps, which means changing the index to efficiently skip over past events is valuable.
Concretely
| on acs_store_archived_template (store_id, migration_id, template_id_qualified_name, created_at, archived_at); | |
| on acs_store_archived_template (store_id, migration_id, template_id_qualified_name, archived_at) include (created_at); |
|
|
||
| -- lookup FeaturedAppRight | ||
| create index scan_tcs_store_active_sid_mid_tid_farp | ||
| on scan_tcs_store_active (store_id, migration_id, template_id_qualified_name, featured_app_right_provider) |
There was a problem hiding this comment.
| on scan_tcs_store_active (store_id, migration_id, template_id_qualified_name, featured_app_right_provider) | |
| on scan_tcs_store_active (store_id, migration_id, featured_app_right_provider) |
seems enough or not?
There was a problem hiding this comment.
I will investigate
There was a problem hiding this comment.
also make the index partial to exclude NULLs. That ensures a small index size.
There was a problem hiding this comment.
I got rid of this as discussed yesterday we won't be doing lookup on party IDs
|
|
||
| -- record_time of the transaction that archived the contract, in micros since epoch. | ||
| archived_at bigint not null | ||
| ); |
There was a problem hiding this comment.
Do we need this given that we use like <actual_store_table_active> below?
There was a problem hiding this comment.
This is used only for the unit test of DbTemporalAcsStoreTest
There was a problem hiding this comment.
Consider renaming it to test_ instead of template_ then.
|
|
||
| -- temporal query support: created_at + archived_at filtering for point-in-time lookups on the archive table | ||
| create index scan_tcs_store_archived_temporal | ||
| on scan_tcs_store_archived (store_id, migration_id, template_id_qualified_name, created_at, archived_at); |
There was a problem hiding this comment.
| on scan_tcs_store_archived (store_id, migration_id, template_id_qualified_name, created_at, archived_at); | |
| on scan_tcs_store_archived (store_id, migration_id, template_id_qualified_name, created_at) include (archived_at); |
There was a problem hiding this comment.
Done, ie using archived_at in index
| ) | ||
| .asRuntimeException() | ||
| } | ||
| .flatMap(_ => waitUntilRecordTimeReached(asOf)) |
There was a problem hiding this comment.
@nicu-da : I remember you fixing some memory leak problems wrt this kind of recursive loop on futures. Is this construction here safe?
There was a problem hiding this comment.
After the fix in https://github.com/DACH-NY/canton-network-node/pull/9212 it should be safe. Modern futures are also pretty safe in chaining this way as they will flatten the recursion loop but it can still cause issues. Main issue if the waitUntilRecordTimeReached is called from a "hotspot" in the ingestion pipeline with a asOf which is relatively far in the future, thus we will have large number of waitUntilRecordTimeReached running in loops for each ingestion from the looks of it.
Ideally we should reverse and just add a promise that gets fulfilled when the ingestion reaches the asOf timestamp, removing the constant looping.
There was a problem hiding this comment.
At the moment the temporal store APIs have no external endpoints, and a single automation doing lookups, hence I went with the simpler approach. I doubt we will ever be exposing this to an external endpoint. The lookup record_time also would typically not be far in future. So I will let this be as it is for now.
There was a problem hiding this comment.
The lookup record_time also would typically not be far in future. So I will let this be as it is for now.
This is likely true. It is though one of these things that's going to be expensive to rediscover as part of scalability work. Let's create at least a tech-debt item the Scalability milestone to switch to a promise-based implementation.
There was a problem hiding this comment.
The situation where it may not be true are catch-up situations. The annoying thing is that these are exceptional, but operationally critical circumstances. So keeping them in mind during development is valuable.
There was a problem hiding this comment.
Ok, I fixed this to work similar to how offset based wakeup of Futures is currently being done.
| import slick.jdbc.canton.SQLActionBuilder | ||
| import slick.sql.SqlStreamingAction | ||
|
|
||
| trait TemporalAcsQueries extends AcsQueries { |
There was a problem hiding this comment.
Side note: @adetokunbo -- renaming DbAppActiviyRecordStore to AppActivityRecordQueries would seem to be more clear, as it really is mostly a helper class to build the queries akin to the TemporalAcsQueries here.
Food for thought. Not key to change, but might help with code clarity.
| protected def c(i: Int): Contract[AppRewardCoupon.ContractId, AppRewardCoupon] = | ||
| appRewardCoupon(i, dsoParty, contractId = validContractId(i)) | ||
|
|
||
| "DbTemporalAcsStore" should { |
There was a problem hiding this comment.
missing test for waitUntilRecordTimeReached
|
|
||
| import scala.concurrent.{ExecutionContext, Future} | ||
|
|
||
| class DbScanTemporalAcsStore( |
There was a problem hiding this comment.
Consider using the Tcs as an abbreviation for TemporalContractStore analogous to Acs for ActiveContractStore.
| ): Future[Seq[ContractWithState[TCid, T]]] = | ||
| multiDomainAcsStore.listContractsAsOf(companion, asOf, limit) | ||
|
|
||
| def lookupFeaturedAppRightsAsOf( |
There was a problem hiding this comment.
we're missing the one for looking them up by party. Ideally as a batch call.
There was a problem hiding this comment.
and in a later PR with caching.
There was a problem hiding this comment.
Hmmm. did we need [Party] based lookup in our flow?
There was a problem hiding this comment.
We'll need the featured app status for all confirmers in the verdict of a confirmation request. My intuition was that organizing the code such that we pre-resolve all of them in a single call is not too onerous and has better performance properties.
There was a problem hiding this comment.
Happy to postpone that though as a performance optimization, so we get to a working implementation more quickly.
There was a problem hiding this comment.
As discussed yesterday on call, we plan on doing a single AsOf lookup of all parties per round.
| import org.lfdecentralizedtrust.splice.store.{AppStore, MultiDomainAcsStore} | ||
| import org.lfdecentralizedtrust.splice.store.db.AcsInterfaceViewRowData | ||
|
|
||
| trait ScanTemporalAcsStore extends AppStore { |
There was a problem hiding this comment.
Consider repeating the comment wrt purpose here from design doc. Goal is to inform future readers as to the tight scoping of this. Perhaps a name that relates to that would help ScanRewardsReferenceDataStore might be an option.
Signed-off-by: Divam <dfordivam@gmail.com>
Signed-off-by: Divam <dfordivam@gmail.com>
Signed-off-by: Divam <dfordivam@gmail.com>
Signed-off-by: Divam <dfordivam@gmail.com>
Signed-off-by: Divam <dfordivam@gmail.com>
Signed-off-by: Divam <dfordivam@gmail.com>
Signed-off-by: Divam <dfordivam@gmail.com>
Signed-off-by: Divam <dfordivam@gmail.com>
Signed-off-by: Divam <dfordivam@gmail.com>
Signed-off-by: Divam <dfordivam@gmail.com>
Signed-off-by: Divam <dfordivam@gmail.com>
Signed-off-by: Divam <dfordivam@gmail.com>
Signed-off-by: Divam <dfordivam@gmail.com>
Signed-off-by: Divam <dfordivam@gmail.com>
Signed-off-by: Divam <dfordivam@gmail.com>
Signed-off-by: Divam <dfordivam@gmail.com>
Signed-off-by: Divam <dfordivam@gmail.com>
Signed-off-by: Divam <dfordivam@gmail.com>
Signed-off-by: Divam <dfordivam@gmail.com>
Signed-off-by: Divam <dfordivam@gmail.com>
Signed-off-by: Divam <dfordivam@gmail.com>
Signed-off-by: Divam <dfordivam@gmail.com>
Signed-off-by: Divam <dfordivam@gmail.com>
Signed-off-by: Divam <dfordivam@gmail.com>
Signed-off-by: Divam <dfordivam@gmail.com>
cb68841 to
e0e833f
Compare
dfordivam
left a comment
There was a problem hiding this comment.
@meiersi-da I have added the fixes and pushed the branch over latest main. PTAL
|
|
||
| -- record_time of the transaction that archived the contract, in micros since epoch. | ||
| archived_at bigint not null | ||
| ); |
| ); | ||
|
|
||
| create index acs_store_archived_template_temporal | ||
| on acs_store_archived_template (store_id, migration_id, template_id_qualified_name, created_at, archived_at); |
|
|
||
| -- lookup FeaturedAppRight | ||
| create index scan_tcs_store_active_sid_mid_tid_farp | ||
| on scan_tcs_store_active (store_id, migration_id, template_id_qualified_name, featured_app_right_provider) |
There was a problem hiding this comment.
I got rid of this as discussed yesterday we won't be doing lookup on party IDs
|
|
||
| -- temporal query support: created_at + archived_at filtering for point-in-time lookups on the archive table | ||
| create index scan_tcs_store_archived_temporal | ||
| on scan_tcs_store_archived (store_id, migration_id, template_id_qualified_name, created_at, archived_at); |
There was a problem hiding this comment.
Done, ie using archived_at in index
| ) | ||
| .asRuntimeException() | ||
| } | ||
| .flatMap(_ => waitUntilRecordTimeReached(asOf)) |
There was a problem hiding this comment.
Ok, I fixed this to work similar to how offset based wakeup of Futures is currently being done.
| } | ||
| } | ||
|
|
||
| override def ingestUpdateBatch(batch: NonEmptyList[TreeUpdateOrOffsetCheckpoint])(implicit |
There was a problem hiding this comment.
Actually I missed it. Added it now, along with a test.
| protected def c(i: Int): Contract[AppRewardCoupon.ContractId, AppRewardCoupon] = | ||
| appRewardCoupon(i, dsoParty, contractId = validContractId(i)) | ||
|
|
||
| "DbTemporalAcsStore" should { |
|
|
||
| import scala.concurrent.{ExecutionContext, Future} | ||
|
|
||
| class DbScanTemporalAcsStore( |
| ): Future[Seq[ContractWithState[TCid, T]]] = | ||
| multiDomainAcsStore.listContractsAsOf(companion, asOf, limit) | ||
|
|
||
| def lookupFeaturedAppRightsAsOf( |
There was a problem hiding this comment.
As discussed yesterday on call, we plan on doing a single AsOf lookup of all parties per round.
| import org.lfdecentralizedtrust.splice.store.{AppStore, MultiDomainAcsStore} | ||
| import org.lfdecentralizedtrust.splice.store.db.AcsInterfaceViewRowData | ||
|
|
||
| trait ScanTemporalAcsStore extends AppStore { |
Fixes #4118
Note this does not implement caching, only the DB based queries
Pull Request Checklist
Cluster Testing
/cluster_teston this PR to request it, and ping someone with access to the DA-internal system to approve it./hdm_teston this PR to request it, and ping someone with access to the DA-internal system to approve it.PR Guidelines
Fixes #n, and mention issues worked on using#nMerge Guidelines