adapter: optimizer and dyncfg cleanups for OCC read-then-write - #37919
Conversation
7826e5b to
71ab839
Compare
71ab839 to
1dd3e75
Compare
1dd3e75 to
a3a9a0c
Compare
a3a9a0c to
4b5e5d3
Compare
4b5e5d3 to
98e9782
Compare
ggevay
left a comment
There was a problem hiding this comment.
LGTM, some minor comments.
Claude: Two more things beyond the inline comments:
-
Could the dyncfg fix get a regression test pinning the sync? For example: open a debug catalog with a
system_parameter_defaultthat sets some dyncfg to a non-default value, then assertstate.system_config().dyncfgs()reflects it. The original bug stayed invisible because nothing asserts this, and such a test is also the only thing that would notice ifdyncfg_updates()ever lost its apply side effect. -
One question on the PR description: I could not find the drift it refers to ("one carried the reasoning about why the sink's
as_ofand the dataflow'suntilare set the way they are, the other had lost it"). At the merge base both match arms carry identical comments, and the as_of/until reasoning lives once inresolve(), which this PR does not touch. Were you thinking of the sequencing layer? There is real drift there: the coordinator path emits theEqualSubscribeBoundsnotice whenas_of == up_toand the frontend path does not. We filed that separately as SQL-599, out of scope for this PR. Either way, worth a small tweak to the description so future archaeology does not hunt for a comment that never existed.
| /// applies to all of them. | ||
| fn optimize_source( | ||
| &mut self, | ||
| source: SubscribeSource, |
There was a problem hiding this comment.
nit: This is not really optimizing the "source". I'd name this optimize_inner.
| // syncs the `ConfigSet` when a `SystemConfiguration` update is present, | ||
| // and a deployment configured purely via `system_parameter_default` has | ||
| // none, so sync explicitly here. | ||
| state.system_config().dyncfg_updates(); |
There was a problem hiding this comment.
Looks a bit surprising that this getter has a side effect. Maybe this could be refactored somehow. (Claude on this in the below thread.)
| // syncs the `ConfigSet` when a `SystemConfiguration` update is present, | ||
| // and a deployment configured purely via `system_parameter_default` has | ||
| // none, so sync explicitly here. | ||
| state.system_config().dyncfg_updates(); |
There was a problem hiding this comment.
This line works because dyncfg_updates() applies the computed updates to the SystemVars' own ConfigSet as a side effect (updates.apply(&self.dyncfgs) at the end of the method). From the call site it reads like a discarded getter result, and a future "make this getter pure" refactor would compile cleanly and silently turn this line and the twin in apply_updates into no-ops, reintroducing the bug.
Consider splitting it: keep dyncfg_updates() pure, add a sync_dyncfgs() that computes, applies, and returns. Only this site and apply_updates need the applying variant. Nice side effect of your fix: those two are now sufficient (sync at open, re-sync on every durable system-config change), so the bootstrap-time calls in coord.rs no longer depend on their accidental sync.
| /// by the optimizer implementations. | ||
| type LirDataflowDescription = DataflowDescription<LirRelationExpr>; | ||
| /// A type for a [`DataflowDescription`] backed by `Lir~` plans. | ||
| pub type LirDataflowDescription = DataflowDescription<LirRelationExpr>; |
There was a problem hiding this comment.
Tiny one: LirDataflowDescription becomes pub without a consumer in this PR (verified: reverting the pub compiles cleanly on this branch). Part 5 names it, so I assume intentional groundwork. Fine as is, just confirming it is deliberate.
…open A startup-only read of a dyncfg observed the compile-time default rather than the configured value, because the `ConfigSet` is only synced when a durable `SystemConfiguration` update is present. A deployment that configures a parameter purely through `system_parameter_default` has no such update, so nothing synced and the read silently saw the wrong value. Mirroring the effective values once, after both the defaults and the durable configuration are in the `SystemVars` map, fixes every read that happens during bootstrap. `ENABLE_EXPRESSION_CACHE`, read just below, is one of them. Computing the updates and applying them to our own `ConfigSet` were the same method, so the two callers that exist for the applying are the ones that discard the result. That reads like a getter whose value is thrown away, and a later change making the getter pure would compile cleanly while silently reintroducing this bug. `sync_dyncfgs` now names that intent. The six callers that forward the updates to metrics or the controllers keep the pure `dyncfg_updates`, since none of them needs this process's `ConfigSet` touched.
98e9782 to
e3e940e
Compare
|
(This is aj, Aljoscha's agent, acting on his behalf. He's away, so replies here Thanks, all four taken. The Split done. You're right about the description, and the claim was simply false. I
On the regression test, I'd push back slightly. The fix does have coverage, I'd also argue the split addresses your underlying worry better than a test
|
Subscribe optimization had two entry points that differed only in whether the source was a query or an existing id, and each rebuilt the same pipeline around that one decision. Keeping them in step was manual, and the copies had already drifted: one carried the reasoning about why the sink's `as_of` and the dataflow's `until` are set the way they are, the other had lost it. `optimize_source` takes the source as a `SubscribeSource` and the output shape as a parameter, so the pipeline exists once and the difference is an argument. The view optimizer's expression-preparation variants collapse the same way, into one constructor plus a builder method for the constant folding limit.
e3e940e to
734a762
Compare
Motivation
Part 2 of 7 in a stack that moves
DELETE,UPDATEandINSERT ... SELECToff the coordinator onto the session task, using optimistic concurrency
control.
Two independent cleanups that the later parts need, both of which stand on
their own. The first is a live bug.
Closes SQL-588
Description
ConfigSetsync at catalog open.apply_updatesonly mirrorsSystemVarsinto the dyncfgConfigSetwhen a durableSystemConfigurationupdate is present. A deployment that configures aparameter purely through
system_parameter_defaulthas no such update, sonothing syncs, and anything reading that dyncfg during bootstrap sees the
compile-time default instead of the configured value.
ENABLE_EXPRESSION_CACHEis read a few lines below and is affected today. Syncing the effective
values once, after both the defaults and the durable configuration are in
the map, fixes every such read.
One subscribe optimization pipeline. Subscribe optimization had two
entry points that differed only in whether the source was a query or an
existing id, and each rebuilt the same pipeline around that one decision.
Both rebuilt the same
SinkDescverbatim, down to the same three comments,and differed only in whether the source needed HIR lowering first. Taking the source as a
SubscribeSourceand the output shape as aparameter leaves the pipeline in one place. The view optimizer's
expression-preparation variants collapse the same way, into one constructor
plus a builder method for the constant folding limit.
The dyncfg commit also splits
dyncfg_updates, which computed the updatesand applied them to its own
ConfigSetin one method. The only two callersof the applying behavior are the ones that discard the result, so the call
sites read like a getter whose value is thrown away, and a later change
making the getter pure would compile cleanly while silently reintroducing
the bug.
sync_dyncfgsnames that intent. The six callers that forward theupdates to metrics or the controllers keep the pure
dyncfg_updates.Verification
Existing subscribe and
COPY FROMcoverage. No behavior change is intendedfor the optimizer part. The dyncfg change is observable as
ENABLE_EXPRESSION_CACHEnow taking effect when set only viasystem_parameter_default.