diff --git a/docs/design/CLAUDE_cross_cn_ddl_visibility_fence.md b/docs/design/CLAUDE_cross_cn_ddl_visibility_fence.md new file mode 100644 index 0000000000000..b22a5be6ff79c --- /dev/null +++ b/docs/design/CLAUDE_cross_cn_ddl_visibility_fence.md @@ -0,0 +1,189 @@ +# Cross-CN DDL Visibility Fence + +- Status: **Approved** +- Revision: 2 +- Approval: user approval recorded in the PR implementation session on 2026-08-30 +- Owning issue: #27743 +- Implementation PR: #27756 +- Protocol version: MORPC v41 + +## 1. Classification and motivation + +This change is a distributed-protocol feature rather than a narrow local fix. It crosses frontend transaction commit, CN lifecycle and query RPC, cluster membership, HAKeeper replicated state, protobuf compatibility, and proxy admission. It changes persistent state, wire contracts, mixed-version rollout, restart, and rollback behavior; therefore it requires design approval. + +Issue #27743 demonstrates a missing catalog-visibility invariant: a DDL can commit on CN A while CN B admits a fresh snapshot before applying the corresponding catalog logtail. The client then observes `no such table` or equivalent stale-catalog behavior. + +## 2. Invariant and success criteria + +### Safety invariant + +After the v41 deployment epoch is committed, every CN capable of public DDL production must satisfy both conditions: + +1. its runtime protocol is at least v41, so DDL commit performs `SyncCommitV2` fan-out; and +2. it belongs to the exact generation/address membership set atomically committed by HAKeeper, or it remains fail-closed until it joins a later exact cut. + +Before a CN opens public ingress, its local catalog frontier must be at least the maximum frontier reported by all participants in the applicable cut. + +### Negation + +It is unsafe for any public or already-connected CN to commit DDL below v41 while another public CN can admit a snapshot that has not applied that commit. + +### Measurable criteria + +- A create on CN0 followed immediately by a first read/load on CN1 succeeds without explicit `SYNCCOMMIT`. +- Markerless startup preserves the current-main v40 baseline before the v41 cut. +- No local downgrade below v41 is accepted after the monotonic v41 epoch is committed. +- Membership change between final scan and epoch commit rejects the old target set atomically. +- Restart, timeout, persistence failure, response loss, and leader failover remain fail-closed. + +## 3. State ownership + +| State | Owner | Persistence | Meaning | +|---|---|---|---| +| Compiled latest protocol | binary/runtime | binary | Receiver/sender code capability only | +| Local deployed protocol | CN metadata file | durable local FS | This CN completed or provisionally entered a cut | +| Cluster deployed epoch | HAKeeper CNState | replicated snapshot/log | Monotonic cluster-wide committed cut | +| Admission generation/address | HAKeeper CNState | replicated snapshot/log | Identity of one CN incarnation | +| Prepared/Fenced/Complete | CN service and HAKeeper CNState | heartbeat-replicated phase proof plus local marker | Progress bound to one authoritative CN incarnation | +| Last committed DDL frontier | DDLCommitGate and HAKeeper CNState | monotonic process state replicated by heartbeat | Catalog frontier produced by that CN, excluding unrelated/no-op snapshot timestamps | +| Public DDL gate | frontend `DDLCommitGate` | process-local | Whether new public/background DDL may enter | +| Proxy ingress readiness | CN heartbeat/HAKeeper | replicated latest state | Whether new routed sessions may enter | + +HAKeeper is the first owner of cluster epoch and membership linearization. CN local metadata is not authoritative for cluster membership. + +## 4. Protocol states + +A CN is in one of these logical states: + +1. **Baseline v40**: v41 not deployed; existing v38-v40 contracts remain active. Public ingress may be open. +2. **Withdrawing**: activation blocks new DDL, withdraws ingress, and drains active DDL. +3. **Prepared v41**: local old-protocol producers are drained; runtime can receive v41 RPCs. +4. **Provisionally fenced**: local frontier synchronization completed and `-41` is durable; ingress remains closed. +5. **Cluster committed**: HAKeeper atomically validated exact `(serviceID, generation, queryAddress)` membership and advanced epoch to 41. +6. **Locally committed**: CN persisted `41`; only then may it republish ingress and unblock DDL. +7. **Markerless post-cut**: no local marker but HAKeeper epoch is 41; runtime remains v41 and ingress/DDL remain closed until a complete retry. + +The cluster epoch commit is the linearization point. Prepared, Fenced, and the last committed DDL frontier are published through each incarnation's heartbeat. The commit heartbeat contains the exact target tuples. In one replicated transition HAKeeper updates the sender heartbeat, compares all eligible raw CNState members, exact generation/address, receiver capability, and that each current incarnation itself published Prepared and Fenced, then advances the epoch only on exact equality. A replacement cannot reuse an older incarnation's Fenced proof. A join before this transition invalidates the target set; a join after it observes epoch 41 and is rejected as ingress-ready. + +## 5. End-to-end flow + +### First rollout + +1. All LogStore/HAKeeper replicas advertise support for the epoch schema. +2. Every CN runs v41-capable code but markerless CNs keep protocol baseline v40. +3. `mo_ctl SetProtocolVersion` refreshes raw authoritative CN membership. +4. The requested set must exactly match all eligible CN tuples and each target must advertise the v41 receiver/barrier capability. +5. Targets concurrently withdraw ingress, block and drain DDL, then heartbeat Prepared together with their monotonic last committed DDL frontier. +6. Each target reads the replicated HAKeeper phase/frontier inventory, applies every remote producer frontier, durably enters provisional Fenced, and heartbeats Fenced. This avoids cyclic QueryService control RPCs while every target is already serving a long-running activation RPC. +7. Each target confirms all exact current incarnations are Fenced in HAKeeper; HAKeeper atomically revalidates those tuple-bound proofs and commits epoch 41. +8. Each CN persists local committed 41, republishes ingress if listeners are live, and unblocks public DDL. + +### Steady-state DDL + +A public real-user DDL, and background DDL after public listeners are enabled, enters `DDLCommitGate`. After commit, protocol v41 triggers `SyncCommitV2` to all barrier-ready CNs. The operation succeeds only after required receivers have applied/synchronized the commit frontier. Bootstrap background work before ingress remains exempt to avoid depending on an unavailable QueryService. + +### Scale-out and replacement + +A markerless CN performs an atomic ingress heartbeat handshake. If it linearizes before the cluster commit, it becomes authoritative membership and invalidates any old target proof. If it linearizes after commit, HAKeeper forces ingress false and returns epoch 41. It never becomes a public v40 producer after the cut. + +## 6. Failure, retry, and lifecycle behavior + +- **Withdrawal failure or drain timeout**: ingress and DDL gate remain closed; retry uses raw QueryService identity. +- **Frontier RPC/application failure**: CN remains Prepared or provisional and closed. +- **Provisional persistence failure**: Fenced is never published. +- **Atomic epoch membership rejection**: epoch does not advance; all targets remain closed for retry with a refreshed set. +- **Epoch response loss**: epoch may be committed, but local marker remains provisional and ingress closed; retry learns the committed epoch. +- **Committed local persistence failure**: cluster epoch remains committed; this CN remains closed and restarts from provisional state. +- **Ingress publication uncertainty**: perform a bounded cleanup withdrawal; never assume publication failed. +- **Restart**: committed marker runs startup frontier synchronization before opening; provisional or markerless post-cut starts v41 fail-closed. +- **Shutdown**: stop periodic heartbeat publication, then withdraw ingress/barrier state before stopping QueryService. +- **Leader failover**: activation is gated until every voting and non-voting LogStore advertises epoch-schema capability, so any eligible HAKeeper leader preserves the field. + +Retries are idempotent for the same generation and target set. Replacement generations or addresses are rejected before local state mutation. + +## 7. Compatibility, rollout, downgrade, and rollback + +### Mixed-version baseline + +Current main already deploys v40 semantics. A fresh v41-capable process without a DDL marker therefore remains at v40, not v37. The v41 DDL state is separate from unrelated v38-v40 capabilities. + +### Rollout order + +1. Upgrade all voting and non-voting LogStores/HAKeeper replicas. +2. Upgrade every CN; verify barrier receiver capability in raw inventory. +3. Invoke one exact complete-target v41 activation. +4. Verify epoch 41 and all eligible CN ingress/committed markers. + +### Downgrade policy + +Before epoch 41, ordinary protocol changes at or below v40 remain possible. After epoch 41, local downgrade below v41 is rejected. A safe rollback would require a separately designed atomic cluster rollback that withdraws and drains every DDL producer before lowering the epoch; this revision deliberately does not implement epoch rollback. + +Binary rollback after epoch 41 is unsupported until such a rollback protocol exists. Operators must restore forward to a v41-capable binary. + +## 8. Proxy and direct ingress + +Proxy routing consumes HAKeeper admission/readiness and therefore excludes fail-closed CNs. Direct SQL listeners are additionally protected by the local `DDLCommitGate`; suppressing proxy routing alone is insufficient because existing and direct sessions survive routing changes. Listener-ready and ingress-ready are separate lifecycle facts. + +## 9. Performance and capacity + +Let `N` be eligible CN count. + +- Activation performs O(N) membership validation and scans heartbeat-replicated phase/frontier state. It is an operator-triggered bounded transition, not a per-statement hot path. Target count is capped at 1024. +- DDL commit fan-out is O(N) RPCs and O(N) response ownership. DDL is low frequency relative to DML; no unbounded queue or background worker is introduced. +- Requests are bounded by existing discovery/RPC contexts. Target maps and response slices are released after each operation. +- Expected added DDL latency is the slowest required CN frontier application plus network fan-out. Rollout acceptance should record N-CN p50/p95 for representative 3-CN and larger staging clusters; no latency claim is made without that evidence. + +## 10. Observability and operations + +Required diagnostic state is available through protocol-version RPCs, CN heartbeat inventory, local deployed marker, HAKeeper cluster epoch, admission generation/address, barrier readiness, and ingress readiness. Activation errors identify missing targets, stale generation/address, unsupported HAKeeper replicas, membership drift, persistence failure, or frontier timeout. + +Follow-up metrics should count activation attempts/failures by phase and DDL fan-out latency/failures. Until those metrics land, logs and `mo_ctl` inventory are the operational source. + +## 11. Security and abuse bounds + +The protocol adds no tenant data exposure and uses internal QueryService/HAKeeper channels. Exact generation/address checks prevent stale incarnation control. The 1024-target cap and bounded contexts limit operator-triggered amplification. Existing authorization for `mo_ctl` remains the trust boundary. + +## 12. Alternatives + +### A. Periodic or statement-local sleeps + +Rejected: timing does not establish catalog visibility, is flaky, and adds unconditional latency. + +### B. Read HAKeeper epoch once at startup + +Rejected: a join can race the final commit after the read. Re-reading at ingress remains TOCTOU unless join and commit share one replicated ordering. + +### C. Always use latest compiled scalar protocol + +Rejected: compiled capability is not proof that every producer completed the distributed cut. + +### D. Separate DDL feature epoch from the shared MORPC scalar + +Architecturally clean and avoids scalar coupling, but still requires the same membership/ingress linearization and broader API migration. This revision retains MORPC v41 as the sender/receiver capability gate while storing deployment completion separately. + +### E. TN-only global catalog barrier on every new transaction + +Could provide a stronger generic read contract, but materially changes every transaction admission path and latency. The selected approach scopes cost to DDL and activation while preserving the required immediate cross-CN visibility. + +## 13. Validation matrix + +| Contract | Deterministic evidence | +|---|---| +| v40 baseline preserved | markerless default-v41 startup UT | +| post-cut downgrade rejected | completed-v41 downgrade UT | +| exact authoritative targets | ctl raw-membership omission/capability UT | +| join/commit atomicity | CNState RSM ordering UT: final scan, join, stale commit | +| HAKeeper failover compatibility | old capability view rejection UT | +| provisional/committed persistence | injected Replace failures plus distinct-service restart UT | +| startup and live frontier fencing | mock frontier/application ordering UT and race runs | +| automatic public behavior | two-CN embedded create on CN0 followed immediately by first CN1 read without `SYNCCOMMIT` | +| cancellation and publication uncertainty | bounded timeout, stale heartbeat, cleanup withdrawal UTs | + +## 14. Decision log and open items + +- Chosen linearization point: HAKeeper replicated heartbeat transition with exact tuple proof. +- Chosen baseline: preserve current-main v40 before v41 activation. +- Chosen downgrade behavior: reject after monotonic epoch commit. +- Chosen direct-ingress protection: local DDL gate in addition to proxy admission. +- Non-blocking follow-up: add dedicated activation/fan-out metrics and publish N-CN staging latency evidence. +- Design gate: revision 1 was explicitly approved on 2026-08-30; no blocking design item remains. diff --git a/pkg/clusterservice/cluster.go b/pkg/clusterservice/cluster.go index 2b19e767f2906..eb51088f3f448 100644 --- a/pkg/clusterservice/cluster.go +++ b/pkg/clusterservice/cluster.go @@ -655,6 +655,8 @@ func newCNService(cn logpb.CNStore) metadata.CNService { ViewMetadataAdmissionGeneration: cn.ViewMetadataAdmissionGeneration, ViewMetadataAdmissionReady: cn.ViewMetadataAdmissionReady, ViewMetadataObservedEpoch: cn.ViewMetadataObservedEpoch, + DDLVisibilityBarrierReady: cn.DDLVisibilityBarrierReady, + ViewMetadataIngressReady: cn.ViewMetadataIngressReady, } } diff --git a/pkg/clusterservice/cluster_test.go b/pkg/clusterservice/cluster_test.go index 49b488c0aa81c..36f1fd8772f22 100644 --- a/pkg/clusterservice/cluster_test.go +++ b/pkg/clusterservice/cluster_test.go @@ -169,6 +169,8 @@ func TestClusterAdmissionSnapshotFiltersEveryPublicInventory(t *testing.T) { UUID: "pending", WorkState: metadata.WorkState_Working, ViewMetadataAdmissionGeneration: 11, + DDLVisibilityBarrierReady: true, + ViewMetadataIngressReady: true, }, } hc.Unlock() @@ -189,12 +191,20 @@ func TestClusterAdmissionSnapshotFiltersEveryPublicInventory(t *testing.T) { require.Equal(t, []string{"ready"}, withoutWorkState) var raw []string + pendingBarrierReady := false + pendingIngressReady := false require.NoError(t, GetCNServiceRawWithContext( context.Background(), c, NewSelector(), func(service metadata.CNService) bool { raw = append(raw, service.ServiceID) + if service.ServiceID == "pending" { + pendingBarrierReady = service.DDLVisibilityBarrierReady + pendingIngressReady = service.ViewMetadataIngressReady + } return true })) require.ElementsMatch(t, []string{"ready", "pending"}, raw) + require.True(t, pendingBarrierReady) + require.True(t, pendingIngressReady) admission := c.GetViewMetadataAdmission() require.True(t, admission.Enabled) diff --git a/pkg/cnservice/server.go b/pkg/cnservice/server.go index 906249160b224..ca4a3d0e245da 100644 --- a/pkg/cnservice/server.go +++ b/pkg/cnservice/server.go @@ -155,15 +155,18 @@ func NewService( UUID: cfg.UUID, Role: metadata.MustParseCNRole(cfg.Role), }, - cfg: cfg, - logger: logutil.GetGlobalLogger().Named("cn-service"), - metadataFS: metadataFS, - etlFS: etlFS, - fileService: fileService, - sessionMgr: queryservice.NewSessionManager(), - addressMgr: address.NewAddressManager(cfg.ServiceHost, cfg.PortBase), - gossipNode: gossipNode, - } + cfg: cfg, + logger: logutil.GetGlobalLogger().Named("cn-service"), + metadataFS: metadataFS, + etlFS: etlFS, + fileService: fileService, + sessionMgr: queryservice.NewSessionManager(), + addressMgr: address.NewAddressManager(cfg.ServiceHost, cfg.PortBase), + gossipNode: gossipNode, + ddlCommitGate: frontend.NewDDLCommitGate(), + } + runtime.ServiceRuntime(cfg.UUID).SetGlobalVariables( + frontend.DDLCommitGateRuntimeKey, srv.ddlCommitGate) srv.colexecServer = colexec.NewServer(cfg.UUID) srv.requestHandler = func(ctx context.Context, @@ -463,6 +466,9 @@ func (s *service) Start() (err error) { if err = s.startUnlessViewMetadataGenerationRevoked(s.queryService.Start); err != nil { return err } + if err = s.prepareDDLVisibilityBarrier(); err != nil { + return err + } if err = s.startFrontendUnlessViewMetadataGenerationRevoked(); err != nil { return err } @@ -477,11 +483,9 @@ func (s *service) Start() (err error) { if err = s.checkViewMetadataGenerationRevoked(); err != nil { return err } - s.viewMetadataIngressReady.Store(true) - if err = s.checkViewMetadataGenerationRevoked(); err != nil { + if err = s.publishDDLVisibilityIngressAfterStart(); err != nil { return err } - s.notifyHeartbeat() if err = s.checkViewMetadataGenerationRevoked(); err != nil { return err @@ -508,9 +512,20 @@ func (s *service) closeService() error { defer logutil.LogClose(s.logger, "cnservice")() s.closeViewMetadataAdmission() + // Prevent an already-admitted protocol command from republishing the + // barrier after shutdown begins. Other QueryService methods remain live + // until authoritative withdrawal, preserving the stale-target retry path. + s.ddlVisibilityBarrierClosing.Store(true) + if s.ddlCommitGate != nil { + s.ddlCommitGate.Close() + } + // Stop periodic heartbeats before publishing the final false readiness. + // QueryService remains available until the authoritative inventory has + // observed the withdrawal, so healthy CNs cannot target a closed barrier. s.stopper.Stop() s.closeErr = closeCNServiceSteps( + s.withdrawDDLVisibilityBarrier, // Query commands can reach frontend, task, engine, lock, shard, // auto-increment, and transaction state. Stop and drain this remote // ingress before clearing any of those dependencies. diff --git a/pkg/cnservice/server_ddl_visibility.go b/pkg/cnservice/server_ddl_visibility.go new file mode 100644 index 0000000000000..4b7554d785413 --- /dev/null +++ b/pkg/cnservice/server_ddl_visibility.go @@ -0,0 +1,771 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cnservice + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/matrixorigin/matrixone/pkg/clusterservice" + "github.com/matrixorigin/matrixone/pkg/common/moerr" + moruntime "github.com/matrixorigin/matrixone/pkg/common/runtime" + "github.com/matrixorigin/matrixone/pkg/defines" + logservicepb "github.com/matrixorigin/matrixone/pkg/pb/logservice" + "github.com/matrixorigin/matrixone/pkg/pb/metadata" + "github.com/matrixorigin/matrixone/pkg/pb/timestamp" + "github.com/matrixorigin/matrixone/pkg/util/file" + "github.com/matrixorigin/matrixone/pkg/util/protoc" +) + +func ddlVisibilityBarrierSupported(serviceID string) bool { + value, ok := moruntime.ServiceRuntime(serviceID).GetGlobalVariables(moruntime.MOProtocolVersion) + version, valid := value.(int64) + return ok && valid && version >= defines.MORPCVersion41 +} + +// prepareDDLVisibilityBarrier publishes this CN only after QueryService is +// listening. With protocol v41 active, it then catches up to the largest +// frontier held by the already-published barrier participants before public +// SQL ingress can be admitted. +func (s *service) prepareDDLVisibilityBarrier() error { + // MORPCLatestVersion describes compiled capability, not deployment-wide + // activation. Restore the durable per-CN deployed protocol before deciding + // whether this restart can produce v41 DDL. A fresh upgraded process has no + // marker and preserves the already-deployed v40 baseline until the + // complete-target cut persists v41. + deployedVersion := s.loadDDLVisibilityDeployedProtocol() + if deployedVersion == 0 && s._hakeeperClient != nil { + ctx, cancel := s.newDDLVisibilityBarrierContext(context.Background()) + details, err := s._hakeeperClient.GetClusterDetails(ctx) + if err != nil { + err = moerr.AttachCause(ctx, err) + cancel() + return err + } + cancel() + if details.DDLVisibilityDeployedProtocol >= defines.MORPCVersion41 { + // Markerless scale-out/replacement after the cluster cut joins v41 as + // provisional. It can synchronize and receive activation RPCs, but it + // cannot publish ingress or produce DDL before joining the exact cut. + deployedVersion = -details.DDLVisibilityDeployedProtocol + } + } + rt := moruntime.ServiceRuntime(s.cfg.UUID) + if deployedVersion >= defines.MORPCVersion41 { + s.ddlVisibilityActivationComplete.Store(true) + rt.SetGlobalVariables(moruntime.MOProtocolVersion, deployedVersion) + } else if deployedVersion <= -defines.MORPCVersion41 { + // A provisional marker proves this CN durably fenced its local producers, + // but not that every target committed the same cut. Keep v41 capability so + // retries can converge while startup/public ingress remains fail-closed. + rt.SetGlobalVariables(moruntime.MOProtocolVersion, -deployedVersion) + } else if value, ok := rt.GetGlobalVariables(moruntime.MOProtocolVersion); ok { + if version, valid := value.(int64); valid && version >= defines.MORPCVersion41 { + rt.SetGlobalVariables(moruntime.MOProtocolVersion, defines.MORPCVersion40) + } + } + + s.ddlVisibilityBarrierMu.Lock() + defer s.ddlVisibilityBarrierMu.Unlock() + if err := s.prepareDDLVisibilityBarrierLocked(); err != nil { + return err + } + s.ddlVisibilityBarrierPrepared.Store(true) + return nil +} + +func (s *service) prepareDDLVisibilityBarrierLocked() error { + supported := ddlVisibilityBarrierSupported(s.cfg.UUID) + if supported && (s.moCluster == nil || s.queryClient == nil || s._txnClient == nil || + s._hakeeperClient == nil || s.config == nil) { + // Focused service tests may construct only the dependencies relevant to + // their lifecycle assertion. Production NewService initializes a non-zero + // admission generation together with all three barrier dependencies. + if s.viewMetadataAdmissionGeneration != 0 { + return moerr.NewInternalErrorNoCtx("DDL visibility barrier dependencies are unavailable") + } + } + + s.ddlVisibilityBarrierReady.Store(true) + if !supported || s.moCluster == nil || s.queryClient == nil || s._txnClient == nil { + s.notifyHeartbeat() + return nil + } + + timeout := s.cfg.HAKeeper.DiscoveryTimeout.Duration + if timeout <= 0 { + timeout = 30 * time.Second + } + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + // Startup owns this mutex, so relying on the periodic heartbeat (which uses + // the same mutex to order readiness snapshots) would deadlock publication. + // Publish the startup barrier directly before waiting for its authoritative + // observation; periodic heartbeats remain serialized behind startup. + if _, err := s._hakeeperClient.SendCNHeartbeat(ctx, s.newCNStoreHeartbeat()); err != nil { + return moerr.AttachCause(ctx, err) + } + + retryInterval := s.cfg.HAKeeper.HeatbeatInterval.Duration + if retryInterval < minClusterReadinessRetryInterval { + retryInterval = minClusterReadinessRetryInterval + } + if err := s.waitForDDLVisibilityBarrierPublication(ctx, retryInterval); err != nil { + return err + } + return s.syncStartupDDLVisibilityFrontier(ctx) +} + +// withdrawDDLVisibilityBarrier closes both externally published gates before +// QueryService is stopped. closeService invokes it only after the periodic +// heartbeat task has terminated, so no previously captured ready heartbeat can +// overwrite this final withdrawal. +func (s *service) publishDDLVisibilityIngressAfterStart() error { + // Serialize listener-ready publication with startup/activation/shutdown. + // Compiled v41 capability is not evidence that the deployment-wide producer + // cut completed: a default-v41 CN stays fail-closed until its complete-target + // activation succeeds. If activation raced ahead of listener startup, this + // method remains the sole owner that opens ingress after listeners are live. + s.ddlVisibilityBarrierMu.Lock() + defer s.ddlVisibilityBarrierMu.Unlock() + if err := s.checkViewMetadataGenerationRevoked(); err != nil { + return err + } + s.ddlVisibilityListenersReady.Store(true) + if ddlVisibilityBarrierSupported(s.cfg.UUID) && + !s.ddlVisibilityActivationComplete.Load() { + s.viewMetadataIngressReady.Store(false) + s.notifyHeartbeat() + return nil + } + if !ddlVisibilityBarrierSupported(s.cfg.UUID) && s._hakeeperClient != nil { + // Atomically ask HAKeeper to publish this markerless CN as ingress-ready. + // The RSM either records this join before the cut (so activation must + // include it), or rejects it after the committed epoch and returns that + // epoch in the same transition. A separate epoch read is a TOCTOU gap. + hb := s.newCNStoreHeartbeat() + hb.ViewMetadataIngressReady = true + ctx, cancel := s.newDDLVisibilityBarrierContext(context.Background()) + batch, err := s._hakeeperClient.SendCNHeartbeat(ctx, hb) + if err != nil { + err = moerr.AttachCause(ctx, err) + cancel() + return err + } + cancel() + if batch.DDLVisibilityDeployedProtocol >= defines.MORPCVersion41 { + moruntime.ServiceRuntime(s.cfg.UUID).SetGlobalVariables( + moruntime.MOProtocolVersion, batch.DDLVisibilityDeployedProtocol) + s.viewMetadataIngressReady.Store(false) + return nil + } + } + if s.ddlCommitGate != nil { + s.ddlCommitGate.EnablePublicDDL() + } + s.viewMetadataIngressReady.Store(true) + if err := s.checkViewMetadataGenerationRevoked(); err != nil { + return err + } + s.notifyHeartbeat() + return nil +} + +func (s *service) withdrawDDLVisibilityBarrier() error { + s.ddlVisibilityBarrierMu.Lock() + defer s.ddlVisibilityBarrierMu.Unlock() + ctx, cancel := s.newDDLVisibilityBarrierContext(context.Background()) + defer cancel() + return s.withdrawDDLVisibilityBarrierLocked(ctx) +} + +func (s *service) withdrawDDLVisibilityBarrierLocked(ctx context.Context) error { + s.ddlVisibilityHeartbeatMu.Lock() + defer s.ddlVisibilityHeartbeatMu.Unlock() + s.viewMetadataIngressReady.Store(false) + s.ddlVisibilityBarrierReady.Store(false) + if s.viewMetadataAdmissionGeneration == 0 { + return nil + } + if s.cfg == nil || s._hakeeperClient == nil || s.moCluster == nil || s.config == nil { + return moerr.NewInternalErrorNoCtx("DDL visibility barrier withdrawal dependencies are unavailable") + } + + if _, err := s._hakeeperClient.SendCNHeartbeat(ctx, s.newCNStoreHeartbeat()); err != nil { + return moerr.AttachCause(ctx, err) + } + return s.waitForDDLVisibilityBarrierWithdrawal(ctx, s.ddlVisibilityBarrierRetryInterval()) +} + +func (s *service) newDDLVisibilityBarrierContext(parent context.Context) (context.Context, context.CancelFunc) { + timeout := 30 * time.Second + if s.cfg != nil && s.cfg.HAKeeper.DiscoveryTimeout.Duration > 0 { + timeout = s.cfg.HAKeeper.DiscoveryTimeout.Duration + } + return context.WithTimeout(parent, timeout) +} + +func (s *service) ddlVisibilityBarrierRetryInterval() time.Duration { + retryInterval := time.Duration(0) + if s.cfg != nil { + retryInterval = s.cfg.HAKeeper.HeatbeatInterval.Duration + } + if retryInterval < minClusterReadinessRetryInterval { + retryInterval = minClusterReadinessRetryInterval + } + return retryInterval +} + +// setProtocolVersion serializes live activation with startup and shutdown. +// Every target CN blocks and drains local DDL before publishing Prepared. Once +// all targets are prepared, no v34 DDL producer exists; each CN applies the +// converged frontier and publishes Fenced. A CN releases its DDL gate only after +// every target is fenced, at which point all later DDL uses the v41 fan-out and +// every still-fencing receiver remains barrier-reachable. +func (s *service) setProtocolVersion(ctx context.Context, version int64, targets []string) error { + s.ddlVisibilityBarrierMu.Lock() + defer s.ddlVisibilityBarrierMu.Unlock() + + if s.cfg == nil { + return moerr.NewInternalError(ctx, "CN configuration is unavailable") + } + rt := moruntime.ServiceRuntime(s.cfg.UUID) + value, ok := rt.GetGlobalVariables(moruntime.MOProtocolVersion) + if !ok { + return moerr.NewInternalError(ctx, "protocol version not found") + } + current, ok := value.(int64) + if !ok { + return moerr.NewInternalError(ctx, "invalid protocol version") + } + pending := s.ddlVisibilityActivationPending.Load() + if version < defines.MORPCVersion41 { + if pending { + return moerr.NewInvalidStateNoCtx("cannot downgrade during DDL visibility activation") + } + deployed := s.loadDDLVisibilityDeployedProtocol() + clusterCommitted := s.ddlVisibilityActivationComplete.Load() || + deployed >= defines.MORPCVersion41 || deployed <= -defines.MORPCVersion41 + if !clusterCommitted && s._hakeeperClient != nil { + queryCtx, cancel := s.newDDLVisibilityBarrierContext(ctx) + details, err := s._hakeeperClient.GetClusterDetails(queryCtx) + if err != nil { + err = moerr.AttachCause(queryCtx, err) + cancel() + return err + } + cancel() + clusterCommitted = details.DDLVisibilityDeployedProtocol >= defines.MORPCVersion41 + } + if clusterCommitted { + return moerr.NewInvalidStateNoCtx( + "cannot downgrade after the DDL visibility cluster epoch is committed") + } + if err := s.persistDDLVisibilityDeployedProtocol(version); err != nil { + return err + } + rt.SetGlobalVariables(moruntime.MOProtocolVersion, version) + s.ddlVisibilityActivationPrepared.Store(false) + s.ddlVisibilityActivationFenced.Store(false) + s.ddlVisibilityActivationComplete.Store(false) + return nil + } + if !s.ddlVisibilityBarrierPrepared.Load() { + rt.SetGlobalVariables(moruntime.MOProtocolVersion, version) + return nil + } + if current >= defines.MORPCVersion41 && !pending && + s.ddlVisibilityActivationComplete.Load() { + rt.SetGlobalVariables(moruntime.MOProtocolVersion, version) + return nil + } + if s.ddlVisibilityBarrierClosing.Load() || s.viewMetadataGenerationRevoked.Load() { + return moerr.NewServiceUnavailableNoCtx("CN is closing") + } + if s.viewMetadataAdmissionGeneration == 0 || s._hakeeperClient == nil || + s.moCluster == nil || s.queryClient == nil || s._txnClient == nil || s.config == nil || + s.ddlCommitGate == nil { + return moerr.NewInternalError(ctx, "DDL visibility activation dependencies are unavailable") + } + activationTargets, err := validateDDLVisibilityActivationTargets(s.cfg.UUID, targets) + if err != nil { + return err + } + if err := s.requireDDLVisibilityEpochHAKeeperCapability(ctx); err != nil { + return err + } + + barrierCtx, cancel := s.newDDLVisibilityBarrierContext(ctx) + defer cancel() + if !pending { + // A default-v41 process deliberately keeps ingress closed until the + // complete-target cut. Restore ingress when listeners are already live; + // if activation races before listener startup, Start opens it afterward. + s.ddlVisibilityRestoreIngress.Store( + s.viewMetadataIngressReady.Load() || s.ddlVisibilityListenersReady.Load()) + } + s.ddlVisibilityActivationPending.Store(true) + s.ddlVisibilityActivationPrepared.Store(false) + s.ddlVisibilityActivationFenced.Store(false) + // Stop new old-protocol DDL admission before the fallible authoritative + // withdrawal. Both withdrawal and subsequent drain failures deliberately + // leave the gate blocked for a fail-closed retry. + if err := s.ddlCommitGate.BlockNew(); err != nil { + return err + } + if err := s.setDDLVisibilityIngressLocked(barrierCtx, false); err != nil { + return err + } + if err := s.ddlCommitGate.WaitDrained(barrierCtx); err != nil { + return err + } + + // Version 41 is visible only after the local pre-v41 producers are drained. It + // acts as the distributed Prepared signal queried by every other target. + rt.SetGlobalVariables(moruntime.MOProtocolVersion, version) + s.ddlVisibilityActivationPrepared.Store(true) + if err := s.publishDDLVisibilityActivationPhaseLocked(barrierCtx); err != nil { + return err + } + if err := s.waitForDDLVisibilityActivationPhase( + barrierCtx, activationTargets, false); err != nil { + return err + } + if err := s.syncStartupDDLVisibilityFrontier(barrierCtx); err != nil { + return err + } + // Fenced is a distributed proof. Publish it only after the local provisional + // state is durable, so a persistence failure can never be observed as Fence. + if err := s.persistDDLVisibilityDeployedProtocol(-version); err != nil { + return err + } + s.ddlVisibilityActivationFenced.Store(true) + if err := s.publishDDLVisibilityActivationPhaseLocked(barrierCtx); err != nil { + return err + } + if err := s.waitForDDLVisibilityActivationPhase( + barrierCtx, activationTargets, true); err != nil { + return err + } + if s.ddlVisibilityBarrierClosing.Load() || s.viewMetadataGenerationRevoked.Load() { + return moerr.NewServiceUnavailableNoCtx("CN is closing") + } + // Re-read authoritative membership after the final phase scan. A markerless + // CN may have joined after dispatch; committing without it would reopen an + // un-fenced legacy producer. The join either appears here and aborts this cut, + // or its atomic ingress heartbeat observes the already committed epoch. + commitTargets, err := s.revalidateDDLVisibilityActivationMembership(barrierCtx, activationTargets) + if err != nil { + return err + } + if err := s.commitDDLVisibilityClusterEpoch(barrierCtx, version, commitTargets); err != nil { + return err + } + // Commit the deployed cut before reopening ingress. A failure leaves the + // durable provisional marker in place; restart therefore remains on v41 but + // fail-closed until a complete-target retry commits the cut. + if err := s.persistDDLVisibilityDeployedProtocol(version); err != nil { + return err + } + if err := s.setDDLVisibilityIngressLocked( + barrierCtx, s.ddlVisibilityRestoreIngress.Load()); err != nil { + cleanupCtx, cleanupCancel := s.newDDLVisibilityBarrierContext(context.Background()) + cleanupErr := s.setDDLVisibilityIngressLocked(cleanupCtx, false) + cleanupCancel() + return errors.Join(err, cleanupErr) + } + + s.ddlVisibilityActivationComplete.Store(true) + s.ddlVisibilityActivationPending.Store(false) + if s.ddlVisibilityRestoreIngress.Load() { + s.ddlCommitGate.EnablePublicDDL() + } + s.ddlCommitGate.Unblock() + return nil +} + +func (s *service) loadDDLVisibilityDeployedProtocol() int64 { + return s.metadata.DDLVisibilityDeployedProtocol +} + +func (s *service) persistDDLVisibilityDeployedProtocol(version int64) error { + if s.metadataFS == nil { + // Focused service tests may omit the local metadata file service, but the + // process-local state must still model the committed heartbeat that follows. + s.metadata.DDLVisibilityDeployedProtocol = version + return nil + } + previous := s.metadata.DDLVisibilityDeployedProtocol + s.metadata.DDLVisibilityDeployedProtocol = version + if err := file.WriteFile(s.metadataFS, getMetadataFile(s.cfg.UUID), protoc.MustMarshal(&s.metadata)); err != nil { + s.metadata.DDLVisibilityDeployedProtocol = previous + return err + } + return nil +} + +func (s *service) revalidateDDLVisibilityActivationMembership( + ctx context.Context, + targets map[string]struct{}, +) ([]logservicepb.DDLVisibilityActivationTarget, error) { + details, err := s._hakeeperClient.GetClusterDetails(ctx) + if err != nil { + return nil, moerr.AttachCause(ctx, err) + } + commitTargets := make([]logservicepb.DDLVisibilityActivationTarget, 0, len(targets)) + seen := 0 + for _, cn := range details.CNStores { + if cn.QueryAddress == "" || cn.ViewMetadataAdmissionGeneration == 0 { + continue + } + seen++ + if _, ok := targets[cn.UUID]; !ok { + return nil, moerr.NewInvalidStateNoCtx(fmt.Sprintf( + "DDL visibility activation membership changed: authoritative CN %s is not fenced", cn.UUID)) + } + if !cn.DDLVisibilityBarrierReady { + return nil, moerr.NewInvalidStateNoCtx(fmt.Sprintf( + "authoritative CN %s does not support the DDL visibility activation receiver", cn.UUID)) + } + commitTargets = append(commitTargets, logservicepb.DDLVisibilityActivationTarget{ + ServiceID: cn.UUID, Generation: cn.ViewMetadataAdmissionGeneration, QueryAddress: cn.QueryAddress, + }) + } + if seen != len(targets) { + return nil, moerr.NewInvalidStateNoCtx(fmt.Sprintf( + "DDL visibility activation membership changed: targets=%d authoritative=%d", len(targets), seen)) + } + return commitTargets, nil +} + +func (s *service) commitDDLVisibilityClusterEpoch( + ctx context.Context, + version int64, + targets []logservicepb.DDLVisibilityActivationTarget, +) error { + hb := s.newCNStoreHeartbeat() + hb.DDLVisibilityDeployedProtocol = version + hb.DDLVisibilityEpochCommitTargets = append( + []logservicepb.DDLVisibilityActivationTarget(nil), targets...) + batch, err := s._hakeeperClient.SendCNHeartbeat(ctx, hb) + if err != nil { + return moerr.AttachCause(ctx, err) + } + if batch.DDLVisibilityDeployedProtocol < version { + return moerr.NewInvalidStateNoCtx( + "DDL visibility activation membership changed before atomic cluster epoch commit") + } + return nil +} + +func (s *service) requireDDLVisibilityEpochHAKeeperCapability(ctx context.Context) error { + queryCtx, cancel := s.newDDLVisibilityBarrierContext(ctx) + defer cancel() + details, err := s._hakeeperClient.GetClusterDetails(queryCtx) + if err != nil { + return moerr.AttachCause(queryCtx, err) + } + if len(details.LogStores) == 0 { + return moerr.NewInvalidStateNoCtx("DDL visibility activation requires HAKeeper capability inventory") + } + for _, store := range details.LogStores { + if !store.DDLVisibilityDeployedProtocolSupported { + return moerr.NewInvalidStateNoCtx(fmt.Sprintf( + "LogStore %s does not support the durable DDL visibility deployment epoch", store.UUID)) + } + } + return nil +} + +func validateDDLVisibilityActivationTargets(serviceID string, targets []string) (map[string]struct{}, error) { + result := make(map[string]struct{}, len(targets)) + for _, target := range targets { + if target == "" { + return nil, moerr.NewInternalErrorNoCtx("DDL visibility activation target is empty") + } + if _, exists := result[target]; exists { + return nil, moerr.NewInternalErrorNoCtxf( + "DDL visibility activation target %s is duplicated", target) + } + result[target] = struct{}{} + } + if _, ok := result[serviceID]; !ok { + return nil, moerr.NewInternalErrorNoCtxf( + "DDL visibility activation targets do not include local CN %s", serviceID) + } + return result, nil +} + +func (s *service) setDDLVisibilityIngressLocked(ctx context.Context, ready bool) error { + s.ddlVisibilityHeartbeatMu.Lock() + s.ddlVisibilityBarrierReady.Store(true) + s.viewMetadataIngressReady.Store(ready) + _, err := s._hakeeperClient.SendCNHeartbeat(ctx, s.newCNStoreHeartbeat()) + s.ddlVisibilityHeartbeatMu.Unlock() + if err != nil { + return moerr.AttachCause(ctx, err) + } + return s.waitForDDLVisibilityIngress( + ctx, s.ddlVisibilityBarrierRetryInterval(), ready) +} + +func (s *service) waitForDDLVisibilityIngress( + ctx context.Context, + retryInterval time.Duration, + ready bool, +) error { + refresher, ok := s.moCluster.(clusterservice.AuthoritativeRefresher) + if !ok { + return moerr.NewInternalErrorNoCtx( + "CN cluster service does not support authoritative DDL visibility refresh") + } + for { + if err := refresher.Refresh(ctx); err == nil { + matched := false + err = clusterservice.GetCNServiceRawWithContext( + ctx, + s.moCluster, + clusterservice.NewServiceIDSelector(s.cfg.UUID), + func(cn metadata.CNService) bool { + matched = cn.ViewMetadataAdmissionGeneration == s.viewMetadataAdmissionGeneration && + cn.DDLVisibilityBarrierReady && cn.ViewMetadataIngressReady == ready + return false + }) + if err != nil { + return err + } + if matched { + return nil + } + } + if err := waitDDLVisibilityRetry(ctx, retryInterval); err != nil { + return moerr.NewInternalErrorf( + context.Background(), + "CN %s DDL visibility ingress=%t was not published before deadline: %v", + s.cfg.UUID, + ready, + err) + } + } +} + +func (s *service) publishDDLVisibilityActivationPhaseLocked(ctx context.Context) error { + s.ddlVisibilityHeartbeatMu.Lock() + defer s.ddlVisibilityHeartbeatMu.Unlock() + _, err := s._hakeeperClient.SendCNHeartbeat(ctx, s.newCNStoreHeartbeat()) + if err != nil { + return moerr.AttachCause(ctx, err) + } + return nil +} + +func (s *service) waitForDDLVisibilityActivationPhase( + ctx context.Context, + targets map[string]struct{}, + requireFenced bool, +) error { + if s._hakeeperClient == nil { + return moerr.NewInternalErrorNoCtx("HAKeeper client is unavailable during DDL visibility activation") + } + var lastErr error + for { + allReady := true + seen := make(map[string]struct{}, len(targets)) + details, err := s._hakeeperClient.GetClusterDetails(ctx) + if err != nil { + lastErr = err + allReady = false + } else { + for _, cn := range details.CNStores { + _, expected := targets[cn.UUID] + if cn.QueryAddress == "" || cn.ViewMetadataAdmissionGeneration == 0 { + if expected { + return moerr.NewInvalidStateNoCtxf( + "DDL visibility activation target %s has no authoritative identity", cn.UUID) + } + continue + } + if !expected { + return moerr.NewInvalidStateNoCtxf( + "DDL visibility activation target set omits authoritative CN %s", cn.UUID) + } + seen[cn.UUID] = struct{}{} + if !cn.DDLVisibilityActivationPrepared || + (requireFenced && !cn.DDLVisibilityActivationFenced) { + allReady = false + } + } + } + if len(seen) != len(targets) { + allReady = false + } + if allReady { + return nil + } + if err := waitDDLVisibilityRetry(ctx, s.ddlVisibilityBarrierRetryInterval()); err != nil { + return moerr.NewInternalErrorf( + context.Background(), + "DDL visibility activation fenced=%t did not converge before deadline: %v (last error: %v)", + requireFenced, + err, + lastErr) + } + } +} + +func waitDDLVisibilityRetry(ctx context.Context, retryInterval time.Duration) error { + timer := time.NewTimer(retryInterval) + defer timer.Stop() + select { + case <-ctx.Done(): + return ctx.Err() + case <-timer.C: + return nil + } +} + +func (s *service) waitForDDLVisibilityBarrierWithdrawal( + ctx context.Context, + retryInterval time.Duration, +) error { + refresher, ok := s.moCluster.(clusterservice.AuthoritativeRefresher) + if !ok { + return moerr.NewInternalErrorNoCtx( + "CN cluster service does not support authoritative DDL visibility refresh") + } + + for { + if err := refresher.Refresh(ctx); err == nil { + withdrawn := true + err = clusterservice.GetCNServiceRawWithContext( + ctx, + s.moCluster, + clusterservice.NewServiceIDSelector(s.cfg.UUID), + func(cn metadata.CNService) bool { + if cn.ViewMetadataAdmissionGeneration < s.viewMetadataAdmissionGeneration || + (cn.ViewMetadataAdmissionGeneration == s.viewMetadataAdmissionGeneration && + cn.DDLVisibilityBarrierReady) { + withdrawn = false + } + return false + }) + if err != nil { + return err + } + if withdrawn { + return nil + } + } + + timer := time.NewTimer(retryInterval) + select { + case <-ctx.Done(): + if !timer.Stop() { + select { + case <-timer.C: + default: + } + } + return moerr.NewInternalErrorf( + context.Background(), + "CN %s DDL visibility barrier was not withdrawn before shutdown deadline: %v", + s.cfg.UUID, + ctx.Err()) + case <-timer.C: + } + } +} + +func (s *service) waitForDDLVisibilityBarrierPublication( + ctx context.Context, + retryInterval time.Duration, +) error { + refresher, ok := s.moCluster.(clusterservice.AuthoritativeRefresher) + if !ok { + return moerr.NewInternalErrorNoCtx( + "CN cluster service does not support authoritative DDL visibility refresh") + } + + for { + if err := refresher.Refresh(ctx); err == nil { + published := false + err = clusterservice.GetCNServiceRawWithContext( + ctx, + s.moCluster, + clusterservice.NewServiceIDSelector(s.cfg.UUID), + func(cn metadata.CNService) bool { + published = cn.ViewMetadataAdmissionGeneration == s.viewMetadataAdmissionGeneration && + cn.DDLVisibilityBarrierReady + return false + }) + if err != nil { + return err + } + if published { + return nil + } + } + + timer := time.NewTimer(retryInterval) + select { + case <-ctx.Done(): + if !timer.Stop() { + select { + case <-timer.C: + default: + } + } + return moerr.NewInternalErrorf( + context.Background(), + "CN %s DDL visibility barrier was not published before startup deadline: %v", + s.cfg.UUID, + ctx.Err()) + case <-timer.C: + } + } +} + +func (s *service) syncStartupDDLVisibilityFrontier(ctx context.Context) error { + if s._hakeeperClient == nil { + return moerr.NewInternalErrorNoCtx("HAKeeper client is unavailable during DDL visibility frontier sync") + } + details, err := s._hakeeperClient.GetClusterDetails(ctx) + if err != nil { + return moerr.AttachCause(ctx, err) + } + maxTS := timestamp.Timestamp{} + for _, cn := range details.CNStores { + if s.cfg != nil && cn.UUID == s.cfg.UUID { + // The drained producer already observes its own committed DDL. Waiting + // only on remote frontiers avoids depending on the control RPC that is + // currently executing on this CN. + continue + } + if cn.DDLVisibilityBarrierReady && maxTS.Less(cn.DDLVisibilityFrontier) { + maxTS = cn.DDLVisibilityFrontier + } + } + if maxTS.IsEmpty() { + return nil + } + if _, err := s._txnClient.WaitLogTailAppliedAt(ctx, maxTS); err != nil { + return err + } + s._txnClient.SyncLatestCommitTS(maxTS) + return nil +} diff --git a/pkg/cnservice/server_ddl_visibility_test.go b/pkg/cnservice/server_ddl_visibility_test.go new file mode 100644 index 0000000000000..b4dcf64ae4c7e --- /dev/null +++ b/pkg/cnservice/server_ddl_visibility_test.go @@ -0,0 +1,1585 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cnservice + +import ( + "context" + "errors" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + + "github.com/matrixorigin/matrixone/pkg/clusterservice" + "github.com/matrixorigin/matrixone/pkg/common/morpc" + moruntime "github.com/matrixorigin/matrixone/pkg/common/runtime" + "github.com/matrixorigin/matrixone/pkg/common/stopper" + "github.com/matrixorigin/matrixone/pkg/defines" + "github.com/matrixorigin/matrixone/pkg/fileservice" + "github.com/matrixorigin/matrixone/pkg/frontend" + mock_frontend "github.com/matrixorigin/matrixone/pkg/frontend/test" + "github.com/matrixorigin/matrixone/pkg/gossip" + "github.com/matrixorigin/matrixone/pkg/lockservice" + "github.com/matrixorigin/matrixone/pkg/logservice" + logservicepb "github.com/matrixorigin/matrixone/pkg/pb/logservice" + "github.com/matrixorigin/matrixone/pkg/pb/metadata" + "github.com/matrixorigin/matrixone/pkg/pb/query" + "github.com/matrixorigin/matrixone/pkg/pb/timestamp" + "github.com/matrixorigin/matrixone/pkg/util" +) + +type failReplaceMetadataFS struct { + fileservice.ReplaceableFileService + failAt int + calls int +} + +func (f *failReplaceMetadataFS) Replace(ctx context.Context, vector fileservice.IOVector) error { + f.calls++ + if f.calls == f.failAt { + return errors.New("injected metadata replace failure") + } + return f.ReplaceableFileService.Replace(ctx, vector) +} + +func newDDLVisibilityMetadataFS(t *testing.T) fileservice.ReplaceableFileService { + t.Helper() + fs, err := fileservice.NewMemoryFS(defines.LocalFileServiceName, fileservice.DisabledCacheConfig, nil) + require.NoError(t, err) + return fs +} + +type ddlVisibilityTestCluster struct { + cnServices []metadata.CNService + refreshCalls int + refreshHook func() + phaseMu sync.Mutex + phases map[string]query.GetProtocolVersionResponse + frontiers map[string]timestamp.Timestamp + phaseHook func(string) query.GetProtocolVersionResponse +} + +func (c *ddlVisibilityTestCluster) GetCNService( + selector clusterservice.Selector, + apply func(metadata.CNService) bool, +) { + c.forEachCN(selector, apply) +} + +func (*ddlVisibilityTestCluster) GetTNService( + clusterservice.Selector, + func(metadata.TNService) bool, +) { +} + +func (*ddlVisibilityTestCluster) GetAllTNServices() []metadata.TNService { return nil } + +func (c *ddlVisibilityTestCluster) GetCNServiceWithoutWorkingState( + selector clusterservice.Selector, + apply func(metadata.CNService) bool, +) { + c.forEachCN(selector, apply) +} + +func (c *ddlVisibilityTestCluster) forEachCN( + selector clusterservice.Selector, + apply func(metadata.CNService) bool, +) { + for _, cn := range c.cnServices { + if selector.MatchCN(cn) && !apply(cn) { + return + } + } +} + +func (*ddlVisibilityTestCluster) ForceRefresh(bool) {} +func (c *ddlVisibilityTestCluster) Refresh(context.Context) error { + c.refreshCalls++ + if c.refreshHook != nil { + c.refreshHook() + } + return nil +} +func (*ddlVisibilityTestCluster) Close() {} +func (*ddlVisibilityTestCluster) DebugUpdateCNLabel(string, map[string][]string) error { + return nil +} +func (*ddlVisibilityTestCluster) DebugUpdateCNWorkState(string, int) error { return nil } +func (*ddlVisibilityTestCluster) RemoveCN(string) {} +func (*ddlVisibilityTestCluster) AddCN(metadata.CNService) {} +func (*ddlVisibilityTestCluster) UpdateCN(metadata.CNService) {} + +type ddlVisibilityTestQueryClient struct { + serviceID string + frontiers map[string]timestamp.Timestamp + protocols map[string]query.GetProtocolVersionResponse + protocolFn func(string) query.GetProtocolVersionResponse + nilProtocol map[string]bool + requests []string + methods []query.CmdMethod + releases int +} + +func (c *ddlVisibilityTestQueryClient) ServiceID() string { return c.serviceID } +func (c *ddlVisibilityTestQueryClient) SendMessage( + _ context.Context, + address string, + req *query.Request, +) (*query.Response, error) { + c.requests = append(c.requests, address) + c.methods = append(c.methods, req.CmdMethod) + if req.CmdMethod == query.CmdMethod_GetProtocolVersion { + if req.GetProtocolVersion == nil { + return nil, errors.New("missing GetProtocolVersion request payload") + } + if c.nilProtocol[address] { + return &query.Response{}, nil + } + protocol := c.protocols[address] + if c.protocolFn != nil { + protocol = c.protocolFn(address) + } + return &query.Response{GetProtocolVersion: &protocol}, nil + } + return &query.Response{GetCommit: &query.GetCommitResponse{ + CurrentCommitTS: c.frontiers[address], + }}, nil +} +func (*ddlVisibilityTestQueryClient) NewRequest(method query.CmdMethod) *query.Request { + return &query.Request{CmdMethod: method} +} +func (c *ddlVisibilityTestQueryClient) Release(*query.Response) { c.releases++ } +func (*ddlVisibilityTestQueryClient) Close() error { return nil } + +func activationTestPeerProtocols() map[string]query.GetProtocolVersionResponse { + return map[string]query.GetProtocolVersionResponse{ + "peer:6001": { + Version: defines.MORPCVersion41, + DDLVisibilityActivationPrepared: true, + DDLVisibilityActivationFenced: true, + }, + } +} + +type ddlVisibilityWithdrawalHAKeeperClient struct { + logservice.CNHAKeeperClient + cluster *ddlVisibilityTestCluster + queryClosed <-chan struct{} + sendErr error + sendErrors map[int]error + heartbeats []logservicepb.CNStoreHeartbeat + queryClosedBeforeSend bool + closeCalls int + clusterDeployedProtocol int64 + oldHAKeeperReplica bool +} + +func (c *ddlVisibilityWithdrawalHAKeeperClient) GetClusterDetails( + context.Context, +) (logservicepb.ClusterDetails, error) { + details := logservicepb.ClusterDetails{ + DDLVisibilityDeployedProtocol: c.clusterDeployedProtocol, + LogStores: []logservicepb.LogStore{{ + UUID: "log-1", DDLVisibilityDeployedProtocolSupported: !c.oldHAKeeperReplica, + }}, + } + if c.cluster != nil { + c.cluster.phaseMu.Lock() + defer c.cluster.phaseMu.Unlock() + for _, cn := range c.cluster.cnServices { + phase, ok := c.cluster.phases[cn.ServiceID] + if c.cluster.phaseHook != nil && len(c.cluster.phases) > 0 { + phase = c.cluster.phaseHook(cn.QueryAddress) + ok = true + } + if !ok { + phase.DDLVisibilityActivationPrepared = cn.DDLVisibilityBarrierReady + phase.DDLVisibilityActivationFenced = cn.DDLVisibilityBarrierReady + } + details.CNStores = append(details.CNStores, logservicepb.CNStore{ + UUID: cn.ServiceID, QueryAddress: cn.QueryAddress, + ViewMetadataAdmissionGeneration: cn.ViewMetadataAdmissionGeneration, + DDLVisibilityBarrierReady: cn.DDLVisibilityBarrierReady, + DDLVisibilityActivationPrepared: phase.DDLVisibilityActivationPrepared, + DDLVisibilityActivationFenced: phase.DDLVisibilityActivationFenced, + DDLVisibilityFrontier: c.cluster.frontiers[cn.ServiceID], + }) + } + } + return details, nil +} + +func (c *ddlVisibilityWithdrawalHAKeeperClient) SendCNHeartbeat( + _ context.Context, + hb logservicepb.CNStoreHeartbeat, +) (logservicepb.CommandBatch, error) { + select { + case <-c.queryClosed: + c.queryClosedBeforeSend = true + default: + } + c.heartbeats = append(c.heartbeats, hb) + if err := c.sendErrors[len(c.heartbeats)]; err != nil { + return logservicepb.CommandBatch{}, err + } + if c.sendErr != nil { + return logservicepb.CommandBatch{}, c.sendErr + } + if c.cluster != nil { + c.cluster.phaseMu.Lock() + if c.cluster.phases == nil { + c.cluster.phases = make(map[string]query.GetProtocolVersionResponse) + } + c.cluster.phases[hb.UUID] = query.GetProtocolVersionResponse{ + Version: hb.DDLVisibilityDeployedProtocol, + DDLVisibilityActivationPrepared: hb.DDLVisibilityActivationPrepared, + DDLVisibilityActivationFenced: hb.DDLVisibilityActivationFenced, + } + if c.cluster.frontiers == nil { + c.cluster.frontiers = make(map[string]timestamp.Timestamp) + } + c.cluster.frontiers[hb.UUID] = hb.DDLVisibilityFrontier + c.cluster.phaseMu.Unlock() + } + for i := range c.cluster.cnServices { + cn := &c.cluster.cnServices[i] + if cn.ServiceID == hb.UUID { + cn.ViewMetadataAdmissionGeneration = hb.ViewMetadataAdmissionGeneration + cn.DDLVisibilityBarrierReady = hb.DDLVisibilityBarrierReady + cn.ViewMetadataIngressReady = hb.ViewMetadataIngressReady + if c.clusterDeployedProtocol >= defines.MORPCVersion41 && + hb.DDLVisibilityDeployedProtocol < c.clusterDeployedProtocol { + cn.ViewMetadataIngressReady = false + } + } + } + if len(hb.DDLVisibilityEpochCommitTargets) > 0 { + targets := make(map[string]logservicepb.DDLVisibilityActivationTarget, + len(hb.DDLVisibilityEpochCommitTargets)) + for _, target := range hb.DDLVisibilityEpochCommitTargets { + targets[target.ServiceID] = target + } + matched := 0 + valid := len(targets) == len(hb.DDLVisibilityEpochCommitTargets) + for _, cn := range c.cluster.cnServices { + if cn.QueryAddress == "" || cn.ViewMetadataAdmissionGeneration == 0 { + continue + } + matched++ + target, ok := targets[cn.ServiceID] + valid = valid && ok && target.Generation == cn.ViewMetadataAdmissionGeneration && + target.QueryAddress == cn.QueryAddress && cn.DDLVisibilityBarrierReady + } + if valid && matched == len(targets) { + c.clusterDeployedProtocol = hb.DDLVisibilityDeployedProtocol + } + } + return logservicepb.CommandBatch{ + DDLVisibilityDeployedProtocol: c.clusterDeployedProtocol, + }, nil +} + +func (c *ddlVisibilityWithdrawalHAKeeperClient) Close() error { + c.closeCalls++ + return nil +} + +type ddlVisibilityHeartbeatOrderClient struct { + logservice.CNHAKeeperClient + mu sync.Mutex + calls int + firstStarted chan struct{} + releaseFirst chan struct{} + completed []logservicepb.CNStoreHeartbeat +} + +func (c *ddlVisibilityHeartbeatOrderClient) SendCNHeartbeat( + _ context.Context, + hb logservicepb.CNStoreHeartbeat, +) (logservicepb.CommandBatch, error) { + c.mu.Lock() + c.calls++ + call := c.calls + c.mu.Unlock() + if call == 1 { + close(c.firstStarted) + <-c.releaseFirst + } + c.mu.Lock() + c.completed = append(c.completed, hb) + c.mu.Unlock() + return logservicepb.CommandBatch{}, errors.New("stop heartbeat after ordering observation") +} + +type ddlVisibilityCloseLockService struct { + lockservice.LockService + closeCalls int +} + +func (s *ddlVisibilityCloseLockService) Close() error { + s.closeCalls++ + return nil +} + +type ddlVisibilityCloseTestState struct { + service *service + hakeeperClient *ddlVisibilityWithdrawalHAKeeperClient + queryService *closeRecordingQueryService + lockService *ddlVisibilityCloseLockService + cluster *ddlVisibilityTestCluster + queryClosedBeforeRefresh bool +} + +func newDDLVisibilityCloseTestService(t *testing.T, sendErr error) *ddlVisibilityCloseTestState { + t.Helper() + const generation = uint64(7) + moruntime.SetupServiceBasedRuntime(t.Name(), moruntime.DefaultRuntime()) + gossipNode, err := gossip.NewNode(context.Background(), t.Name()) + require.NoError(t, err) + cfg := &Config{UUID: t.Name()} + cfg.HAKeeper.DiscoveryTimeout.Duration = time.Second + cfg.HAKeeper.HeatbeatInterval.Duration = time.Millisecond + state := &ddlVisibilityCloseTestState{ + queryService: &closeRecordingQueryService{closed: make(chan struct{})}, + lockService: &ddlVisibilityCloseLockService{}, + cluster: &ddlVisibilityTestCluster{cnServices: []metadata.CNService{{ + ServiceID: t.Name(), QueryAddress: "self:6001", + ViewMetadataAdmissionGeneration: generation, DDLVisibilityBarrierReady: true, + }}}, + } + state.cluster.refreshHook = func() { + select { + case <-state.queryService.closed: + state.queryClosedBeforeRefresh = true + default: + } + } + state.hakeeperClient = &ddlVisibilityWithdrawalHAKeeperClient{ + cluster: state.cluster, queryClosed: state.queryService.closed, sendErr: sendErr, + } + state.service = &service{ + cfg: cfg, + logger: zap.NewNop(), + stopper: stopper.NewStopper("ddl-visibility-close-test"), + _hakeeperClient: state.hakeeperClient, + moCluster: state.cluster, + queryService: state.queryService, + mo: closeErrorMOServer{}, + cancelMoServerFunc: func() {}, + server: closeOnlyRPCServer{}, + lockService: state.lockService, + gossipNode: gossipNode, + config: util.NewConfigData(nil), + viewMetadataAdmissionGeneration: generation, + ddlCommitGate: frontend.NewDDLCommitGate(), + } + state.service.viewMetadataIngressReady.Store(true) + state.service.ddlVisibilityBarrierReady.Store(true) + return state +} + +func TestPrepareDDLVisibilityBarrier(t *testing.T) { + const serviceID = "ddl-visibility-startup-test" + moruntime.SetupServiceBasedRuntime(serviceID, moruntime.DefaultRuntime()) + targetTS := timestamp.Timestamp{PhysicalTime: 200, LogicalTime: 3} + cluster := &ddlVisibilityTestCluster{cnServices: []metadata.CNService{ + { + ServiceID: serviceID, QueryAddress: "self:6001", + ViewMetadataAdmissionGeneration: 7, DDLVisibilityBarrierReady: true, + }, + { + ServiceID: "peer", QueryAddress: "peer:6001", + ViewMetadataAdmissionGeneration: 9, DDLVisibilityBarrierReady: true, + }, + }, frontiers: map[string]timestamp.Timestamp{"peer": targetTS}} + queryClient := &ddlVisibilityTestQueryClient{ + serviceID: serviceID, + frontiers: map[string]timestamp.Timestamp{ + "self:6001": {PhysicalTime: 100}, + "peer:6001": targetTS, + }, + } + ctrl := gomock.NewController(t) + txnClient := mock_frontend.NewMockTxnClient(ctrl) + txnClient.EXPECT().WaitLogTailAppliedAt(gomock.Any(), targetTS).Return(targetTS.Next(), nil) + txnClient.EXPECT().SyncLatestCommitTS(targetTS) + + cfg := &Config{UUID: serviceID} + cfg.HAKeeper.DiscoveryTimeout.Duration = time.Second + metadataFS := newDDLVisibilityMetadataFS(t) + writer := &service{ + cfg: cfg, metadata: metadata.CNStore{UUID: serviceID}, metadataFS: metadataFS, + } + // Persist with the old process, then construct a distinct service and load + // through the production metadata initialization path. + require.NoError(t, writer.persistDDLVisibilityDeployedProtocol(defines.MORPCVersion41)) + s := &service{ + cfg: cfg, metadata: metadata.CNStore{UUID: serviceID}, metadataFS: metadataFS, + logger: zap.NewNop(), + moCluster: cluster, queryClient: queryClient, _txnClient: txnClient, + _hakeeperClient: &ddlVisibilityWithdrawalHAKeeperClient{cluster: cluster}, + config: util.NewConfigData(nil), + viewMetadataAdmissionGeneration: 7, + ddlCommitGate: frontend.NewDDLCommitGate(), + } + require.NoError(t, s.initMetadata()) + require.Equal(t, defines.MORPCVersion41, s.metadata.DDLVisibilityDeployedProtocol) + require.False(t, s.ddlVisibilityActivationComplete.Load()) + require.NoError(t, s.prepareDDLVisibilityBarrier()) + require.True(t, s.ddlVisibilityBarrierPrepared.Load()) + require.False(t, s.ddlVisibilityActivationPrepared.Load()) + require.False(t, s.ddlVisibilityActivationFenced.Load()) + require.True(t, s.ddlVisibilityActivationComplete.Load()) + require.True(t, s.ddlVisibilityBarrierReady.Load()) + require.Equal(t, 1, cluster.refreshCalls) + require.Empty(t, queryClient.requests) + require.Empty(t, queryClient.methods) + require.Zero(t, queryClient.releases) + require.NoError(t, s.publishDDLVisibilityIngressAfterStart()) + version, ok := moruntime.ServiceRuntime(serviceID).GetGlobalVariables(moruntime.MOProtocolVersion) + require.True(t, ok) + require.Equal(t, defines.MORPCVersion41, version) + require.True(t, s.viewMetadataIngressReady.Load()) + require.True(t, s.ddlCommitGate.PublicDDLEnabled()) +} + +func TestDefaultV41StartupUsesLastDeployedProtocolUntilActivation(t *testing.T) { + const serviceID = "ddl-visibility-default-v41-startup-protocol-test" + moruntime.SetupServiceBasedRuntime(serviceID, moruntime.DefaultRuntime()) + gate := frontend.NewDDLCommitGate() + s := &service{cfg: &Config{UUID: serviceID}, ddlCommitGate: gate} + + require.NoError(t, s.prepareDDLVisibilityBarrier()) + require.NoError(t, s.publishDDLVisibilityIngressAfterStart()) + version, ok := moruntime.ServiceRuntime(serviceID).GetGlobalVariables(moruntime.MOProtocolVersion) + require.True(t, ok) + require.Equal(t, defines.MORPCVersion40, version) + require.True(t, s.ddlVisibilityListenersReady.Load()) + require.True(t, s.viewMetadataIngressReady.Load()) + require.True(t, gate.PublicDDLEnabled()) + require.False(t, s.ddlVisibilityActivationPrepared.Load()) + require.False(t, s.ddlVisibilityActivationFenced.Load()) +} + +func TestDDLVisibilityEpochHAKeeperCapabilityRejectsOldLeaderView(t *testing.T) { + client := &ddlVisibilityWithdrawalHAKeeperClient{} + s := &service{ + cfg: &Config{}, _hakeeperClient: client, + } + s.cfg.HAKeeper.DiscoveryTimeout.Duration = time.Second + require.NoError(t, s.requireDDLVisibilityEpochHAKeeperCapability(context.Background())) + + // A leader failover to an old RSM returns the additive field as false. The + // activation entry point must interpret that as unsupported, never epoch 0. + client.oldHAKeeperReplica = true + err := s.requireDDLVisibilityEpochHAKeeperCapability(context.Background()) + require.ErrorContains(t, err, "does not support the durable DDL visibility deployment epoch") +} + +func TestActivationFinalMembershipScanRejectsConcurrentJoin(t *testing.T) { + cluster := &ddlVisibilityTestCluster{cnServices: []metadata.CNService{ + {ServiceID: "target-cn", QueryAddress: "target:6001", ViewMetadataAdmissionGeneration: 1, + DDLVisibilityBarrierReady: true}, + {ServiceID: "joining-cn", QueryAddress: "joining:6001", ViewMetadataAdmissionGeneration: 2, + DDLVisibilityBarrierReady: true}, + }} + s := &service{_hakeeperClient: &ddlVisibilityWithdrawalHAKeeperClient{cluster: cluster}} + _, err := s.revalidateDDLVisibilityActivationMembership( + context.Background(), map[string]struct{}{"target-cn": {}}) + require.ErrorContains(t, err, "authoritative CN joining-cn is not fenced") +} + +func TestMarkerlessCNIngressHandshakeLinearizesWithCommittedCut(t *testing.T) { + const serviceID = "ddl-visibility-markerless-join-race-test" + moruntime.SetupServiceBasedRuntime(serviceID, moruntime.DefaultRuntime()) + cluster := &ddlVisibilityTestCluster{cnServices: []metadata.CNService{{ServiceID: serviceID}}} + client := &ddlVisibilityWithdrawalHAKeeperClient{cluster: cluster} + gate := frontend.NewDDLCommitGate() + s := &service{ + cfg: &Config{UUID: serviceID}, ddlCommitGate: gate, + _hakeeperClient: client, config: util.NewConfigData(nil), + } + + // The startup read precedes the cut, but the atomic ingress heartbeat lands + // after it. HAKeeper must reject ingress and return the committed epoch. + require.NoError(t, s.prepareDDLVisibilityBarrier()) + client.clusterDeployedProtocol = defines.MORPCVersion41 + require.NoError(t, s.publishDDLVisibilityIngressAfterStart()) + version, ok := moruntime.ServiceRuntime(serviceID).GetGlobalVariables(moruntime.MOProtocolVersion) + require.True(t, ok) + require.Equal(t, defines.MORPCVersion41, version) + require.False(t, s.viewMetadataIngressReady.Load()) + require.False(t, cluster.cnServices[0].ViewMetadataIngressReady) + require.False(t, gate.PublicDDLEnabled()) +} + +func TestMarkerlessCNJoinsCommittedClusterFailClosed(t *testing.T) { + const serviceID = "ddl-visibility-markerless-post-cut-test" + moruntime.SetupServiceBasedRuntime(serviceID, moruntime.DefaultRuntime()) + cluster := &ddlVisibilityTestCluster{} + gate := frontend.NewDDLCommitGate() + s := &service{ + cfg: &Config{UUID: serviceID}, ddlCommitGate: gate, config: util.NewConfigData(nil), + _hakeeperClient: &ddlVisibilityWithdrawalHAKeeperClient{ + cluster: cluster, clusterDeployedProtocol: defines.MORPCVersion41, + }, + } + + require.NoError(t, s.prepareDDLVisibilityBarrier()) + require.NoError(t, s.publishDDLVisibilityIngressAfterStart()) + version, ok := moruntime.ServiceRuntime(serviceID).GetGlobalVariables(moruntime.MOProtocolVersion) + require.True(t, ok) + require.Equal(t, defines.MORPCVersion41, version) + require.False(t, s.ddlVisibilityActivationComplete.Load()) + require.False(t, s.viewMetadataIngressReady.Load()) + require.False(t, gate.PublicDDLEnabled()) +} + +func TestProvisionalV41RestartRemainsFailClosed(t *testing.T) { + const serviceID = "ddl-visibility-provisional-v41-restart-test" + moruntime.SetupServiceBasedRuntime(serviceID, moruntime.DefaultRuntime()) + metadataFS := newDDLVisibilityMetadataFS(t) + cfg := &Config{UUID: serviceID} + writer := &service{cfg: cfg, metadata: metadata.CNStore{UUID: serviceID}, metadataFS: metadataFS} + require.NoError(t, writer.persistDDLVisibilityDeployedProtocol(-defines.MORPCVersion41)) + + gate := frontend.NewDDLCommitGate() + restarted := &service{ + cfg: cfg, metadata: metadata.CNStore{UUID: serviceID}, metadataFS: metadataFS, + logger: zap.NewNop(), ddlCommitGate: gate, + } + require.NoError(t, restarted.initMetadata()) + require.NoError(t, restarted.prepareDDLVisibilityBarrier()) + require.NoError(t, restarted.publishDDLVisibilityIngressAfterStart()) + version, ok := moruntime.ServiceRuntime(serviceID).GetGlobalVariables(moruntime.MOProtocolVersion) + require.True(t, ok) + require.Equal(t, defines.MORPCVersion41, version) + require.False(t, restarted.ddlVisibilityActivationComplete.Load()) + require.False(t, restarted.viewMetadataIngressReady.Load()) + require.False(t, gate.PublicDDLEnabled()) +} + +func TestPrepareDDLVisibilityBarrierRejectsMissingProductionDependencies(t *testing.T) { + const serviceID = "ddl-visibility-missing-dependency-test" + moruntime.SetupServiceBasedRuntime(serviceID, moruntime.DefaultRuntime()) + s := &service{ + cfg: &Config{UUID: serviceID}, metadata: metadata.CNStore{UUID: serviceID}, + metadataFS: newDDLVisibilityMetadataFS(t), viewMetadataAdmissionGeneration: 1, + } + require.NoError(t, s.persistDDLVisibilityDeployedProtocol(defines.MORPCVersion41)) + + err := s.prepareDDLVisibilityBarrier() + require.ErrorContains(t, err, "dependencies are unavailable") + require.False(t, s.ddlVisibilityBarrierPrepared.Load()) + require.False(t, s.ddlVisibilityBarrierReady.Load()) +} + +func TestPrepareDDLVisibilityBarrierSkipsFrontierSyncDuringRollingUpgrade(t *testing.T) { + const serviceID = "ddl-visibility-mixed-version-test" + rt := moruntime.DefaultRuntime() + rt.SetGlobalVariables(moruntime.MOProtocolVersion, defines.MORPCVersion34) + moruntime.SetupServiceBasedRuntime(serviceID, rt) + + s := &service{cfg: &Config{UUID: serviceID}} + require.NoError(t, s.prepareDDLVisibilityBarrier()) + require.True(t, s.ddlVisibilityBarrierPrepared.Load()) + require.False(t, s.ddlVisibilityActivationPrepared.Load()) + require.False(t, s.ddlVisibilityActivationFenced.Load()) + require.True(t, s.ddlVisibilityBarrierReady.Load()) +} + +func TestHandleSetProtocolVersionRejectsStaleRecoveryIdentity(t *testing.T) { + const serviceID = "ddl-visibility-stale-recovery-identity-test" + const generation = uint64(7) + rt := moruntime.DefaultRuntime() + rt.SetGlobalVariables(moruntime.MOProtocolVersion, defines.MORPCVersion34) + moruntime.SetupServiceBasedRuntime(serviceID, rt) + s := &service{ + cfg: &Config{UUID: serviceID}, viewMetadataAdmissionGeneration: generation, + } + err := s.handleSetProtocolVersion(context.Background(), &query.Request{ + SetProtocolVersion: &query.SetProtocolVersionRequest{ + Version: defines.MORPCVersion41, DDLVisibilityActivationTargets: []string{serviceID}, + DDLVisibilityTargetGeneration: generation + 1, + DDLVisibilityTargetQueryAddress: "stale:6001", + }, + }, &query.Response{}, nil) + require.ErrorContains(t, err, "stale DDL visibility activation target identity") + version, ok := rt.GetGlobalVariables(moruntime.MOProtocolVersion) + require.True(t, ok) + require.Equal(t, defines.MORPCVersion34, version) +} + +func TestActivationDoesNotPublishFencedBeforeProvisionalPersistence(t *testing.T) { + const serviceID = "ddl-visibility-provisional-persist-failure-test" + const generation = uint64(7) + rt := moruntime.DefaultRuntime() + rt.SetGlobalVariables(moruntime.MOProtocolVersion, defines.MORPCVersion40) + moruntime.SetupServiceBasedRuntime(serviceID, rt) + cluster := &ddlVisibilityTestCluster{cnServices: []metadata.CNService{{ + ServiceID: serviceID, QueryAddress: "self:6001", + ViewMetadataAdmissionGeneration: generation, DDLVisibilityBarrierReady: true, + ViewMetadataIngressReady: true, + }}} + queryClient := &ddlVisibilityTestQueryClient{ + serviceID: serviceID, + frontiers: map[string]timestamp.Timestamp{"self:6001": {PhysicalTime: 100}}, + } + txnClient := mock_frontend.NewMockTxnClient(gomock.NewController(t)) + baseFS := newDDLVisibilityMetadataFS(t) + metadataFS := &failReplaceMetadataFS{ReplaceableFileService: baseFS, failAt: 1} + cfg := &Config{UUID: serviceID} + cfg.HAKeeper.DiscoveryTimeout.Duration = time.Second + cfg.HAKeeper.HeatbeatInterval.Duration = time.Millisecond + s := &service{ + cfg: cfg, metadata: metadata.CNStore{UUID: serviceID}, metadataFS: metadataFS, + _hakeeperClient: &ddlVisibilityWithdrawalHAKeeperClient{cluster: cluster}, + moCluster: cluster, queryClient: queryClient, _txnClient: txnClient, + config: util.NewConfigData(nil), viewMetadataAdmissionGeneration: generation, + ddlCommitGate: frontend.NewDDLCommitGate(), + } + s.ddlVisibilityBarrierPrepared.Store(true) + s.ddlVisibilityBarrierReady.Store(true) + s.viewMetadataIngressReady.Store(true) + + err := s.setProtocolVersion(context.Background(), defines.MORPCVersion41, []string{serviceID}) + require.ErrorContains(t, err, "injected metadata replace failure") + require.True(t, s.ddlVisibilityActivationPrepared.Load()) + require.False(t, s.ddlVisibilityActivationFenced.Load()) + require.False(t, s.ddlVisibilityActivationComplete.Load()) + require.False(t, s.viewMetadataIngressReady.Load()) + require.Equal(t, int64(0), s.loadDDLVisibilityDeployedProtocol()) +} + +func TestCommittedPersistenceFailureRestartsFromProvisionalFailClosed(t *testing.T) { + const serviceID = "ddl-visibility-committed-persist-failure-test" + const generation = uint64(7) + rt := moruntime.DefaultRuntime() + rt.SetGlobalVariables(moruntime.MOProtocolVersion, defines.MORPCVersion40) + moruntime.SetupServiceBasedRuntime(serviceID, rt) + cluster := &ddlVisibilityTestCluster{cnServices: []metadata.CNService{{ + ServiceID: serviceID, QueryAddress: "self:6001", + ViewMetadataAdmissionGeneration: generation, DDLVisibilityBarrierReady: true, + ViewMetadataIngressReady: true, + }}} + targetTS := timestamp.Timestamp{PhysicalTime: 100} + queryClient := &ddlVisibilityTestQueryClient{ + serviceID: serviceID, + frontiers: map[string]timestamp.Timestamp{"self:6001": targetTS}, + } + txnClient := mock_frontend.NewMockTxnClient(gomock.NewController(t)) + baseFS := newDDLVisibilityMetadataFS(t) + metadataFS := &failReplaceMetadataFS{ReplaceableFileService: baseFS, failAt: 2} + cfg := &Config{UUID: serviceID} + cfg.HAKeeper.DiscoveryTimeout.Duration = time.Second + cfg.HAKeeper.HeatbeatInterval.Duration = time.Millisecond + s := &service{ + cfg: cfg, metadata: metadata.CNStore{UUID: serviceID}, metadataFS: metadataFS, + _hakeeperClient: &ddlVisibilityWithdrawalHAKeeperClient{cluster: cluster}, + moCluster: cluster, queryClient: queryClient, _txnClient: txnClient, + config: util.NewConfigData(nil), viewMetadataAdmissionGeneration: generation, + ddlCommitGate: frontend.NewDDLCommitGate(), + } + s.ddlVisibilityBarrierPrepared.Store(true) + s.ddlVisibilityBarrierReady.Store(true) + s.viewMetadataIngressReady.Store(true) + + err := s.setProtocolVersion(context.Background(), defines.MORPCVersion41, []string{serviceID}) + require.ErrorContains(t, err, "injected metadata replace failure") + require.True(t, s.ddlVisibilityActivationFenced.Load()) + require.False(t, s.ddlVisibilityActivationComplete.Load()) + require.False(t, s.viewMetadataIngressReady.Load()) + require.Equal(t, -defines.MORPCVersion41, s.loadDDLVisibilityDeployedProtocol()) + + // A distinct process reloads the provisional marker through initMetadata and + // must remain a v41, non-routable producer until activation is retried. + moruntime.SetupServiceBasedRuntime(serviceID, moruntime.DefaultRuntime()) + restarted := &service{ + cfg: cfg, metadata: metadata.CNStore{UUID: serviceID}, metadataFS: baseFS, + logger: zap.NewNop(), ddlCommitGate: frontend.NewDDLCommitGate(), + } + require.NoError(t, restarted.initMetadata()) + require.NoError(t, restarted.prepareDDLVisibilityBarrier()) + require.NoError(t, restarted.publishDDLVisibilityIngressAfterStart()) + version, ok := moruntime.ServiceRuntime(serviceID).GetGlobalVariables(moruntime.MOProtocolVersion) + require.True(t, ok) + require.Equal(t, defines.MORPCVersion41, version) + require.False(t, restarted.viewMetadataIngressReady.Load()) + require.False(t, restarted.ddlCommitGate.PublicDDLEnabled()) +} + +func TestActivationWithdrawalFailureStillBlocksNewDDL(t *testing.T) { + const serviceID = "ddl-visibility-withdrawal-failure-gate-test" + const generation = uint64(7) + withdrawErr := errors.New("injected withdrawal failure") + rt := moruntime.DefaultRuntime() + rt.SetGlobalVariables(moruntime.MOProtocolVersion, defines.MORPCVersion35) + moruntime.SetupServiceBasedRuntime(serviceID, rt) + cluster := &ddlVisibilityTestCluster{cnServices: []metadata.CNService{{ + ServiceID: serviceID, QueryAddress: "self:6001", + ViewMetadataAdmissionGeneration: generation, DDLVisibilityBarrierReady: true, + ViewMetadataIngressReady: true, + }}} + cfg := &Config{UUID: serviceID} + cfg.HAKeeper.DiscoveryTimeout.Duration = time.Second + gate := frontend.NewDDLCommitGate() + s := &service{ + cfg: cfg, _hakeeperClient: &ddlVisibilityWithdrawalHAKeeperClient{ + cluster: cluster, sendErr: withdrawErr, + }, + moCluster: cluster, queryClient: &ddlVisibilityTestQueryClient{serviceID: serviceID}, + _txnClient: mock_frontend.NewMockTxnClient(gomock.NewController(t)), + config: util.NewConfigData(nil), viewMetadataAdmissionGeneration: generation, + ddlCommitGate: gate, + } + s.ddlVisibilityBarrierPrepared.Store(true) + s.ddlVisibilityBarrierReady.Store(true) + s.viewMetadataIngressReady.Store(true) + + err := s.setProtocolVersion(context.Background(), defines.MORPCVersion41, []string{serviceID}) + require.ErrorIs(t, err, withdrawErr) + require.True(t, cluster.cnServices[0].ViewMetadataIngressReady, + "the injected heartbeat failure leaves authoritative ingress unchanged") + blockedCtx, cancel := context.WithCancel(context.Background()) + cancel() + _, err = gate.Enter(blockedCtx) + require.ErrorIs(t, err, context.Canceled, + "withdrawal failure must not reopen old-protocol DDL admission") +} + +func TestActivationDrainTimeoutKeepsIngressWithdrawn(t *testing.T) { + const serviceID = "ddl-visibility-drain-timeout-test" + const generation = uint64(7) + rt := moruntime.DefaultRuntime() + rt.SetGlobalVariables(moruntime.MOProtocolVersion, defines.MORPCVersion35) + moruntime.SetupServiceBasedRuntime(serviceID, rt) + cluster := &ddlVisibilityTestCluster{cnServices: []metadata.CNService{{ + ServiceID: serviceID, QueryAddress: "self:6001", + ViewMetadataAdmissionGeneration: generation, DDLVisibilityBarrierReady: true, + ViewMetadataIngressReady: true, + }}} + cfg := &Config{UUID: serviceID} + cfg.HAKeeper.DiscoveryTimeout.Duration = 10 * time.Millisecond + cfg.HAKeeper.HeatbeatInterval.Duration = time.Millisecond + gate := frontend.NewDDLCommitGate() + releaseActive, err := gate.Enter(context.Background()) + require.NoError(t, err) + defer releaseActive() + s := &service{ + cfg: cfg, _hakeeperClient: &ddlVisibilityWithdrawalHAKeeperClient{cluster: cluster}, + moCluster: cluster, queryClient: &ddlVisibilityTestQueryClient{serviceID: serviceID}, + _txnClient: mock_frontend.NewMockTxnClient(gomock.NewController(t)), + config: util.NewConfigData(nil), viewMetadataAdmissionGeneration: generation, + ddlCommitGate: gate, + } + s.ddlVisibilityBarrierPrepared.Store(true) + s.ddlVisibilityBarrierReady.Store(true) + s.viewMetadataIngressReady.Store(true) + + err = s.setProtocolVersion(context.Background(), defines.MORPCVersion41, []string{serviceID}) + require.Error(t, err) + require.True(t, s.ddlVisibilityActivationPending.Load()) + require.False(t, s.viewMetadataIngressReady.Load()) + require.False(t, cluster.cnServices[0].ViewMetadataIngressReady) + blockedCtx, cancel := context.WithCancel(context.Background()) + cancel() + _, err = gate.Enter(blockedCtx) + require.Error(t, err) +} + +func TestDefaultV41StillRunsCompleteTargetActivation(t *testing.T) { + const serviceID = "ddl-visibility-default-v41-activation-test" + const generation = uint64(7) + moruntime.SetupServiceBasedRuntime(serviceID, moruntime.DefaultRuntime()) + targetTS := timestamp.Timestamp{PhysicalTime: 300, LogicalTime: 4} + cluster := &ddlVisibilityTestCluster{cnServices: []metadata.CNService{ + {ServiceID: serviceID, QueryAddress: "self:6001", + ViewMetadataAdmissionGeneration: generation, DDLVisibilityBarrierReady: true, + ViewMetadataIngressReady: true}, + {ServiceID: "legacy-peer", QueryAddress: "peer:6001", + ViewMetadataAdmissionGeneration: 8, DDLVisibilityBarrierReady: false, + ViewMetadataIngressReady: true}, + }, frontiers: map[string]timestamp.Timestamp{"legacy-peer": targetTS}} + cluster.refreshHook = func() { + // The control plane dispatches activation concurrently. Model the legacy + // peer completing its local drain before this CN checks global phases. + cluster.cnServices[1].DDLVisibilityBarrierReady = true + } + queryClient := &ddlVisibilityTestQueryClient{ + serviceID: serviceID, + frontiers: map[string]timestamp.Timestamp{ + "self:6001": {PhysicalTime: 100}, "peer:6001": targetTS, + }, + protocols: map[string]query.GetProtocolVersionResponse{"peer:6001": { + Version: defines.MORPCVersion41, + DDLVisibilityActivationPrepared: true, DDLVisibilityActivationFenced: true, + }}, + } + ctrl := gomock.NewController(t) + txnClient := mock_frontend.NewMockTxnClient(ctrl) + txnClient.EXPECT().WaitLogTailAppliedAt(gomock.Any(), targetTS).Return(targetTS.Next(), nil) + txnClient.EXPECT().SyncLatestCommitTS(targetTS) + cfg := &Config{UUID: serviceID} + cfg.HAKeeper.DiscoveryTimeout.Duration = time.Second + cfg.HAKeeper.HeatbeatInterval.Duration = time.Millisecond + s := &service{ + cfg: cfg, metadata: metadata.CNStore{UUID: serviceID}, metadataFS: newDDLVisibilityMetadataFS(t), + _hakeeperClient: &ddlVisibilityWithdrawalHAKeeperClient{cluster: cluster}, + moCluster: cluster, queryClient: queryClient, _txnClient: txnClient, + config: util.NewConfigData(nil), viewMetadataAdmissionGeneration: generation, + ddlCommitGate: frontend.NewDDLCommitGate(), + } + s.ddlVisibilityBarrierPrepared.Store(true) + s.ddlVisibilityBarrierReady.Store(true) + s.ddlVisibilityActivationPrepared.Store(true) + s.ddlVisibilityActivationFenced.Store(true) + s.viewMetadataIngressReady.Store(true) + + require.False(t, s.ddlVisibilityActivationComplete.Load()) + require.NoError(t, s.setProtocolVersion( + context.Background(), defines.MORPCVersion41, []string{serviceID, "legacy-peer"})) + require.True(t, s.ddlVisibilityActivationComplete.Load()) + require.Equal(t, defines.MORPCVersion41, s.loadDDLVisibilityDeployedProtocol()) + require.True(t, s.viewMetadataIngressReady.Load()) + require.True(t, s.ddlCommitGate.PublicDDLEnabled()) + require.Empty(t, queryClient.requests) +} + +func TestHandleSetProtocolVersionFencesRunningCNActivation(t *testing.T) { + const serviceID = "ddl-visibility-live-activation-test" + const generation = uint64(7) + rt := moruntime.DefaultRuntime() + rt.SetGlobalVariables(moruntime.MOProtocolVersion, defines.MORPCVersion34) + moruntime.SetupServiceBasedRuntime(serviceID, rt) + targetTS := timestamp.Timestamp{PhysicalTime: 300, LogicalTime: 4} + cluster := &ddlVisibilityTestCluster{cnServices: []metadata.CNService{ + { + ServiceID: serviceID, QueryAddress: "self:6001", + ViewMetadataAdmissionGeneration: generation, DDLVisibilityBarrierReady: true, + }, + { + ServiceID: "peer", QueryAddress: "peer:6001", + ViewMetadataAdmissionGeneration: 9, DDLVisibilityBarrierReady: true, + }, + }} + cluster.frontiers = map[string]timestamp.Timestamp{"peer": targetTS} + var peerReady atomic.Bool + var protocolObserved atomic.Bool + firstProtocol := make(chan struct{}) + queryClient := &ddlVisibilityTestQueryClient{ + serviceID: serviceID, + frontiers: map[string]timestamp.Timestamp{ + "self:6001": {PhysicalTime: 200}, + "peer:6001": targetTS, + }, + protocolFn: func(string) query.GetProtocolVersionResponse { + if protocolObserved.CompareAndSwap(false, true) { + close(firstProtocol) + } + ready := peerReady.Load() + return query.GetProtocolVersionResponse{ + Version: defines.MORPCVersion41, + DDLVisibilityActivationPrepared: ready, + DDLVisibilityActivationFenced: ready, + } + }, + } + cluster.phaseHook = queryClient.protocolFn + ctrl := gomock.NewController(t) + txnClient := mock_frontend.NewMockTxnClient(ctrl) + txnClient.EXPECT().WaitLogTailAppliedAt(gomock.Any(), targetTS).DoAndReturn( + func(context.Context, timestamp.Timestamp) (timestamp.Timestamp, error) { + version, ok := rt.GetGlobalVariables(moruntime.MOProtocolVersion) + require.True(t, ok) + require.Equal(t, defines.MORPCVersion41, version, + "sender capability must be v41 after every local v34 DDL producer is drained") + require.True(t, cluster.cnServices[0].DDLVisibilityBarrierReady, + "the transitioning CN must remain reachable by v41 DDL fan-out") + require.False(t, cluster.cnServices[0].ViewMetadataIngressReady, + "the transitioning CN must not accept new proxy sessions before the fence") + return targetTS.Next(), nil + }) + txnClient.EXPECT().SyncLatestCommitTS(targetTS) + cfg := &Config{UUID: serviceID} + cfg.HAKeeper.DiscoveryTimeout.Duration = time.Second + cfg.HAKeeper.HeatbeatInterval.Duration = time.Millisecond + hakeeperClient := &ddlVisibilityWithdrawalHAKeeperClient{cluster: cluster} + ddlCommitGate := frontend.NewDDLCommitGate() + s := &service{ + cfg: cfg, + _hakeeperClient: hakeeperClient, + moCluster: cluster, + queryClient: queryClient, + _txnClient: txnClient, + config: util.NewConfigData(nil), + viewMetadataAdmissionGeneration: generation, + ddlCommitGate: ddlCommitGate, + } + s.ddlVisibilityBarrierPrepared.Store(true) + s.ddlVisibilityBarrierReady.Store(true) + s.viewMetadataIngressReady.Store(true) + + req := &query.Request{SetProtocolVersion: &query.SetProtocolVersionRequest{ + Version: defines.MORPCVersion41, + DDLVisibilityActivationTargets: []string{serviceID, "peer"}, + DDLVisibilityTargetGeneration: generation, + DDLVisibilityTargetQueryAddress: "self:6001", + }} + resp := &query.Response{} + activationDone := make(chan error, 1) + go func() { + activationDone <- s.handleSetProtocolVersion(context.Background(), req, resp, nil) + }() + <-firstProtocol + blockedCtx, cancelBlocked := context.WithCancel(context.Background()) + cancelBlocked() + _, err := ddlCommitGate.Enter(blockedCtx) + require.ErrorIs(t, err, context.Canceled, + "DDL producer must remain blocked until every target is prepared and fenced") + peerReady.Store(true) + require.NoError(t, <-activationDone) + releaseDDL, err := ddlCommitGate.Enter(context.Background()) + require.NoError(t, err, "activation must release DDL producers after global convergence") + releaseDDL() + require.Equal(t, defines.MORPCVersion41, resp.SetProtocolVersion.Version) + version, ok := rt.GetGlobalVariables(moruntime.MOProtocolVersion) + require.True(t, ok) + require.Equal(t, defines.MORPCVersion41, version) + require.Empty(t, queryClient.requests) + require.Empty(t, queryClient.methods) + require.Zero(t, queryClient.releases) + require.Len(t, hakeeperClient.heartbeats, 5) + require.False(t, hakeeperClient.heartbeats[0].ViewMetadataIngressReady) + require.True(t, hakeeperClient.heartbeats[1].DDLVisibilityActivationPrepared) + require.False(t, hakeeperClient.heartbeats[1].DDLVisibilityActivationFenced) + require.True(t, hakeeperClient.heartbeats[2].DDLVisibilityActivationFenced) + require.NotEmpty(t, hakeeperClient.heartbeats[3].DDLVisibilityEpochCommitTargets) + require.False(t, hakeeperClient.heartbeats[3].ViewMetadataIngressReady) + require.True(t, hakeeperClient.heartbeats[4].ViewMetadataIngressReady) + require.Equal(t, 2, cluster.refreshCalls) + require.True(t, s.ddlVisibilityBarrierReady.Load()) + require.True(t, s.viewMetadataIngressReady.Load()) + + // Repeating an already-active version must not withdraw or re-run the fence. + resp = &query.Response{} + require.NoError(t, s.handleSetProtocolVersion(context.Background(), req, resp, nil)) + require.Equal(t, defines.MORPCVersion41, resp.SetProtocolVersion.Version) + require.Len(t, hakeeperClient.heartbeats, 5) + require.Equal(t, 2, cluster.refreshCalls) + require.Empty(t, queryClient.requests) +} + +func TestHandleSetProtocolVersionPreservesPreStartIngress(t *testing.T) { + const serviceID = "ddl-visibility-pre-start-ingress-test" + const generation = uint64(7) + rt := moruntime.DefaultRuntime() + rt.SetGlobalVariables(moruntime.MOProtocolVersion, defines.MORPCVersion34) + moruntime.SetupServiceBasedRuntime(serviceID, rt) + cluster := &ddlVisibilityTestCluster{cnServices: []metadata.CNService{ + {ServiceID: serviceID, QueryAddress: "self:6001", + ViewMetadataAdmissionGeneration: generation, DDLVisibilityBarrierReady: true}, + {ServiceID: "peer", QueryAddress: "peer:6001", + ViewMetadataAdmissionGeneration: 8, DDLVisibilityBarrierReady: true}, + }} + queryClient := &ddlVisibilityTestQueryClient{ + serviceID: serviceID, + frontiers: map[string]timestamp.Timestamp{"self:6001": {}, "peer:6001": {}}, + protocols: activationTestPeerProtocols(), + } + cfg := &Config{UUID: serviceID} + cfg.HAKeeper.DiscoveryTimeout.Duration = time.Second + cfg.HAKeeper.HeatbeatInterval.Duration = time.Millisecond + hakeeperClient := &ddlVisibilityWithdrawalHAKeeperClient{cluster: cluster} + s := &service{ + cfg: cfg, _hakeeperClient: hakeeperClient, moCluster: cluster, + queryClient: queryClient, _txnClient: mock_frontend.NewMockTxnClient(gomock.NewController(t)), + config: util.NewConfigData(nil), viewMetadataAdmissionGeneration: generation, + ddlCommitGate: frontend.NewDDLCommitGate(), + } + s.ddlVisibilityBarrierPrepared.Store(true) + s.ddlVisibilityBarrierReady.Store(true) + s.viewMetadataIngressReady.Store(false) + + resp := &query.Response{} + require.NoError(t, s.handleSetProtocolVersion(context.Background(), &query.Request{ + SetProtocolVersion: &query.SetProtocolVersionRequest{ + Version: defines.MORPCVersion41, + DDLVisibilityActivationTargets: []string{serviceID, "peer"}, + DDLVisibilityTargetGeneration: generation, DDLVisibilityTargetQueryAddress: "self:6001", + }, + }, resp, nil)) + require.Equal(t, defines.MORPCVersion41, resp.SetProtocolVersion.Version) + require.False(t, s.viewMetadataIngressReady.Load()) + require.False(t, cluster.cnServices[0].ViewMetadataIngressReady) + require.Len(t, hakeeperClient.heartbeats, 5) + require.False(t, hakeeperClient.heartbeats[0].ViewMetadataIngressReady) + require.True(t, hakeeperClient.heartbeats[1].DDLVisibilityActivationPrepared) + require.True(t, hakeeperClient.heartbeats[2].DDLVisibilityActivationFenced) + require.NotEmpty(t, hakeeperClient.heartbeats[3].DDLVisibilityEpochCommitTargets) + require.False(t, hakeeperClient.heartbeats[4].ViewMetadataIngressReady) + release, err := s.ddlCommitGate.Enter(context.Background()) + require.NoError(t, err) + release() + + // Re-run the transition and linearize listener-ready publication while + // activation still owns the barrier lock. The later Start publication must + // win; activation must not restore its stale pre-listener false sample. + serviceRuntime := moruntime.ServiceRuntime(serviceID) + serviceRuntime.SetGlobalVariables(moruntime.MOProtocolVersion, defines.MORPCVersion34) + value, ok := serviceRuntime.GetGlobalVariables(moruntime.MOProtocolVersion) + require.True(t, ok) + require.Equal(t, defines.MORPCVersion34, value) + s.ddlVisibilityActivationPending.Store(true) + s.ddlVisibilityActivationPrepared.Store(false) + s.ddlVisibilityActivationFenced.Store(false) + s.viewMetadataIngressReady.Store(false) + cluster.cnServices[0].ViewMetadataIngressReady = false + phaseEntered := make(chan struct{}) + allowPhase := make(chan struct{}) + var phaseCalls atomic.Int32 + queryClient.protocolFn = func(string) query.GetProtocolVersionResponse { + if phaseCalls.Add(1) == 1 { + close(phaseEntered) + <-allowPhase + } + return query.GetProtocolVersionResponse{ + Version: defines.MORPCVersion41, + DDLVisibilityActivationPrepared: true, DDLVisibilityActivationFenced: true, + } + } + cluster.phaseHook = queryClient.protocolFn + activationDone := make(chan error, 1) + go func() { + activationDone <- s.handleSetProtocolVersion(context.Background(), &query.Request{ + SetProtocolVersion: &query.SetProtocolVersionRequest{ + Version: defines.MORPCVersion41, + DDLVisibilityActivationTargets: []string{serviceID, "peer"}, + DDLVisibilityTargetGeneration: generation, DDLVisibilityTargetQueryAddress: "self:6001", + }, + }, &query.Response{}, nil) + }() + select { + case <-phaseEntered: + case err := <-activationDone: + require.NoError(t, err) + t.Fatal("activation completed before entering phase convergence") + } + publishDone := make(chan error, 1) + go func() { publishDone <- s.publishDDLVisibilityIngressAfterStart() }() + close(allowPhase) + require.NoError(t, <-activationDone) + require.NoError(t, <-publishDone) + require.True(t, s.viewMetadataIngressReady.Load()) +} + +func TestHandleSetProtocolVersionFailsClosedWhenActivationSyncFails(t *testing.T) { + const serviceID = "ddl-visibility-live-activation-failure-test" + const generation = uint64(7) + rt := moruntime.DefaultRuntime() + rt.SetGlobalVariables(moruntime.MOProtocolVersion, defines.MORPCVersion34) + moruntime.SetupServiceBasedRuntime(serviceID, rt) + targetTS := timestamp.Timestamp{PhysicalTime: 300, LogicalTime: 4} + cluster := &ddlVisibilityTestCluster{cnServices: []metadata.CNService{ + { + ServiceID: serviceID, QueryAddress: "self:6001", + ViewMetadataAdmissionGeneration: generation, DDLVisibilityBarrierReady: true, + }, + {ServiceID: "peer", QueryAddress: "peer:6001", ViewMetadataAdmissionGeneration: 8, + DDLVisibilityBarrierReady: true}, + }} + cluster.frontiers = map[string]timestamp.Timestamp{"peer": targetTS} + queryClient := &ddlVisibilityTestQueryClient{ + serviceID: serviceID, + frontiers: map[string]timestamp.Timestamp{ + "self:6001": {PhysicalTime: 200}, "peer:6001": targetTS, + }, + protocols: activationTestPeerProtocols(), + } + ctrl := gomock.NewController(t) + txnClient := mock_frontend.NewMockTxnClient(ctrl) + syncErr := errors.New("logtail activation sync failed") + txnClient.EXPECT().WaitLogTailAppliedAt(gomock.Any(), targetTS).Return(timestamp.Timestamp{}, syncErr) + cfg := &Config{UUID: serviceID} + cfg.HAKeeper.DiscoveryTimeout.Duration = time.Second + cfg.HAKeeper.HeatbeatInterval.Duration = time.Millisecond + hakeeperClient := &ddlVisibilityWithdrawalHAKeeperClient{cluster: cluster} + s := &service{ + cfg: cfg, + _hakeeperClient: hakeeperClient, + moCluster: cluster, + queryClient: queryClient, + _txnClient: txnClient, + config: util.NewConfigData(nil), + viewMetadataAdmissionGeneration: generation, + ddlCommitGate: frontend.NewDDLCommitGate(), + } + s.ddlVisibilityBarrierPrepared.Store(true) + s.ddlVisibilityBarrierReady.Store(true) + s.viewMetadataIngressReady.Store(true) + + resp := &query.Response{} + err := s.handleSetProtocolVersion(context.Background(), &query.Request{ + SetProtocolVersion: &query.SetProtocolVersionRequest{ + Version: defines.MORPCVersion41, DDLVisibilityActivationTargets: []string{serviceID, "peer"}, + DDLVisibilityTargetGeneration: generation, DDLVisibilityTargetQueryAddress: "self:6001", + }, + }, resp, nil) + require.ErrorIs(t, err, syncErr) + require.Nil(t, resp.SetProtocolVersion) + version, ok := rt.GetGlobalVariables(moruntime.MOProtocolVersion) + require.True(t, ok) + require.Equal(t, defines.MORPCVersion41, version) + require.True(t, s.ddlVisibilityActivationPending.Load()) + require.True(t, s.ddlVisibilityActivationPrepared.Load()) + require.False(t, s.ddlVisibilityActivationFenced.Load()) + blockedCtx, cancelBlocked := context.WithCancel(context.Background()) + cancelBlocked() + _, gateErr := s.ddlCommitGate.Enter(blockedCtx) + require.ErrorIs(t, gateErr, context.Canceled) + require.Len(t, hakeeperClient.heartbeats, 2) + require.True(t, hakeeperClient.heartbeats[0].DDLVisibilityBarrierReady) + require.True(t, hakeeperClient.heartbeats[1].DDLVisibilityActivationPrepared) + require.False(t, s.viewMetadataIngressReady.Load()) + require.True(t, cluster.cnServices[0].DDLVisibilityBarrierReady) +} + +func TestHandleSetProtocolVersionWithdrawsAfterActivationPublishFails(t *testing.T) { + const serviceID = "ddl-visibility-live-activation-publish-failure-test" + const generation = uint64(7) + rt := moruntime.DefaultRuntime() + rt.SetGlobalVariables(moruntime.MOProtocolVersion, defines.MORPCVersion34) + moruntime.SetupServiceBasedRuntime(serviceID, rt) + targetTS := timestamp.Timestamp{PhysicalTime: 300, LogicalTime: 4} + cluster := &ddlVisibilityTestCluster{cnServices: []metadata.CNService{ + { + ServiceID: serviceID, QueryAddress: "self:6001", + ViewMetadataAdmissionGeneration: generation, DDLVisibilityBarrierReady: true, + }, + {ServiceID: "peer", QueryAddress: "peer:6001", ViewMetadataAdmissionGeneration: 8, + DDLVisibilityBarrierReady: true}, + }} + cluster.frontiers = map[string]timestamp.Timestamp{"peer": targetTS} + queryClient := &ddlVisibilityTestQueryClient{ + serviceID: serviceID, + frontiers: map[string]timestamp.Timestamp{ + "self:6001": {PhysicalTime: 200}, "peer:6001": targetTS, + }, + protocols: activationTestPeerProtocols(), + } + ctrl := gomock.NewController(t) + txnClient := mock_frontend.NewMockTxnClient(ctrl) + txnClient.EXPECT().WaitLogTailAppliedAt(gomock.Any(), targetTS).Return(targetTS.Next(), nil) + txnClient.EXPECT().SyncLatestCommitTS(targetTS) + cfg := &Config{UUID: serviceID} + cfg.HAKeeper.DiscoveryTimeout.Duration = time.Second + cfg.HAKeeper.HeatbeatInterval.Duration = time.Millisecond + publishErr := errors.New("activation publication failed") + hakeeperClient := &ddlVisibilityWithdrawalHAKeeperClient{ + cluster: cluster, sendErrors: map[int]error{5: publishErr}, + } + s := &service{ + cfg: cfg, + _hakeeperClient: hakeeperClient, + moCluster: cluster, + queryClient: queryClient, + _txnClient: txnClient, + config: util.NewConfigData(nil), + viewMetadataAdmissionGeneration: generation, + ddlCommitGate: frontend.NewDDLCommitGate(), + } + s.ddlVisibilityBarrierPrepared.Store(true) + s.ddlVisibilityBarrierReady.Store(true) + s.viewMetadataIngressReady.Store(true) + + err := s.handleSetProtocolVersion(context.Background(), &query.Request{ + SetProtocolVersion: &query.SetProtocolVersionRequest{ + Version: defines.MORPCVersion41, DDLVisibilityActivationTargets: []string{serviceID, "peer"}, + DDLVisibilityTargetGeneration: generation, DDLVisibilityTargetQueryAddress: "self:6001", + }, + }, &query.Response{}, nil) + require.ErrorIs(t, err, publishErr) + version, ok := rt.GetGlobalVariables(moruntime.MOProtocolVersion) + require.True(t, ok) + require.Equal(t, defines.MORPCVersion41, version) + require.True(t, s.ddlVisibilityActivationPending.Load()) + require.True(t, s.ddlVisibilityActivationFenced.Load()) + blockedCtx, cancelBlocked := context.WithCancel(context.Background()) + cancelBlocked() + _, gateErr := s.ddlCommitGate.Enter(blockedCtx) + require.ErrorIs(t, gateErr, context.Canceled) + require.Len(t, hakeeperClient.heartbeats, 6) + require.True(t, hakeeperClient.heartbeats[0].DDLVisibilityBarrierReady) + require.True(t, hakeeperClient.heartbeats[1].DDLVisibilityActivationPrepared) + require.True(t, hakeeperClient.heartbeats[2].DDLVisibilityActivationFenced) + require.NotEmpty(t, hakeeperClient.heartbeats[3].DDLVisibilityEpochCommitTargets) + require.True(t, hakeeperClient.heartbeats[4].ViewMetadataIngressReady) + require.False(t, hakeeperClient.heartbeats[5].ViewMetadataIngressReady) + require.False(t, s.viewMetadataIngressReady.Load()) + require.True(t, cluster.cnServices[0].DDLVisibilityBarrierReady) + require.Equal(t, 2, cluster.refreshCalls) +} + +func TestSetProtocolVersionBeforeBarrierPreparationDefersToStartupFence(t *testing.T) { + const serviceID = "ddl-visibility-pre-start-activation-test" + rt := moruntime.DefaultRuntime() + rt.SetGlobalVariables(moruntime.MOProtocolVersion, defines.MORPCVersion34) + moruntime.SetupServiceBasedRuntime(serviceID, rt) + s := &service{cfg: &Config{UUID: serviceID}} + + require.NoError(t, s.setProtocolVersion(context.Background(), defines.MORPCVersion41, nil)) + version, ok := rt.GetGlobalVariables(moruntime.MOProtocolVersion) + require.True(t, ok) + require.Equal(t, defines.MORPCVersion41, version) + require.False(t, s.ddlVisibilityBarrierPrepared.Load()) + require.False(t, s.ddlVisibilityBarrierReady.Load()) +} + +func TestInitQueryCommandHandlerOverridesProtocolActivation(t *testing.T) { + const serviceID = "ddl-visibility-public-protocol-handler-test" + rt := moruntime.DefaultRuntime() + rt.SetGlobalVariables(moruntime.MOProtocolVersion, defines.MORPCVersion34) + moruntime.SetupServiceBasedRuntime(serviceID, rt) + queryService := &closeRecordingQueryService{ + handlers: make(map[query.CmdMethod]func(context.Context, *query.Request, *query.Response, *morpc.Buffer) error), + } + s := &service{cfg: &Config{UUID: serviceID}, queryService: queryService} + s.ddlVisibilityBarrierPrepared.Store(true) + s.ddlVisibilityBarrierClosing.Store(true) + s.ddlVisibilityActivationPrepared.Store(true) + s.ddlVisibilityActivationFenced.Store(true) + s.initQueryCommandHandler() + + getHandler, ok := queryService.handlers[query.CmdMethod_GetProtocolVersion] + require.True(t, ok) + getResp := &query.Response{} + require.NoError(t, getHandler(context.Background(), &query.Request{ + GetProtocolVersion: &query.GetProtocolVersionRequest{}, + }, getResp, nil)) + require.Equal(t, defines.MORPCVersion34, getResp.GetProtocolVersion.Version) + require.True(t, getResp.GetProtocolVersion.DDLVisibilityActivationPrepared) + require.True(t, getResp.GetProtocolVersion.DDLVisibilityActivationFenced) + require.ErrorContains(t, + getHandler(context.Background(), &query.Request{}, &query.Response{}, nil), + "bad request") + rt.SetGlobalVariables(moruntime.MOProtocolVersion, "invalid") + require.ErrorContains(t, getHandler(context.Background(), &query.Request{ + GetProtocolVersion: &query.GetProtocolVersionRequest{}, + }, &query.Response{}, nil), "invalid protocol version") + rt.SetGlobalVariables(moruntime.MOProtocolVersion, defines.MORPCVersion34) + + handler, ok := queryService.handlers[query.CmdMethod_SetProtocolVersion] + require.True(t, ok) + resp := &query.Response{} + err := handler(context.Background(), &query.Request{ + SetProtocolVersion: &query.SetProtocolVersionRequest{Version: defines.MORPCVersion41}, + }, resp, nil) + require.ErrorContains(t, err, "CN is closing") + require.Nil(t, resp.SetProtocolVersion) + version, ok := rt.GetGlobalVariables(moruntime.MOProtocolVersion) + require.True(t, ok) + require.Equal(t, defines.MORPCVersion34, version) +} + +func TestSetProtocolVersionRejectsActivationDuringClose(t *testing.T) { + const serviceID = "ddl-visibility-closing-activation-test" + rt := moruntime.DefaultRuntime() + rt.SetGlobalVariables(moruntime.MOProtocolVersion, defines.MORPCVersion34) + moruntime.SetupServiceBasedRuntime(serviceID, rt) + s := &service{cfg: &Config{UUID: serviceID}} + s.ddlVisibilityBarrierPrepared.Store(true) + s.ddlVisibilityBarrierClosing.Store(true) + + err := s.setProtocolVersion(context.Background(), defines.MORPCVersion41, nil) + require.ErrorContains(t, err, "CN is closing") + version, ok := rt.GetGlobalVariables(moruntime.MOProtocolVersion) + require.True(t, ok) + require.Equal(t, defines.MORPCVersion34, version) +} + +func TestWithdrawDDLVisibilityBarrierHandlesPartialService(t *testing.T) { + t.Run("zero generation has no authoritative publication", func(t *testing.T) { + s := &service{} + s.ddlVisibilityBarrierReady.Store(true) + s.viewMetadataIngressReady.Store(true) + require.NoError(t, s.withdrawDDLVisibilityBarrierLocked(context.Background())) + require.False(t, s.ddlVisibilityBarrierReady.Load()) + require.False(t, s.viewMetadataIngressReady.Load()) + }) + + t.Run("published generation requires dependencies", func(t *testing.T) { + s := &service{cfg: &Config{}, viewMetadataAdmissionGeneration: 1} + err := s.withdrawDDLVisibilityBarrierLocked(context.Background()) + require.ErrorContains(t, err, "withdrawal dependencies are unavailable") + }) +} + +func TestValidateDDLVisibilityActivationTargets(t *testing.T) { + _, err := validateDDLVisibilityActivationTargets("self", []string{"self", ""}) + require.ErrorContains(t, err, "empty") + _, err = validateDDLVisibilityActivationTargets("self", []string{"self", "self"}) + require.ErrorContains(t, err, "duplicated") + _, err = validateDDLVisibilityActivationTargets("self", []string{"peer"}) + require.ErrorContains(t, err, "do not include local CN") + targets, err := validateDDLVisibilityActivationTargets("self", []string{"self", "peer"}) + require.NoError(t, err) + require.Equal(t, map[string]struct{}{"self": {}, "peer": {}}, targets) +} + +func TestSetProtocolVersionDowngradeGuards(t *testing.T) { + const serviceID = "ddl-visibility-downgrade-test" + rt := moruntime.DefaultRuntime() + rt.SetGlobalVariables(moruntime.MOProtocolVersion, defines.MORPCVersion41) + moruntime.SetupServiceBasedRuntime(serviceID, rt) + s := &service{cfg: &Config{UUID: serviceID}} + s.ddlVisibilityBarrierPrepared.Store(true) + s.ddlVisibilityActivationPrepared.Store(true) + s.ddlVisibilityActivationFenced.Store(true) + s.ddlVisibilityActivationComplete.Store(true) + + err := s.setProtocolVersion(context.Background(), defines.MORPCVersion40, nil) + require.ErrorContains(t, err, "cannot downgrade after the DDL visibility cluster epoch is committed") + version, ok := rt.GetGlobalVariables(moruntime.MOProtocolVersion) + require.True(t, ok) + require.Equal(t, defines.MORPCVersion41, version) + + s.ddlVisibilityActivationComplete.Store(false) + s._hakeeperClient = &ddlVisibilityWithdrawalHAKeeperClient{ + clusterDeployedProtocol: defines.MORPCVersion41, + } + rt.SetGlobalVariables(moruntime.MOProtocolVersion, defines.MORPCVersion40) + err = s.setProtocolVersion(context.Background(), defines.MORPCVersion34, nil) + require.ErrorContains(t, err, "cannot downgrade after the DDL visibility cluster epoch is committed") + + s._hakeeperClient = nil + rt.SetGlobalVariables(moruntime.MOProtocolVersion, defines.MORPCVersion40) + require.NoError(t, s.setProtocolVersion(context.Background(), defines.MORPCVersion34, nil)) + version, ok = rt.GetGlobalVariables(moruntime.MOProtocolVersion) + require.True(t, ok) + require.Equal(t, defines.MORPCVersion34, version) + + s.ddlVisibilityActivationPending.Store(true) + err = s.setProtocolVersion(context.Background(), defines.MORPCVersion34, nil) + require.ErrorContains(t, err, "cannot downgrade during DDL visibility activation") +} + +func TestWaitForDDLVisibilityActivationPhaseRejectsInvalidInventory(t *testing.T) { + const serviceID = "activation-phase-inventory-test" + newService := func(cluster *ddlVisibilityTestCluster) *service { + cfg := &Config{UUID: serviceID} + cfg.HAKeeper.HeatbeatInterval.Duration = time.Millisecond + s := &service{cfg: cfg, moCluster: cluster, + _hakeeperClient: &ddlVisibilityWithdrawalHAKeeperClient{cluster: cluster}} + s.ddlVisibilityActivationPrepared.Store(true) + s.ddlVisibilityActivationFenced.Store(true) + return s + } + targets := map[string]struct{}{serviceID: {}, "peer": {}} + + t.Run("target address is required", func(t *testing.T) { + cluster := &ddlVisibilityTestCluster{cnServices: []metadata.CNService{ + {ServiceID: serviceID, DDLVisibilityBarrierReady: true}, + {ServiceID: "peer", DDLVisibilityBarrierReady: true}, + }} + s := newService(cluster) + err := s.waitForDDLVisibilityActivationPhase(context.Background(), targets, false) + require.ErrorContains(t, err, "has no authoritative identity") + }) + + t.Run("target list must include every barrier participant", func(t *testing.T) { + cluster := &ddlVisibilityTestCluster{cnServices: []metadata.CNService{ + {ServiceID: serviceID, QueryAddress: "self:6001", ViewMetadataAdmissionGeneration: 7, + DDLVisibilityBarrierReady: true}, + {ServiceID: "peer", QueryAddress: "peer:6001", ViewMetadataAdmissionGeneration: 8, + DDLVisibilityBarrierReady: true}, + {ServiceID: "extra", QueryAddress: "extra:6001", ViewMetadataAdmissionGeneration: 9, + DDLVisibilityBarrierReady: true}, + }} + s := newService(cluster) + err := s.waitForDDLVisibilityActivationPhase(context.Background(), targets, false) + require.ErrorContains(t, err, "omits authoritative CN") + }) +} + +func TestWaitForDDLVisibilityIngressHonorsCancellation(t *testing.T) { + const serviceID = "ddl-visibility-ingress-cancel-test" + cluster := &ddlVisibilityTestCluster{cnServices: []metadata.CNService{{ + ServiceID: serviceID, ViewMetadataAdmissionGeneration: 1, + DDLVisibilityBarrierReady: true, ViewMetadataIngressReady: true, + }}} + s := &service{ + cfg: &Config{UUID: serviceID}, moCluster: cluster, + viewMetadataAdmissionGeneration: 1, + } + ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond) + defer cancel() + err := s.waitForDDLVisibilityIngress(ctx, time.Second, false) + require.ErrorContains(t, err, "was not published") +} + +func TestWaitForDDLVisibilityBarrierPublicationHonorsCancellation(t *testing.T) { + cluster := &ddlVisibilityTestCluster{} + s := &service{ + cfg: &Config{UUID: "unpublished-cn"}, + moCluster: cluster, + viewMetadataAdmissionGeneration: 1, + } + ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond) + defer cancel() + + err := s.waitForDDLVisibilityBarrierPublication(ctx, time.Second) + require.Error(t, err) + require.Equal(t, 1, cluster.refreshCalls) +} + +func TestPeriodicHeartbeatCannotRepublishStaleIngressDuringActivation(t *testing.T) { + client := &ddlVisibilityHeartbeatOrderClient{ + firstStarted: make(chan struct{}), releaseFirst: make(chan struct{}), + } + cfg := &Config{UUID: "ddl-visibility-heartbeat-order-test"} + cfg.HAKeeper.HeatbeatTimeout.Duration = time.Second + s := &service{ + cfg: cfg, logger: zap.NewNop(), _hakeeperClient: client, + config: util.NewConfigData(nil), + } + s.ddlVisibilityBarrierReady.Store(true) + s.viewMetadataIngressReady.Store(true) + + heartbeatDone := make(chan struct{}) + go func() { + s.heartbeat(context.Background()) + close(heartbeatDone) + }() + <-client.firstStarted + activationStarted := make(chan struct{}) + activationDone := make(chan struct{}) + go func() { + close(activationStarted) + s.ddlVisibilityHeartbeatMu.Lock() + s.viewMetadataIngressReady.Store(false) + _, _ = client.SendCNHeartbeat(context.Background(), s.newCNStoreHeartbeat()) + s.ddlVisibilityHeartbeatMu.Unlock() + close(activationDone) + }() + <-activationStarted + close(client.releaseFirst) + <-heartbeatDone + <-activationDone + + client.mu.Lock() + defer client.mu.Unlock() + require.Len(t, client.completed, 2) + require.True(t, client.completed[0].ViewMetadataIngressReady) + require.False(t, client.completed[1].ViewMetadataIngressReady, + "the activation withdrawal must complete after every older true heartbeat") +} + +func TestServiceCloseWithdrawsDDLVisibilityBeforeQueryService(t *testing.T) { + state := newDDLVisibilityCloseTestService(t, nil) + + require.NoError(t, state.service.Close()) + require.False(t, state.hakeeperClient.queryClosedBeforeSend) + require.False(t, state.queryClosedBeforeRefresh) + require.Len(t, state.hakeeperClient.heartbeats, 1) + require.False(t, state.hakeeperClient.heartbeats[0].DDLVisibilityBarrierReady) + require.False(t, state.hakeeperClient.heartbeats[0].ViewMetadataIngressReady) + require.Equal(t, 1, state.cluster.refreshCalls) + require.False(t, state.cluster.cnServices[0].DDLVisibilityBarrierReady) + require.Equal(t, 1, state.hakeeperClient.closeCalls) + require.Equal(t, 2, state.lockService.closeCalls) + _, err := state.service.ddlCommitGate.Enter(context.Background()) + require.ErrorContains(t, err, "closed") + select { + case <-state.queryService.closed: + default: + t.Fatal("QueryService was not closed after authoritative barrier withdrawal") + } +} + +func TestServiceCloseContinuesAfterDDLVisibilityWithdrawalFailure(t *testing.T) { + withdrawErr := errors.New("withdraw heartbeat failed") + state := newDDLVisibilityCloseTestService(t, withdrawErr) + + err := state.service.Close() + require.ErrorIs(t, err, withdrawErr) + require.False(t, state.hakeeperClient.queryClosedBeforeSend) + require.Len(t, state.hakeeperClient.heartbeats, 1) + require.Zero(t, state.cluster.refreshCalls) + require.Equal(t, 1, state.hakeeperClient.closeCalls) + require.Equal(t, 2, state.lockService.closeCalls) + select { + case <-state.queryService.closed: + default: + t.Fatal("QueryService cleanup was skipped after barrier withdrawal failure") + } +} + +func TestWaitForDDLVisibilityBarrierWithdrawalAcceptsNewerGeneration(t *testing.T) { + const serviceID = "ddl-visibility-newer-generation-test" + cluster := &ddlVisibilityTestCluster{cnServices: []metadata.CNService{{ + ServiceID: serviceID, ViewMetadataAdmissionGeneration: 8, + DDLVisibilityBarrierReady: true, + }}} + s := &service{ + cfg: &Config{UUID: serviceID}, + moCluster: cluster, + viewMetadataAdmissionGeneration: 7, + } + + require.NoError(t, s.waitForDDLVisibilityBarrierWithdrawal(context.Background(), time.Second)) + require.Equal(t, 1, cluster.refreshCalls) +} + +func TestWaitForDDLVisibilityBarrierWithdrawalHonorsCancellation(t *testing.T) { + const serviceID = "ddl-visibility-withdrawal-timeout-test" + cluster := &ddlVisibilityTestCluster{cnServices: []metadata.CNService{{ + ServiceID: serviceID, ViewMetadataAdmissionGeneration: 7, + DDLVisibilityBarrierReady: true, + }}} + s := &service{ + cfg: &Config{UUID: serviceID}, + moCluster: cluster, + viewMetadataAdmissionGeneration: 7, + } + ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond) + defer cancel() + + err := s.waitForDDLVisibilityBarrierWithdrawal(ctx, time.Second) + require.Error(t, err) + require.Equal(t, 1, cluster.refreshCalls) +} + +func TestSyncStartupDDLVisibilityFrontierAllowsEmptyFrontier(t *testing.T) { + const serviceID = "ddl-visibility-empty-frontier-test" + cluster := &ddlVisibilityTestCluster{cnServices: []metadata.CNService{{ + ServiceID: serviceID, QueryAddress: "self:6001", + DDLVisibilityBarrierReady: true, + }}} + queryClient := &ddlVisibilityTestQueryClient{ + serviceID: serviceID, + frontiers: map[string]timestamp.Timestamp{"self:6001": {}}, + } + s := &service{ + cfg: &Config{UUID: serviceID}, moCluster: cluster, queryClient: queryClient, + _hakeeperClient: &ddlVisibilityWithdrawalHAKeeperClient{cluster: cluster}, + } + + require.NoError(t, s.syncStartupDDLVisibilityFrontier(context.Background())) + require.Empty(t, queryClient.requests) + require.Zero(t, queryClient.releases) +} diff --git a/pkg/cnservice/server_heartbeat.go b/pkg/cnservice/server_heartbeat.go index 2a9b2c01fefb0..391b37a37a1f7 100644 --- a/pkg/cnservice/server_heartbeat.go +++ b/pkg/cnservice/server_heartbeat.go @@ -22,6 +22,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/common/system" + "github.com/matrixorigin/matrixone/pkg/defines" "github.com/matrixorigin/matrixone/pkg/logservice" "github.com/matrixorigin/matrixone/pkg/logutil" logservicepb "github.com/matrixorigin/matrixone/pkg/pb/logservice" @@ -185,15 +186,7 @@ func (s *service) notifyCommandPoll() { } } -func (s *service) heartbeat(ctx context.Context) { - start := time.Now() - defer func() { - v2.CNHeartbeatHistogram.Observe(time.Since(start).Seconds()) - }() - - ctx2, cancel := context.WithTimeoutCause(ctx, s.cfg.HAKeeper.HeatbeatTimeout.Duration, moerr.CauseHeartbeat) - defer cancel() - +func (s *service) newCNStoreHeartbeat() logservicepb.CNStoreHeartbeat { hb := logservicepb.CNStoreHeartbeat{ UUID: s.cfg.UUID, ServiceAddress: s.pipelineServiceServiceAddr(), @@ -218,6 +211,15 @@ func (s *service) heartbeat(ctx context.Context) { ViewMetadataAdmissionGeneration: s.viewMetadataAdmissionGeneration, ViewMetadataCatalogFencedEpoch: s.viewMetadataCatalogFencedEpoch.Load(), ViewMetadataIngressReady: s.viewMetadataIngressReady.Load(), + DDLVisibilityBarrierReady: s.ddlVisibilityBarrierReady.Load(), + DDLVisibilityActivationPrepared: s.ddlVisibilityActivationPrepared.Load(), + DDLVisibilityActivationFenced: s.ddlVisibilityActivationFenced.Load(), + } + if s.ddlCommitGate != nil { + hb.DDLVisibilityFrontier = s.ddlCommitGate.LatestDDLFrontier() + } + if deployed := s.loadDDLVisibilityDeployedProtocol(); deployed >= defines.MORPCVersion41 { + hb.DDLVisibilityDeployedProtocol = deployed } if s.viewMetadataEpochFence != nil { hb.ViewMetadataObservedEpoch = s.viewMetadataEpochFence.Epoch() @@ -226,11 +228,28 @@ func (s *service) heartbeat(ctx context.Context) { hb.GossipAddress = s.gossipServiceAddr() hb.GossipJoined = s.gossipNode.Joined() } + return hb +} + +func (s *service) heartbeat(ctx context.Context) { + start := time.Now() + defer func() { + v2.CNHeartbeatHistogram.Observe(time.Since(start).Seconds()) + }() + + ctx2, cancel := context.WithTimeoutCause(ctx, s.cfg.HAKeeper.HeatbeatTimeout.Duration, moerr.CauseHeartbeat) + defer cancel() + // Snapshot and send readiness under the same owner as startup, activation, + // and shutdown publication. Otherwise an older in-flight true heartbeat can + // arrive after activation has authoritatively published ingress=false. + s.ddlVisibilityHeartbeatMu.Lock() + hb := s.newCNStoreHeartbeat() s.heartbeatInFlight.Store(true) s.notifyCommandPoll() cb, err := s._hakeeperClient.SendCNHeartbeat(ctx2, hb) s.heartbeatInFlight.Store(false) + s.ddlVisibilityHeartbeatMu.Unlock() if err != nil { s.commandPollNeeded.Store(true) s.notifyCommandPoll() diff --git a/pkg/cnservice/server_query.go b/pkg/cnservice/server_query.go index fd904ccf551f6..7f69eba0aa484 100644 --- a/pkg/cnservice/server_query.go +++ b/pkg/cnservice/server_query.go @@ -140,6 +140,11 @@ func (s *service) initQueryService() error { } func (s *service) initQueryCommandHandler() { + // Override queryservice's generic runtime-only setter so a live CN can + // execute the v36 DDL visibility activation fence before acknowledging the + // protocol transition. + s.addQueryCommandHandler(query.CmdMethod_GetProtocolVersion, s.handleGetProtocolVersion) + s.addQueryCommandHandler(query.CmdMethod_SetProtocolVersion, s.handleSetProtocolVersion) s.addQueryCommandHandler(query.CmdMethod_KillConn, s.handleKillConn) s.addQueryCommandHandler(query.CmdMethod_AlterAccount, s.handleAlterAccount) s.addQueryCommandHandler(query.CmdMethod_TraceSpan, s.handleTraceSpan) @@ -147,6 +152,7 @@ func (s *service) initQueryCommandHandler() { s.addQueryCommandHandler(query.CmdMethod_GetTxnInfo, s.handleGetTxnInfo) s.addQueryCommandHandler(query.CmdMethod_GetCacheInfo, s.handleGetCacheInfo) s.addQueryCommandHandler(query.CmdMethod_SyncCommit, s.handleSyncCommit) + s.addQueryCommandHandler(query.CmdMethod_SyncCommitV2, s.handleSyncCommit) s.addQueryCommandHandler(query.CmdMethod_GetCommit, s.handleGetCommit) s.addQueryCommandHandler(query.CmdMethod_ShowProcessList, s.handleShowProcessList) s.addQueryCommandHandler(query.CmdMethod_RunTask, s.handleRunTask) @@ -204,6 +210,59 @@ func (s *service) closeQueryService() error { }) } +func (s *service) handleGetProtocolVersion( + ctx context.Context, + req *query.Request, + resp *query.Response, + _ *morpc.Buffer, +) error { + if req == nil || req.GetProtocolVersion == nil { + return moerr.NewInternalError(ctx, "bad request") + } + value, ok := moruntime.ServiceRuntime(s.cfg.UUID).GetGlobalVariables(moruntime.MOProtocolVersion) + if !ok { + return moerr.NewInternalError(ctx, "protocol version not found") + } + version, ok := value.(int64) + if !ok { + return moerr.NewInternalError(ctx, "invalid protocol version") + } + resp.GetProtocolVersion = &query.GetProtocolVersionResponse{ + Version: version, + DDLVisibilityActivationPrepared: s.ddlVisibilityActivationPrepared.Load(), + DDLVisibilityActivationFenced: s.ddlVisibilityActivationFenced.Load(), + } + return nil +} + +func (s *service) handleSetProtocolVersion( + ctx context.Context, + req *query.Request, + resp *query.Response, + _ *morpc.Buffer, +) error { + if req == nil || req.SetProtocolVersion == nil { + return moerr.NewInternalError(ctx, "bad request") + } + version := req.SetProtocolVersion.Version + if version >= defines.MORPCVersion41 && + len(req.SetProtocolVersion.DDLVisibilityActivationTargets) > 0 { + expectedGeneration := req.SetProtocolVersion.DDLVisibilityTargetGeneration + expectedAddress := req.SetProtocolVersion.DDLVisibilityTargetQueryAddress + localAddress := s.queryServiceServiceAddr() + if expectedGeneration == 0 || expectedGeneration != s.viewMetadataAdmissionGeneration || + expectedAddress == "" || (localAddress != "" && expectedAddress != localAddress) { + return moerr.NewInvalidStateNoCtx("stale DDL visibility activation target identity") + } + } + if err := s.setProtocolVersion( + ctx, version, req.SetProtocolVersion.DDLVisibilityActivationTargets); err != nil { + return err + } + resp.SetProtocolVersion = &query.SetProtocolVersionResponse{Version: version} + return nil +} + func (s *service) handleKillConn(ctx context.Context, req *query.Request, resp *query.Response, _ *morpc.Buffer) error { if req == nil || req.KillConnRequest == nil { return moerr.NewInternalError(ctx, "bad request") @@ -444,8 +503,23 @@ func (s *service) handleGetTxnInfo(ctx context.Context, req *query.Request, resp return nil } +const DDLVisibilitySyncCommitFaultPoint = "ddl_visibility_sync_commit_error" + func (s *service) handleSyncCommit(ctx context.Context, req *query.Request, resp *query.Response, _ *morpc.Buffer) error { - s._txnClient.SyncLatestCommitTS(req.SycnCommit.LatestCommitTS) + if _, msg, injected := fault.TriggerFault(DDLVisibilitySyncCommitFaultPoint); injected { + if msg == "" { + msg = DDLVisibilitySyncCommitFaultPoint + } + return moerr.NewInternalErrorNoCtxf("injected DDL visibility sync commit error: %s", msg) + } + targetTS := req.SycnCommit.LatestCommitTS + if _, err := s._txnClient.WaitLogTailAppliedAt(ctx, targetTS); err != nil { + return err + } + // Preserve the existing CN-based latest-commit update and BVT counter. The + // timestamp wait above makes this call non-blocking while keeping it on the + // established SyncCommit path. + s._txnClient.SyncLatestCommitTS(targetTS) return nil } diff --git a/pkg/cnservice/server_query_test.go b/pkg/cnservice/server_query_test.go index bb881375b135e..9541978cb57d9 100644 --- a/pkg/cnservice/server_query_test.go +++ b/pkg/cnservice/server_query_test.go @@ -53,6 +53,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/pb/query" "github.com/matrixorigin/matrixone/pkg/pb/statsinfo" "github.com/matrixorigin/matrixone/pkg/pb/task" + "github.com/matrixorigin/matrixone/pkg/pb/timestamp" "github.com/matrixorigin/matrixone/pkg/queryservice" "github.com/matrixorigin/matrixone/pkg/shardservice" sqlmongodb "github.com/matrixorigin/matrixone/pkg/sql/mongodb" @@ -67,6 +68,45 @@ import ( var dummyBadRequestErr = moerr.NewInternalError(context.TODO(), "bad request") var dummyErr = moerr.NewInternalError(context.TODO(), "dummy error") +func TestServiceHandleSyncCommitWaitsForAppliedLogtail(t *testing.T) { + targetTS := timestamp.Timestamp{PhysicalTime: 100, LogicalTime: 7} + + t.Run("updates latest commit after logtail is applied", func(t *testing.T) { + ctrl := gomock.NewController(t) + txnClient := mock_frontend.NewMockTxnClient(ctrl) + txnClient.EXPECT().WaitLogTailAppliedAt(gomock.Any(), targetTS). + Return(targetTS.Next(), nil) + txnClient.EXPECT().SyncLatestCommitTS(targetTS) + + s := &service{_txnClient: txnClient} + err := s.handleSyncCommit( + context.Background(), + &query.Request{SycnCommit: &query.SyncCommitRequest{LatestCommitTS: targetTS}}, + &query.Response{}, + nil, + ) + require.NoError(t, err) + }) + + t.Run("propagates cancellation without updating latest commit", func(t *testing.T) { + ctrl := gomock.NewController(t) + txnClient := mock_frontend.NewMockTxnClient(ctrl) + ctx, cancel := context.WithCancel(context.Background()) + cancel() + txnClient.EXPECT().WaitLogTailAppliedAt(ctx, targetTS). + Return(timestamp.Timestamp{}, context.Canceled) + + s := &service{_txnClient: txnClient} + err := s.handleSyncCommit( + ctx, + &query.Request{SycnCommit: &query.SyncCommitRequest{LatestCommitTS: targetTS}}, + &query.Response{}, + nil, + ) + require.ErrorIs(t, err, context.Canceled) + }) +} + func Test_service_handleISCPDrainConsumerRenewFenceOnly(t *testing.T) { exec := &iscp.ISCPTaskExecutor{} iscp.RegisterExecutorRuntime("runner-cn", exec) diff --git a/pkg/cnservice/server_view_metadata_admission.go b/pkg/cnservice/server_view_metadata_admission.go index fa7332e792af8..912ec86778c3f 100644 --- a/pkg/cnservice/server_view_metadata_admission.go +++ b/pkg/cnservice/server_view_metadata_admission.go @@ -121,6 +121,7 @@ func (s *service) revokeViewMetadataGeneration(authoritative uint64) { s.viewMetadataRevocationOnce.Do(func() { s.viewMetadataGenerationRevoked.Store(true) s.viewMetadataIngressReady.Store(false) + s.ddlVisibilityBarrierReady.Store(false) _ = s.closePipelineAdmission() s.queryWork.beginClose() runner := s.detachRevokedTaskRunner() diff --git a/pkg/cnservice/types.go b/pkg/cnservice/types.go index 7520a3c572633..07e5636498e7e 100644 --- a/pkg/cnservice/types.go +++ b/pkg/cnservice/types.go @@ -799,6 +799,18 @@ type service struct { viewMetadataCatalogFenceMu sync.Mutex viewMetadataCatalogFenceReady atomic.Bool viewMetadataIngressReady atomic.Bool + ddlVisibilityBarrierReady atomic.Bool + ddlVisibilityBarrierPrepared atomic.Bool + ddlVisibilityBarrierClosing atomic.Bool + ddlVisibilityActivationPending atomic.Bool + ddlVisibilityActivationPrepared atomic.Bool + ddlVisibilityActivationFenced atomic.Bool + ddlVisibilityActivationComplete atomic.Bool + ddlVisibilityListenersReady atomic.Bool + ddlVisibilityRestoreIngress atomic.Bool + ddlVisibilityBarrierMu sync.Mutex + ddlVisibilityHeartbeatMu sync.Mutex + ddlCommitGate *frontend.DDLCommitGate viewMetadataGenerationRevoked atomic.Bool viewMetadataRevocationOnce sync.Once // viewMetadataCloseFn is a deterministic test hook for the asynchronous diff --git a/pkg/defines/const.go b/pkg/defines/const.go index 3f69101a123ea..8574a5fd3fdc8 100644 --- a/pkg/defines/const.go +++ b/pkg/defines/const.go @@ -76,7 +76,8 @@ const ( MORPCVersion38 int64 = 38 // session temporary-table connection migration MORPCVersion39 int64 = 39 // linearizable TN-ordered logtail read barrier MORPCVersion40 int64 = 40 // PAD SPACE comparison casts and set-operation equality keys - MORPCLatestVersion = MORPCVersion40 + MORPCVersion41 int64 = 41 // cancellable cross-CN DDL visibility fence + MORPCLatestVersion = MORPCVersion41 ) // DefaultLockWaitTimeoutSeconds is shared by the frontend default and by diff --git a/pkg/frontend/cdc_dao_sync_test.go b/pkg/frontend/cdc_dao_sync_test.go index 98d940f5dfca0..07491ac83a3bc 100644 --- a/pkg/frontend/cdc_dao_sync_test.go +++ b/pkg/frontend/cdc_dao_sync_test.go @@ -80,7 +80,11 @@ func (m *mockQueryClient) Close() error { // Mock MOCluster for testing type mockMOCluster struct { - cnServices []metadata.CNService + cnServices []metadata.CNService + refreshSnapshots [][]metadata.CNService + refreshCalls int + refreshError error + refreshErrors map[int]error } func (m *mockMOCluster) GetCNService(selector clusterservice.Selector, apply func(metadata.CNService) bool) { @@ -100,13 +104,28 @@ func (m *mockMOCluster) GetAllTNServices() []metadata.TNService { } func (m *mockMOCluster) GetCNServiceWithoutWorkingState(selector clusterservice.Selector, apply func(metadata.CNService) bool) { - // Not used in syncCommitTimestamp + for _, cn := range m.cnServices { + if !apply(cn) { + break + } + } } func (m *mockMOCluster) ForceRefresh(sync bool) { // Not used in syncCommitTimestamp } +func (m *mockMOCluster) Refresh(context.Context) error { + m.refreshCalls++ + if m.refreshCalls <= len(m.refreshSnapshots) { + m.cnServices = m.refreshSnapshots[m.refreshCalls-1] + } + if err := m.refreshErrors[m.refreshCalls]; err != nil { + return err + } + return m.refreshError +} + func (m *mockMOCluster) Close() { // Not used in syncCommitTimestamp } diff --git a/pkg/frontend/ddl_commit_gate.go b/pkg/frontend/ddl_commit_gate.go new file mode 100644 index 0000000000000..3257e7dcf1728 --- /dev/null +++ b/pkg/frontend/ddl_commit_gate.go @@ -0,0 +1,219 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package frontend + +import ( + "context" + "sync" + "sync/atomic" + + "github.com/matrixorigin/matrixone/pkg/common/moerr" + moruntime "github.com/matrixorigin/matrixone/pkg/common/runtime" + "github.com/matrixorigin/matrixone/pkg/pb/timestamp" +) + +const DDLCommitGateRuntimeKey = "frontend.ddl-commit-gate" + +// DDLCommitGate gives DDL producers and live protocol activation one local +// linearization point. Enter admits a commit while Block prevents new commits +// and waits for already-admitted commits to leave. A failed activation may keep +// the gate blocked across RPC attempts; Close wakes blocked sessions during CN +// shutdown. +type DDLCommitGate struct { + mu sync.Mutex + changed chan struct{} + blocked bool + closed bool + publicDDL bool + active int + ddlFrontier atomic.Pointer[timestamp.Timestamp] + // enterBlockedHook is a deterministic test hook invoked after Enter observes + // a blocked gate and before it waits. Production leaves it nil. + enterBlockedHook func() +} + +func NewDDLCommitGate() *DDLCommitGate { + return &DDLCommitGate{changed: make(chan struct{})} +} + +func (g *DDLCommitGate) RecordDDLFrontier(ts timestamp.Timestamp) { + if ts.IsEmpty() { + return + } + candidate := ts + for { + current := g.ddlFrontier.Load() + if current != nil && current.GreaterEq(ts) { + return + } + if g.ddlFrontier.CompareAndSwap(current, &candidate) { + return + } + } +} + +func (g *DDLCommitGate) LatestDDLFrontier() timestamp.Timestamp { + if current := g.ddlFrontier.Load(); current != nil { + return *current + } + return timestamp.Timestamp{} +} + +func publicBackgroundDDLBarrierEnabled(serviceID string) bool { + value, ok := moruntime.ServiceRuntime(serviceID).GetGlobalVariables(DDLCommitGateRuntimeKey) + if !ok || value == nil { + return false + } + gate, ok := value.(*DDLCommitGate) + return ok && gate.PublicDDLEnabled() +} + +func recordDDLCommitFrontier(serviceID string, ts timestamp.Timestamp) { + value, ok := moruntime.ServiceRuntime(serviceID).GetGlobalVariables(DDLCommitGateRuntimeKey) + if !ok || value == nil { + return + } + if gate, ok := value.(*DDLCommitGate); ok { + gate.RecordDDLFrontier(ts) + } +} + +func enterDDLCommitGate(ctx context.Context, serviceID string) (func(), error) { + value, ok := moruntime.ServiceRuntime(serviceID).GetGlobalVariables(DDLCommitGateRuntimeKey) + if !ok || value == nil { + return func() {}, nil + } + gate, ok := value.(*DDLCommitGate) + if !ok { + return nil, moerr.NewInternalError(ctx, "invalid DDL commit gate") + } + return gate.Enter(ctx) +} + +// EnablePublicDDL marks that public listeners are live. Background sessions +// created on behalf of a client must participate in the cross-CN barrier after +// this point; bootstrap background DDL remains local before it. +func (g *DDLCommitGate) EnablePublicDDL() { + g.mu.Lock() + g.publicDDL = true + g.mu.Unlock() +} + +func (g *DDLCommitGate) PublicDDLEnabled() bool { + g.mu.Lock() + defer g.mu.Unlock() + // Close rejects every later Enter, but a DDL transaction admitted before + // shutdown retains the eligibility it had when public listeners were live. + return g.publicDDL +} + +func (g *DDLCommitGate) Enter(ctx context.Context) (func(), error) { + for { + g.mu.Lock() + if g.closed { + g.mu.Unlock() + return nil, moerr.NewServiceUnavailableNoCtx("DDL commit gate is closed") + } + if !g.blocked { + g.active++ + g.mu.Unlock() + var once sync.Once + return func() { + once.Do(func() { + g.mu.Lock() + g.active-- + g.signalLocked() + g.mu.Unlock() + }) + }, nil + } + changed := g.changed + hook := g.enterBlockedHook + g.mu.Unlock() + if hook != nil { + hook() + } + + select { + case <-ctx.Done(): + return nil, context.Cause(ctx) + case <-changed: + } + } +} + +func (g *DDLCommitGate) BlockNew() error { + g.mu.Lock() + defer g.mu.Unlock() + if g.closed { + return moerr.NewServiceUnavailableNoCtx("DDL commit gate is closed") + } + if !g.blocked { + g.blocked = true + g.signalLocked() + } + return nil +} + +func (g *DDLCommitGate) WaitDrained(ctx context.Context) error { + g.mu.Lock() + for g.active > 0 { + changed := g.changed + g.mu.Unlock() + select { + case <-ctx.Done(): + return context.Cause(ctx) + case <-changed: + } + g.mu.Lock() + if g.closed { + g.mu.Unlock() + return moerr.NewServiceUnavailableNoCtx("DDL commit gate is closed") + } + } + g.mu.Unlock() + return nil +} + +func (g *DDLCommitGate) Block(ctx context.Context) error { + if err := g.BlockNew(); err != nil { + return err + } + return g.WaitDrained(ctx) +} + +func (g *DDLCommitGate) Unblock() { + g.mu.Lock() + if !g.closed && g.blocked { + g.blocked = false + g.signalLocked() + } + g.mu.Unlock() +} + +func (g *DDLCommitGate) Close() { + g.mu.Lock() + if !g.closed { + g.closed = true + g.blocked = true + g.signalLocked() + } + g.mu.Unlock() +} + +func (g *DDLCommitGate) signalLocked() { + close(g.changed) + g.changed = make(chan struct{}) +} diff --git a/pkg/frontend/ddl_commit_gate_test.go b/pkg/frontend/ddl_commit_gate_test.go new file mode 100644 index 0000000000000..d2a57edcf8eb4 --- /dev/null +++ b/pkg/frontend/ddl_commit_gate_test.go @@ -0,0 +1,98 @@ +// Copyright 2026 Matrix Origin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package frontend + +import ( + "context" + "testing" + + "github.com/matrixorigin/matrixone/pkg/pb/timestamp" + "github.com/stretchr/testify/require" +) + +func TestDDLCommitGateLinearizesBlockAndCommit(t *testing.T) { + gate := NewDDLCommitGate() + releaseFirst, err := gate.Enter(context.Background()) + require.NoError(t, err) + + gate.mu.Lock() + blockStarted := gate.changed + gate.mu.Unlock() + blockDone := make(chan error, 1) + go func() { blockDone <- gate.Block(context.Background()) }() + <-blockStarted // Block has published blocked=true. + select { + case err := <-blockDone: + t.Fatalf("block returned before the admitted DDL left: %v", err) + default: + } + releaseFirst() + require.NoError(t, <-blockDone) + + enterDone := make(chan error, 1) + go func() { + release, enterErr := gate.Enter(context.Background()) + if enterErr == nil { + release() + } + enterDone <- enterErr + }() + gate.mu.Lock() + require.True(t, gate.blocked) + require.Zero(t, gate.active) + gate.mu.Unlock() + select { + case err := <-enterDone: + t.Fatalf("DDL entered while activation held the gate: %v", err) + default: + } + gate.Unblock() + require.NoError(t, <-enterDone) +} + +func TestDDLCommitGateTracksOnlyMonotonicDDLFrontier(t *testing.T) { + gate := NewDDLCommitGate() + older := timestamp.Timestamp{PhysicalTime: 100} + newer := timestamp.Timestamp{PhysicalTime: 200} + + require.True(t, gate.LatestDDLFrontier().IsEmpty()) + gate.RecordDDLFrontier(newer) + gate.RecordDDLFrontier(older) + require.Equal(t, newer, gate.LatestDDLFrontier()) +} + +func TestDDLCommitGateCancellationAndClose(t *testing.T) { + gate := NewDDLCommitGate() + gate.EnablePublicDDL() + release, err := gate.Enter(context.Background()) + require.NoError(t, err) + ctx, cancel := context.WithCancel(context.Background()) + cancel() + err = gate.Block(ctx) + require.ErrorIs(t, err, context.Canceled) + release() + require.NoError(t, gate.Block(context.Background()), + "a canceled activation must retain the producer fence for a safe retry") + _, err = gate.Enter(ctx) + require.ErrorIs(t, err, context.Canceled) + + gate.Close() + require.True(t, gate.PublicDDLEnabled(), + "an admitted public background DDL retains shutdown fan-out eligibility") + _, err = gate.Enter(context.Background()) + require.ErrorContains(t, err, "closed") + err = gate.Block(context.Background()) + require.ErrorContains(t, err, "closed") +} diff --git a/pkg/frontend/txn.go b/pkg/frontend/txn.go index 85c15c787dbe4..db09c40da0f5f 100644 --- a/pkg/frontend/txn.go +++ b/pkg/frontend/txn.go @@ -17,6 +17,8 @@ package frontend import ( "context" "errors" + "maps" + "sort" "strings" "sync" "time" @@ -24,9 +26,13 @@ import ( "github.com/google/uuid" "go.uber.org/zap" + "github.com/matrixorigin/matrixone/pkg/clusterservice" "github.com/matrixorigin/matrixone/pkg/common/moerr" moruntime "github.com/matrixorigin/matrixone/pkg/common/runtime" "github.com/matrixorigin/matrixone/pkg/defines" + "github.com/matrixorigin/matrixone/pkg/pb/metadata" + querypb "github.com/matrixorigin/matrixone/pkg/pb/query" + "github.com/matrixorigin/matrixone/pkg/pb/timestamp" pbtxn "github.com/matrixorigin/matrixone/pkg/pb/txn" "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" txnclient "github.com/matrixorigin/matrixone/pkg/txn/client" @@ -968,9 +974,23 @@ func (th *TxnHandler) commitUnsafe(execCtx *ExecCtx) error { if th.txnOp != nil { execCtx.ses.EnterFPrint(FPCommitUnsafeBeforeCommitWithTxn) defer execCtx.ses.ExitFPrint(FPCommitUnsafeBeforeCommitWithTxn) - commitTs := th.txnOp.Txn().CommitTS + txnMeta := th.txnOp.Txn() + commitTs := txnMeta.CommitTS + visibilityTS := timestamp.Timestamp{} + if !txnMeta.SnapshotTS.IsEmpty() { + // SnapshotTS is exclusive. A no-op DDL observed catalog state through + // SnapshotTS.Prev(), which is the frontier other CNs must also apply. + visibilityTS = txnMeta.SnapshotTS.Prev() + } tempTxnKey := tempTableTxnKey(th.txnOp) haveDDL := th.txnOp.GetWorkspace().GetHaveDDL() + if haveDDL { + releaseDDLCommit, gateErr := enterDDLCommitGate(ctx2, execCtx.ses.GetService()) + if gateErr != nil { + return gateErr + } + defer releaseDDLCommit() + } execCtx.ses.SetTxnId(th.txnOp.Txn().ID) commitResultUnknown := false err, hasRecovered = ExecuteFuncWithRecover(func() error { @@ -983,6 +1003,9 @@ func (th *TxnHandler) commitUnsafe(execCtx *ExecCtx) error { // left in a state that is unsafe to inspect. if !hasRecovered { commitTs = th.txnOp.Txn().CommitTS + if !commitTs.IsEmpty() { + visibilityTS = commitTs + } } if err != nil { err = moerr.AttachCause(ctx2, err) @@ -1014,6 +1037,9 @@ func (th *TxnHandler) commitUnsafe(execCtx *ExecCtx) error { owner.rollbackTempTableTransaction(tempTxnKey) } } + if haveDDL && !commitTs.IsEmpty() && (err == nil || commitResultUnknown) { + recordDDLCommitFrontier(execCtx.ses.GetService(), commitTs) + } if commitResultUnknown { // ErrTxnUnknown is terminal for this frontend handle. The operator // has already finalized its workspace non-destructively; retaining it @@ -1022,12 +1048,195 @@ func (th *TxnHandler) commitUnsafe(execCtx *ExecCtx) error { execCtx.ses.SetTxnId(dumpUUID[:]) return err } + if err == nil && haveDDL && !visibilityTS.IsEmpty() && + (execCtx.ses.GetFromRealUser() || + publicBackgroundDDLBarrierEnabled(execCtx.ses.GetService())) { + // A fresh proxy connection has no session commit timestamp to carry + // across CNs. Do not acknowledge a client DDL until every working CN + // has reached the commit, or the observed catalog frontier for a no-op. + err = syncDDLCommitToBarrierReadyCNs(ctx2, execCtx.ses.GetService(), visibilityTS) + } } th.invalidateTxnUnsafe() execCtx.ses.SetTxnId(dumpUUID[:]) return err } +type ddlVisibilityTarget struct { + serviceID string + generation uint64 + address string +} + +// syncDDLCommitToBarrierReadyCNs closes both sides of CN admission. A CN publishes +// barrier readiness before its startup frontier fence and public ingress. The +// DDL sender refreshes membership again after fan-out and repeats if any ready +// generation changed. A failed target is retried only after authoritative +// revalidation proves that exact generation/address tuple has departed. +// Protocol v41 is a deployment gate: mixed-version +// clusters retain legacy behavior without invoking an old receiver's fatal +// SyncCommit path. +func syncDDLCommitToBarrierReadyCNs( + ctx context.Context, + service string, + visibilityTS timestamp.Timestamp, +) error { + pu := getPuIfPresent(service) + if pu == nil || pu.QueryClient == nil { + return nil + } + + qc := pu.QueryClient + protocol, ok := moruntime.ServiceRuntime(qc.ServiceID()).GetGlobalVariables(moruntime.MOProtocolVersion) + protocolVersion, valid := protocol.(int64) + if !ok || !valid || protocolVersion < defines.MORPCVersion41 { + return nil + } + cluster := clusterservice.GetMOCluster(qc.ServiceID()) + if cluster == nil { + return moerr.NewInternalError(ctx, "cannot establish DDL visibility barrier: cluster service is unavailable") + } + refresher, ok := cluster.(clusterservice.AuthoritativeRefresher) + if !ok { + return moerr.NewInternalError(ctx, "cannot establish DDL visibility barrier: authoritative refresh is unavailable") + } + + for { + if err := refresher.Refresh(ctx); err != nil { + return errors.Join( + err, + moerr.NewInternalError(ctx, "failed to refresh CNs for DDL visibility barrier"), + ) + } + targets, err := ddlVisibilityTargets(ctx, cluster) + if err != nil { + return err + } + if len(targets) == 0 { + return moerr.NewInternalError(ctx, "cannot establish DDL visibility barrier: no barrier-ready CN is available") + } + + ordered := make([]ddlVisibilityTarget, 0, len(targets)) + for _, target := range targets { + ordered = append(ordered, target) + } + sort.Slice(ordered, func(i, j int) bool { return ordered[i].serviceID < ordered[j].serviceID }) + retryFanout := false + for _, target := range ordered { + req := qc.NewRequest(querypb.CmdMethod_SyncCommitV2) + req.SycnCommit = &querypb.SyncCommitRequest{LatestCommitTS: visibilityTS} + resp, sendErr := qc.SendMessage(ctx, target.address, req) + if sendErr != nil { + failure := errors.Join( + sendErr, + moerr.NewInternalErrorf(ctx, "failed to apply DDL commit on CN %s", target.address), + ) + retryFanout, err = revalidateDDLVisibilityTargetFailure( + ctx, refresher, cluster, target, failure) + if err != nil { + return err + } + break + } + if resp == nil { + failure := moerr.NewInternalErrorf( + ctx, "empty DDL visibility response from CN %s", target.address) + retryFanout, err = revalidateDDLVisibilityTargetFailure( + ctx, refresher, cluster, target, failure) + if err != nil { + return err + } + break + } + qc.Release(resp) + } + if retryFanout { + continue + } + + if err := refresher.Refresh(ctx); err != nil { + return errors.Join( + err, + moerr.NewInternalError(ctx, "failed to verify CNs for DDL visibility barrier"), + ) + } + verified, err := ddlVisibilityTargets(ctx, cluster) + if err != nil { + return err + } + if maps.Equal(targets, verified) { + return nil + } + } +} + +// revalidateDDLVisibilityTargetFailure distinguishes a graceful target +// withdrawal/replacement from a failure of a still-required barrier member. +// Unknown membership never becomes success: refresh and inventory errors are +// joined with the original target failure. +func revalidateDDLVisibilityTargetFailure( + ctx context.Context, + refresher clusterservice.AuthoritativeRefresher, + cluster clusterservice.MOCluster, + failed ddlVisibilityTarget, + failure error, +) (bool, error) { + if err := refresher.Refresh(ctx); err != nil { + return false, errors.Join( + failure, + err, + moerr.NewInternalError(ctx, "failed to revalidate CN after DDL visibility target failure"), + ) + } + targets, err := ddlVisibilityTargets(ctx, cluster) + if err != nil { + return false, errors.Join(failure, err) + } + current, ok := targets[failed.serviceID] + if ok && current == failed { + return false, failure + } + return true, nil +} + +func ddlVisibilityTargets( + ctx context.Context, + cluster clusterservice.MOCluster, +) (map[string]ddlVisibilityTarget, error) { + targets := make(map[string]ddlVisibilityTarget) + missingAddress := "" + err := clusterservice.GetCNServiceRawWithContext( + ctx, + cluster, + clusterservice.NewSelector(), + func(cn metadata.CNService) bool { + if !cn.DDLVisibilityBarrierReady { + return true + } + if cn.QueryAddress == "" { + missingAddress = cn.ServiceID + return false + } + targets[cn.ServiceID] = ddlVisibilityTarget{ + serviceID: cn.ServiceID, + generation: cn.ViewMetadataAdmissionGeneration, + address: cn.QueryAddress, + } + return true + }) + if err != nil { + return nil, err + } + if missingAddress != "" { + return nil, moerr.NewInternalErrorf( + ctx, + "cannot establish DDL visibility barrier: barrier-ready CN %s has no query address", + missingAddress, + ) + } + return targets, nil +} + // Rollback rolls back the txn // the option bits decide the actual behavior func (th *TxnHandler) Rollback(execCtx *ExecCtx) error { diff --git a/pkg/frontend/txn_test.go b/pkg/frontend/txn_test.go index 4002883302703..a80574b73f855 100644 --- a/pkg/frontend/txn_test.go +++ b/pkg/frontend/txn_test.go @@ -28,12 +28,15 @@ import ( "github.com/smartystreets/goconvey/convey" "github.com/stretchr/testify/require" + "github.com/matrixorigin/matrixone/pkg/clusterservice" "github.com/matrixorigin/matrixone/pkg/common/moerr" moruntime "github.com/matrixorigin/matrixone/pkg/common/runtime" "github.com/matrixorigin/matrixone/pkg/defines" "github.com/matrixorigin/matrixone/pkg/frontend/databranchutils" mock_frontend "github.com/matrixorigin/matrixone/pkg/frontend/test" "github.com/matrixorigin/matrixone/pkg/pb/lock" + "github.com/matrixorigin/matrixone/pkg/pb/metadata" + querypb "github.com/matrixorigin/matrixone/pkg/pb/query" "github.com/matrixorigin/matrixone/pkg/pb/timestamp" "github.com/matrixorigin/matrixone/pkg/pb/txn" "github.com/matrixorigin/matrixone/pkg/sql/parsers/tree" @@ -856,6 +859,468 @@ func TestCommitUsesFinalCommitTSForNextTxn(t *testing.T) { } } +type ddlSyncRequest struct { + address string + method querypb.CmdMethod + commitTS timestamp.Timestamp +} + +type ddlSyncQueryClient struct { + serviceID string + sendError map[string]error + nilResponse map[string]bool + requests []ddlSyncRequest + releases int +} + +func (c *ddlSyncQueryClient) ServiceID() string { + return c.serviceID +} + +func (c *ddlSyncQueryClient) SendMessage( + _ context.Context, + address string, + req *querypb.Request, +) (*querypb.Response, error) { + request := ddlSyncRequest{address: address, method: req.CmdMethod} + if req.SycnCommit != nil { + request.commitTS = req.SycnCommit.LatestCommitTS + } + c.requests = append(c.requests, request) + if err := c.sendError[address]; err != nil { + return nil, err + } + if c.nilResponse[address] { + return nil, nil + } + return &querypb.Response{}, nil +} + +func (c *ddlSyncQueryClient) NewRequest(method querypb.CmdMethod) *querypb.Request { + return &querypb.Request{CmdMethod: method} +} + +func (c *ddlSyncQueryClient) Release(*querypb.Response) { + c.releases++ +} + +func (c *ddlSyncQueryClient) Close() error { + return nil +} + +func TestCommitSyncsDDLCommitToBarrierReadyCNs(t *testing.T) { + const queryServiceID = "ddl-sync-commit-test" + commitTS := timestamp.Timestamp{PhysicalTime: 100, LogicalTime: 7} + snapshotTS := timestamp.Timestamp{PhysicalTime: 90, LogicalTime: 5} + targets := []metadata.CNService{ + { + ServiceID: "cn-1", QueryAddress: "cn-1:6001", + ViewMetadataAdmissionGeneration: 1, DDLVisibilityBarrierReady: true, + }, + { + ServiceID: "cn-2", QueryAddress: "cn-2:6001", + ViewMetadataAdmissionGeneration: 1, DDLVisibilityBarrierReady: true, + }, + } + + newState := func(t *testing.T) (*Session, *testTxnOp, *ddlSyncQueryClient, *ExecCtx) { + t.Helper() + ctrl := gomock.NewController(t) + ctx := defines.AttachAccountId(context.Background(), sysAccountID) + ses := newTestSession(t, ctrl) + ses.SetFromRealUser(true) + eng := mock_frontend.NewMockEngine(ctrl) + eng.EXPECT().Hints().Return(engine.Hints{CommitOrRollbackTimeout: time.Second}).AnyTimes() + ses.txnHandler.storage = eng + + op := newTestTxnOp() + op.meta = txn.TxnMeta{ + ID: []byte{1, 2, 3, 4}, + Status: txn.TxnStatus_Active, + SnapshotTS: snapshotTS, + } + op.commitTS = commitTS + op.wp.SetHaveDDL(true) + ses.txnHandler.txnOp = op + ses.txnHandler.txnCtx = ctx + + qc := &ddlSyncQueryClient{ + serviceID: queryServiceID, + sendError: make(map[string]error), + nilResponse: make(map[string]bool), + } + pu := getPu("") + previousQueryClient := pu.QueryClient + pu.QueryClient = qc + t.Cleanup(func() { pu.QueryClient = previousQueryClient }) + moruntime.SetupServiceBasedRuntime(queryServiceID, moruntime.DefaultRuntime()) + moruntime.ServiceRuntime(queryServiceID).SetGlobalVariables( + moruntime.ClusterService, + &mockMOCluster{cnServices: targets}, + ) + + execCtx := newTestExecCtx(ctx, ctrl) + execCtx.ses = ses + execCtx.stmt = &tree.CreateTable{} + execCtx.txnOpt = FeTxnOption{autoCommit: true} + return ses, op, qc, execCtx + } + + t.Run("waits for every stable barrier-ready CN", func(t *testing.T) { + ses, op, qc, execCtx := newState(t) + defer ses.Close() + defer execCtx.Close() + + require.NoError(t, ses.GetTxnHandler().Commit(execCtx)) + require.Equal(t, []ddlSyncRequest{ + {address: "cn-1:6001", method: querypb.CmdMethod_SyncCommitV2, commitTS: commitTS}, + {address: "cn-2:6001", method: querypb.CmdMethod_SyncCommitV2, commitTS: commitTS}, + }, qc.requests) + require.Equal(t, 2, qc.releases) + cluster := clusterservice.GetMOCluster(queryServiceID).(*mockMOCluster) + require.Equal(t, 2, cluster.refreshCalls) + require.Equal(t, txn.TxnStatus_Committed, op.meta.Status) + require.Zero(t, op.rollbackCalls) + require.Nil(t, ses.GetTxnHandler().GetTxn()) + }) + + t.Run("activation gate drains and blocks concurrent DDL commit", func(t *testing.T) { + ses, op, _, execCtx := newState(t) + defer ses.Close() + defer execCtx.Close() + gate := NewDDLCommitGate() + require.NoError(t, gate.Block(context.Background())) + var once sync.Once + commitWaiting := make(chan struct{}) + gate.enterBlockedHook = func() { once.Do(func() { close(commitWaiting) }) } + rt := moruntime.ServiceRuntime(ses.GetService()) + previousGate, hadPreviousGate := rt.GetGlobalVariables(DDLCommitGateRuntimeKey) + rt.SetGlobalVariables(DDLCommitGateRuntimeKey, gate) + t.Cleanup(func() { + if hadPreviousGate { + rt.SetGlobalVariables(DDLCommitGateRuntimeKey, previousGate) + } else { + rt.SetGlobalVariables(DDLCommitGateRuntimeKey, nil) + } + }) + + commitDone := make(chan error, 1) + go func() { commitDone <- ses.GetTxnHandler().Commit(execCtx) }() + <-commitWaiting + require.Zero(t, op.commitCalls, + "DDL commit must not cross the activation linearization point") + gate.Unblock() + require.NoError(t, <-commitDone) + require.Equal(t, 1, op.commitCalls) + }) + + t.Run("retries when a CN becomes barrier-ready during fan-out", func(t *testing.T) { + ses, op, qc, execCtx := newState(t) + defer ses.Close() + defer execCtx.Close() + cluster := clusterservice.GetMOCluster(queryServiceID).(*mockMOCluster) + expanded := append([]metadata.CNService{}, targets...) + expanded = append(expanded, metadata.CNService{ + ServiceID: "cn-3", QueryAddress: "cn-3:6001", + ViewMetadataAdmissionGeneration: 7, DDLVisibilityBarrierReady: true, + }) + cluster.refreshSnapshots = [][]metadata.CNService{targets, expanded, expanded, expanded} + + require.NoError(t, ses.GetTxnHandler().Commit(execCtx)) + require.Equal(t, []ddlSyncRequest{ + {address: "cn-1:6001", method: querypb.CmdMethod_SyncCommitV2, commitTS: commitTS}, + {address: "cn-2:6001", method: querypb.CmdMethod_SyncCommitV2, commitTS: commitTS}, + {address: "cn-1:6001", method: querypb.CmdMethod_SyncCommitV2, commitTS: commitTS}, + {address: "cn-2:6001", method: querypb.CmdMethod_SyncCommitV2, commitTS: commitTS}, + {address: "cn-3:6001", method: querypb.CmdMethod_SyncCommitV2, commitTS: commitTS}, + }, qc.requests) + require.Equal(t, 5, qc.releases) + require.Equal(t, 4, cluster.refreshCalls) + require.Equal(t, txn.TxnStatus_Committed, op.meta.Status) + require.Nil(t, ses.GetTxnHandler().GetTxn()) + }) + + t.Run("retries when a barrier-ready CN generation is replaced", func(t *testing.T) { + ses, op, qc, execCtx := newState(t) + defer ses.Close() + defer execCtx.Close() + cluster := clusterservice.GetMOCluster(queryServiceID).(*mockMOCluster) + replaced := append([]metadata.CNService{}, targets...) + replaced[1].ViewMetadataAdmissionGeneration = 2 + replaced[1].QueryAddress = "cn-2-new:6001" + cluster.refreshSnapshots = [][]metadata.CNService{targets, replaced, replaced, replaced} + + require.NoError(t, ses.GetTxnHandler().Commit(execCtx)) + require.Equal(t, []ddlSyncRequest{ + {address: "cn-1:6001", method: querypb.CmdMethod_SyncCommitV2, commitTS: commitTS}, + {address: "cn-2:6001", method: querypb.CmdMethod_SyncCommitV2, commitTS: commitTS}, + {address: "cn-1:6001", method: querypb.CmdMethod_SyncCommitV2, commitTS: commitTS}, + {address: "cn-2-new:6001", method: querypb.CmdMethod_SyncCommitV2, commitTS: commitTS}, + }, qc.requests) + require.Equal(t, 4, qc.releases) + require.Equal(t, 4, cluster.refreshCalls) + require.Equal(t, txn.TxnStatus_Committed, op.meta.Status) + require.Nil(t, ses.GetTxnHandler().GetTxn()) + }) + + t.Run("retries when a failed target withdrew after the membership snapshot", func(t *testing.T) { + ses, op, qc, execCtx := newState(t) + defer ses.Close() + defer execCtx.Close() + sendErr := moerr.NewInternalErrorNoCtx("closing CN unavailable") + qc.sendError["cn-2:6001"] = sendErr + cluster := clusterservice.GetMOCluster(queryServiceID).(*mockMOCluster) + remaining := append([]metadata.CNService{}, targets[:1]...) + cluster.refreshSnapshots = [][]metadata.CNService{targets, remaining, remaining, remaining} + + require.NoError(t, ses.GetTxnHandler().Commit(execCtx)) + require.Equal(t, []ddlSyncRequest{ + {address: "cn-1:6001", method: querypb.CmdMethod_SyncCommitV2, commitTS: commitTS}, + {address: "cn-2:6001", method: querypb.CmdMethod_SyncCommitV2, commitTS: commitTS}, + {address: "cn-1:6001", method: querypb.CmdMethod_SyncCommitV2, commitTS: commitTS}, + }, qc.requests) + require.Equal(t, 2, qc.releases) + require.Equal(t, 4, cluster.refreshCalls) + require.Equal(t, txn.TxnStatus_Committed, op.meta.Status) + require.Zero(t, op.rollbackCalls) + require.Nil(t, ses.GetTxnHandler().GetTxn()) + }) + + t.Run("retries when a failed target tuple is replaced", func(t *testing.T) { + ses, op, qc, execCtx := newState(t) + defer ses.Close() + defer execCtx.Close() + sendErr := moerr.NewInternalErrorNoCtx("replaced CN unavailable") + qc.sendError["cn-2:6001"] = sendErr + cluster := clusterservice.GetMOCluster(queryServiceID).(*mockMOCluster) + replaced := append([]metadata.CNService{}, targets...) + replaced[1].ViewMetadataAdmissionGeneration = 2 + replaced[1].QueryAddress = "cn-2-new:6001" + cluster.refreshSnapshots = [][]metadata.CNService{targets, replaced, replaced, replaced} + + require.NoError(t, ses.GetTxnHandler().Commit(execCtx)) + require.Equal(t, []ddlSyncRequest{ + {address: "cn-1:6001", method: querypb.CmdMethod_SyncCommitV2, commitTS: commitTS}, + {address: "cn-2:6001", method: querypb.CmdMethod_SyncCommitV2, commitTS: commitTS}, + {address: "cn-1:6001", method: querypb.CmdMethod_SyncCommitV2, commitTS: commitTS}, + {address: "cn-2-new:6001", method: querypb.CmdMethod_SyncCommitV2, commitTS: commitTS}, + }, qc.requests) + require.Equal(t, 3, qc.releases) + require.Equal(t, 4, cluster.refreshCalls) + require.Equal(t, txn.TxnStatus_Committed, op.meta.Status) + require.Zero(t, op.rollbackCalls) + require.Nil(t, ses.GetTxnHandler().GetTxn()) + }) + + t.Run("mixed-version cluster does not call a legacy receiver", func(t *testing.T) { + ses, op, qc, execCtx := newState(t) + defer ses.Close() + defer execCtx.Close() + moruntime.ServiceRuntime(queryServiceID).SetGlobalVariables( + moruntime.MOProtocolVersion, + defines.MORPCVersion34, + ) + + require.NoError(t, ses.GetTxnHandler().Commit(execCtx)) + require.Empty(t, qc.requests) + require.Zero(t, qc.releases) + cluster := clusterservice.GetMOCluster(queryServiceID).(*mockMOCluster) + require.Zero(t, cluster.refreshCalls) + require.Equal(t, txn.TxnStatus_Committed, op.meta.Status) + require.Nil(t, ses.GetTxnHandler().GetTxn()) + }) + + t.Run("does not acknowledge a membership refresh failure", func(t *testing.T) { + ses, op, qc, execCtx := newState(t) + defer ses.Close() + defer execCtx.Close() + refreshErr := moerr.NewInternalErrorNoCtx("HAKeeper unavailable") + cluster := clusterservice.GetMOCluster(queryServiceID).(*mockMOCluster) + cluster.refreshError = refreshErr + + err := ses.GetTxnHandler().Commit(execCtx) + require.ErrorIs(t, err, refreshErr) + require.Equal(t, 1, cluster.refreshCalls) + require.Empty(t, qc.requests) + require.Equal(t, txn.TxnStatus_Committed, op.meta.Status) + require.Zero(t, op.rollbackCalls) + require.Nil(t, ses.GetTxnHandler().GetTxn()) + }) + + t.Run("does not acknowledge a partial barrier", func(t *testing.T) { + ses, op, qc, execCtx := newState(t) + defer ses.Close() + defer execCtx.Close() + sendErr := moerr.NewInternalErrorNoCtx("CN unavailable") + qc.sendError["cn-2:6001"] = sendErr + + err := ses.GetTxnHandler().Commit(execCtx) + require.ErrorIs(t, err, sendErr) + require.Len(t, qc.requests, 2) + require.Equal(t, 1, qc.releases) + cluster := clusterservice.GetMOCluster(queryServiceID).(*mockMOCluster) + require.Equal(t, 2, cluster.refreshCalls) + require.Equal(t, txn.TxnStatus_Committed, op.meta.Status) + require.Zero(t, op.rollbackCalls) + require.Nil(t, ses.GetTxnHandler().GetTxn()) + }) + + t.Run("does not retry when failure revalidation cannot refresh membership", func(t *testing.T) { + ses, op, qc, execCtx := newState(t) + defer ses.Close() + defer execCtx.Close() + sendErr := moerr.NewInternalErrorNoCtx("CN unavailable") + refreshErr := moerr.NewInternalErrorNoCtx("HAKeeper unavailable after send failure") + qc.sendError["cn-2:6001"] = sendErr + cluster := clusterservice.GetMOCluster(queryServiceID).(*mockMOCluster) + cluster.refreshErrors = map[int]error{2: refreshErr} + + err := ses.GetTxnHandler().Commit(execCtx) + require.ErrorIs(t, err, sendErr) + require.ErrorIs(t, err, refreshErr) + require.Len(t, qc.requests, 2) + require.Equal(t, 1, qc.releases) + require.Equal(t, 2, cluster.refreshCalls) + require.Equal(t, txn.TxnStatus_Committed, op.meta.Status) + require.Zero(t, op.rollbackCalls) + require.Nil(t, ses.GetTxnHandler().GetTxn()) + }) + + t.Run("rejects an empty CN response", func(t *testing.T) { + ses, op, qc, execCtx := newState(t) + defer ses.Close() + defer execCtx.Close() + qc.nilResponse["cn-1:6001"] = true + + err := ses.GetTxnHandler().Commit(execCtx) + require.ErrorContains(t, err, "empty DDL visibility response") + require.Len(t, qc.requests, 1) + require.Zero(t, qc.releases) + cluster := clusterservice.GetMOCluster(queryServiceID).(*mockMOCluster) + require.Equal(t, 2, cluster.refreshCalls) + require.Equal(t, txn.TxnStatus_Committed, op.meta.Status) + require.Nil(t, ses.GetTxnHandler().GetTxn()) + }) + + t.Run("rejects an empty barrier-ready CN set", func(t *testing.T) { + ses, op, qc, execCtx := newState(t) + defer ses.Close() + defer execCtx.Close() + moruntime.ServiceRuntime(queryServiceID).SetGlobalVariables( + moruntime.ClusterService, + &mockMOCluster{}, + ) + + err := ses.GetTxnHandler().Commit(execCtx) + require.ErrorContains(t, err, "no barrier-ready CN is available") + require.Empty(t, qc.requests) + require.Equal(t, txn.TxnStatus_Committed, op.meta.Status) + require.Nil(t, ses.GetTxnHandler().GetTxn()) + }) + + t.Run("rejects a barrier-ready CN without query address", func(t *testing.T) { + ses, op, qc, execCtx := newState(t) + defer ses.Close() + defer execCtx.Close() + moruntime.ServiceRuntime(queryServiceID).SetGlobalVariables( + moruntime.ClusterService, + &mockMOCluster{cnServices: []metadata.CNService{{ + ServiceID: "cn-missing-address", DDLVisibilityBarrierReady: true, + }}}, + ) + + err := ses.GetTxnHandler().Commit(execCtx) + require.ErrorContains(t, err, "barrier-ready CN cn-missing-address has no query address") + require.Empty(t, qc.requests) + require.Equal(t, txn.TxnStatus_Committed, op.meta.Status) + require.Nil(t, ses.GetTxnHandler().GetTxn()) + }) + + t.Run("synchronizes the observed frontier for a DDL no-op", func(t *testing.T) { + ses, op, qc, execCtx := newState(t) + defer ses.Close() + defer execCtx.Close() + op.commitTS = timestamp.Timestamp{} + + require.NoError(t, ses.GetTxnHandler().Commit(execCtx)) + require.Equal(t, []ddlSyncRequest{ + {address: "cn-1:6001", method: querypb.CmdMethod_SyncCommitV2, commitTS: snapshotTS.Prev()}, + {address: "cn-2:6001", method: querypb.CmdMethod_SyncCommitV2, commitTS: snapshotTS.Prev()}, + }, qc.requests) + require.Equal(t, 2, qc.releases) + require.Equal(t, txn.TxnStatus_Committed, op.meta.Status) + require.Nil(t, ses.GetTxnHandler().GetTxn()) + }) + + t.Run("live client-owned background DDL participates in barrier", func(t *testing.T) { + ses, op, qc, execCtx := newState(t) + defer ses.Close() + defer execCtx.Close() + ses.SetFromRealUser(false) + gate := NewDDLCommitGate() + gate.EnablePublicDDL() + rt := moruntime.ServiceRuntime(ses.GetService()) + previousGate, hadPreviousGate := rt.GetGlobalVariables(DDLCommitGateRuntimeKey) + rt.SetGlobalVariables(DDLCommitGateRuntimeKey, gate) + t.Cleanup(func() { + if hadPreviousGate { + rt.SetGlobalVariables(DDLCommitGateRuntimeKey, previousGate) + } else { + rt.SetGlobalVariables(DDLCommitGateRuntimeKey, nil) + } + }) + + commitEntered := make(chan struct{}) + allowCommit := make(chan struct{}) + op.commitHook = func() { + close(commitEntered) + <-allowCommit + } + commitDone := make(chan error, 1) + go func() { commitDone <- ses.GetTxnHandler().Commit(execCtx) }() + <-commitEntered + gate.Close() + close(allowCommit) + require.NoError(t, <-commitDone) + require.Equal(t, []ddlSyncRequest{ + {address: "cn-1:6001", method: querypb.CmdMethod_SyncCommitV2, commitTS: commitTS}, + {address: "cn-2:6001", method: querypb.CmdMethod_SyncCommitV2, commitTS: commitTS}, + }, qc.requests) + require.Equal(t, 2, qc.releases) + require.Equal(t, txn.TxnStatus_Committed, op.meta.Status) + }) + + tests := []struct { + name string + configure func(*Session, *testTxnOp) + }{ + { + name: "non-DDL transaction", + configure: func(_ *Session, op *testTxnOp) { + op.wp.SetHaveDDL(false) + }, + }, + { + name: "pre-ingress internal session", + configure: func(ses *Session, _ *testTxnOp) { + ses.SetFromRealUser(false) + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + ses, _, qc, execCtx := newState(t) + defer ses.Close() + defer execCtx.Close() + test.configure(ses, ses.txnHandler.txnOp.(*testTxnOp)) + + require.NoError(t, ses.GetTxnHandler().Commit(execCtx)) + require.Empty(t, qc.requests) + require.Zero(t, qc.releases) + }) + } +} + func TestCreateUnsafeRollsBackPublishedGenerationOnPanic(t *testing.T) { ctrl := gomock.NewController(t) ctx := defines.AttachAccountId(context.Background(), sysAccountID) diff --git a/pkg/hakeeper/rsm.go b/pkg/hakeeper/rsm.go index fa9e52ef76526..2505faae693ea 100644 --- a/pkg/hakeeper/rsm.go +++ b/pkg/hakeeper/rsm.go @@ -1705,10 +1705,11 @@ func (s *stateMachine) handleScheduleCommandQuery(uuid string) *pb.CommandBatch func (s *stateMachine) handleClusterDetailsQuery(cfg Config) *pb.ClusterDetails { cfg.Fill() cd := &pb.ClusterDetails{ - CNStores: make([]pb.CNStore, 0, len(s.state.CNState.Stores)), - TNStores: make([]pb.TNStore, 0, len(s.state.TNState.Stores)), - LogStores: make([]pb.LogStore, 0, len(s.state.LogState.Stores)), - ProxyStores: make([]pb.ProxyStore, 0, len(s.state.ProxyState.Stores)), + CNStores: make([]pb.CNStore, 0, len(s.state.CNState.Stores)), + TNStores: make([]pb.TNStore, 0, len(s.state.TNState.Stores)), + LogStores: make([]pb.LogStore, 0, len(s.state.LogState.Stores)), + ProxyStores: make([]pb.ProxyStore, 0, len(s.state.ProxyState.Stores)), + DDLVisibilityDeployedProtocol: s.state.CNState.DDLVisibilityDeployedProtocol, } if s.viewMetadataAdmissionActive() { cd.ViewMetadataAdmission = &pb.ViewMetadataAdmission{ @@ -1749,6 +1750,12 @@ func (s *stateMachine) handleClusterDetailsQuery(cfg Config) *pb.ClusterDetails ViewMetadataAdmissionGeneration: info.ViewMetadataAdmissionGeneration, ViewMetadataObservedEpoch: info.ViewMetadataObservedEpoch, ViewMetadataAdmissionReady: info.ViewMetadataAdmissionReady, + DDLVisibilityBarrierReady: info.DDLVisibilityBarrierReady, + DDLVisibilityDeployedProtocol: info.DDLVisibilityDeployedProtocol, + DDLVisibilityActivationPrepared: info.DDLVisibilityActivationPrepared, + DDLVisibilityActivationFenced: info.DDLVisibilityActivationFenced, + DDLVisibilityFrontier: info.DDLVisibilityFrontier, + ViewMetadataIngressReady: info.ViewMetadataIngressReady, } cd.CNStores = append(cd.CNStores, n) } @@ -1778,13 +1785,14 @@ func (s *stateMachine) handleClusterDetailsQuery(cfg Config) *pb.ClusterDetails state = pb.TimeoutState } n := pb.LogStore{ - UUID: uuid, - Tick: info.Tick, - State: state, - ServiceAddress: info.ServiceAddress, - Replicas: info.Replicas, - ConfigData: info.ConfigData, - Locality: info.Locality, + UUID: uuid, + Tick: info.Tick, + State: state, + ServiceAddress: info.ServiceAddress, + Replicas: info.Replicas, + ConfigData: info.ConfigData, + Locality: info.Locality, + DDLVisibilityDeployedProtocolSupported: info.DDLVisibilityDeployedProtocolSupported, } cd.LogStores = append(cd.LogStores, n) } diff --git a/pkg/hakeeper/view_metadata_admission.go b/pkg/hakeeper/view_metadata_admission.go index f447aabbd3cd4..c1e31a7474d7d 100644 --- a/pkg/hakeeper/view_metadata_admission.go +++ b/pkg/hakeeper/view_metadata_admission.go @@ -605,6 +605,9 @@ func (s *stateMachine) attachViewMetadataAdmission( } } batch.ViewMetadataAdmission = s.viewMetadataAdmissionSnapshot(uuid, proxy) + if !proxy { + batch.DDLVisibilityDeployedProtocol = s.state.CNState.DDLVisibilityDeployedProtocol + } data, err := batch.Marshal() if err != nil { panic(err) diff --git a/pkg/hakeeper/view_metadata_admission_test.go b/pkg/hakeeper/view_metadata_admission_test.go index 5dc2c051dccb2..91335a14d9e35 100644 --- a/pkg/hakeeper/view_metadata_admission_test.go +++ b/pkg/hakeeper/view_metadata_admission_test.go @@ -612,6 +612,7 @@ func TestClusterDetailsKeepsSupportedPendingButHidesLegacyIngress(t *testing.T) rsm.state.CNState.Stores["pending"] = pb.CNStoreInfo{ ViewMetadataAdmissionSupported: true, ViewMetadataAdmissionGeneration: 2, + DDLVisibilityBarrierReady: true, } rsm.state.CNState.Stores["ready"] = pb.CNStoreInfo{ ViewMetadataAdmissionSupported: true, @@ -629,7 +630,9 @@ func TestClusterDetailsKeepsSupportedPendingButHidesLegacyIngress(t *testing.T) } require.Contains(t, byID, "pending") require.False(t, byID["pending"].ViewMetadataAdmissionReady) + require.True(t, byID["pending"].DDLVisibilityBarrierReady) require.Contains(t, byID, "ready") + require.True(t, byID["ready"].ViewMetadataIngressReady) require.NotContains(t, byID, "legacy") } diff --git a/pkg/logservice/store.go b/pkg/logservice/store.go index 57fa5a49008c5..93b44e8c772c9 100644 --- a/pkg/logservice/store.go +++ b/pkg/logservice/store.go @@ -1523,14 +1523,15 @@ func (l *store) hakeeperTick() { func (l *store) getHeartbeatMessage() pb.LogStoreHeartbeat { m := pb.LogStoreHeartbeat{ - UUID: l.id(), - RaftAddress: l.cfg.RaftServiceAddr(), - ServiceAddress: l.cfg.LogServiceServiceAddr(), - GossipAddress: l.cfg.GossipServiceAddr(), - Replicas: make([]pb.LogReplicaInfo, 0), - Locality: l.cfg.getLocality(), - CommandDeliverySupported: true, - ViewMetadataAdmissionSupported: true, + UUID: l.id(), + RaftAddress: l.cfg.RaftServiceAddr(), + ServiceAddress: l.cfg.LogServiceServiceAddr(), + GossipAddress: l.cfg.GossipServiceAddr(), + Replicas: make([]pb.LogReplicaInfo, 0), + Locality: l.cfg.getLocality(), + CommandDeliverySupported: true, + ViewMetadataAdmissionSupported: true, + DDLVisibilityDeployedProtocolSupported: true, } opts := dragonboat.NodeHostInfoOption{ SkipLogInfo: true, diff --git a/pkg/pb/logservice/logservice.go b/pkg/pb/logservice/logservice.go index fffce7594142f..99a693f2c7a27 100644 --- a/pkg/pb/logservice/logservice.go +++ b/pkg/pb/logservice/logservice.go @@ -163,7 +163,50 @@ func (s *CNState) Update(hb CNStoreHeartbeat, tick uint64) { storeInfo.ViewMetadataRefreshSupported = hb.ViewMetadataRefreshSupported storeInfo.ViewMetadataRevalidatedEpoch = hb.ViewMetadataRevalidatedEpoch storeInfo.ViewMetadataIngressReady = hb.ViewMetadataIngressReady + if s.DDLVisibilityDeployedProtocol > 0 && + hb.DDLVisibilityDeployedProtocol < s.DDLVisibilityDeployedProtocol { + // Linearize markerless joins with the committed cut. A stale producer may + // remain barrier-reachable, but it cannot become publicly routable. + storeInfo.ViewMetadataIngressReady = false + } + storeInfo.DDLVisibilityBarrierReady = hb.DDLVisibilityBarrierReady + storeInfo.DDLVisibilityDeployedProtocol = hb.DDLVisibilityDeployedProtocol + storeInfo.DDLVisibilityActivationPrepared = hb.DDLVisibilityActivationPrepared + storeInfo.DDLVisibilityActivationFenced = hb.DDLVisibilityActivationFenced + storeInfo.DDLVisibilityFrontier = hb.DDLVisibilityFrontier s.Stores[hb.UUID] = storeInfo + if hb.DDLVisibilityDeployedProtocol > s.DDLVisibilityDeployedProtocol && + len(hb.DDLVisibilityEpochCommitTargets) > 0 && + s.matchesDDLVisibilityEpochCommitTargets(hb.DDLVisibilityEpochCommitTargets) { + s.DDLVisibilityDeployedProtocol = hb.DDLVisibilityDeployedProtocol + } +} + +func (s *CNState) matchesDDLVisibilityEpochCommitTargets(targets []DDLVisibilityActivationTarget) bool { + expected := make(map[string]DDLVisibilityActivationTarget, len(targets)) + for _, target := range targets { + if target.ServiceID == "" || target.Generation == 0 || target.QueryAddress == "" { + return false + } + if _, ok := expected[target.ServiceID]; ok { + return false + } + expected[target.ServiceID] = target + } + seen := 0 + for serviceID, store := range s.Stores { + if store.QueryAddress == "" || store.ViewMetadataAdmissionGeneration == 0 { + continue + } + seen++ + target, ok := expected[serviceID] + if !ok || target.Generation != store.ViewMetadataAdmissionGeneration || + target.QueryAddress != store.QueryAddress || !store.DDLVisibilityBarrierReady || + !store.DDLVisibilityActivationPrepared || !store.DDLVisibilityActivationFenced { + return false + } + } + return seen == len(expected) } // UpdateLabel updates labels of CN store. @@ -274,6 +317,7 @@ func (s *LogState) updateStores(hb LogStoreHeartbeat, tick uint64) { storeInfo.Locality = hb.Locality storeInfo.CommandDeliverySupported = hb.CommandDeliverySupported storeInfo.ViewMetadataAdmissionSupported = hb.ViewMetadataAdmissionSupported + storeInfo.DDLVisibilityDeployedProtocolSupported = hb.DDLVisibilityDeployedProtocolSupported s.Stores[hb.UUID] = storeInfo } diff --git a/pkg/pb/logservice/logservice.pb.go b/pkg/pb/logservice/logservice.pb.go index 187fb5f887d5e..292aaf1ec4b91 100644 --- a/pkg/pb/logservice/logservice.pb.go +++ b/pkg/pb/logservice/logservice.pb.go @@ -15,6 +15,7 @@ import ( proto "github.com/gogo/protobuf/proto" github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" metadata "github.com/matrixorigin/matrixone/pkg/pb/metadata" + timestamp "github.com/matrixorigin/matrixone/pkg/pb/timestamp" _ "google.golang.org/protobuf/types/known/timestamppb" ) @@ -530,9 +531,18 @@ type CNStore struct { ViewMetadataAdmissionGeneration uint64 `protobuf:"varint,18,opt,name=ViewMetadataAdmissionGeneration,proto3" json:"ViewMetadataAdmissionGeneration,omitempty"` ViewMetadataObservedEpoch uint64 `protobuf:"varint,19,opt,name=ViewMetadataObservedEpoch,proto3" json:"ViewMetadataObservedEpoch,omitempty"` ViewMetadataAdmissionReady bool `protobuf:"varint,20,opt,name=ViewMetadataAdmissionReady,proto3" json:"ViewMetadataAdmissionReady,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + // DDLVisibilityBarrierReady means QueryService can accept the versioned DDL + // visibility fence even when public SQL ingress is not admitted yet. + DDLVisibilityBarrierReady bool `protobuf:"varint,21,opt,name=DDLVisibilityBarrierReady,proto3" json:"DDLVisibilityBarrierReady,omitempty"` + // ViewMetadataIngressReady is retained for raw control-plane inventory. + ViewMetadataIngressReady bool `protobuf:"varint,22,opt,name=ViewMetadataIngressReady,proto3" json:"ViewMetadataIngressReady,omitempty"` + DDLVisibilityDeployedProtocol int64 `protobuf:"varint,23,opt,name=DDLVisibilityDeployedProtocol,proto3" json:"DDLVisibilityDeployedProtocol,omitempty"` + DDLVisibilityActivationPrepared bool `protobuf:"varint,24,opt,name=DDLVisibilityActivationPrepared,proto3" json:"DDLVisibilityActivationPrepared,omitempty"` + DDLVisibilityActivationFenced bool `protobuf:"varint,25,opt,name=DDLVisibilityActivationFenced,proto3" json:"DDLVisibilityActivationFenced,omitempty"` + DDLVisibilityFrontier timestamp.Timestamp `protobuf:"bytes,26,opt,name=DDLVisibilityFrontier,proto3" json:"DDLVisibilityFrontier"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *CNStore) Reset() { *m = CNStore{} } @@ -701,6 +711,48 @@ func (m *CNStore) GetViewMetadataAdmissionReady() bool { return false } +func (m *CNStore) GetDDLVisibilityBarrierReady() bool { + if m != nil { + return m.DDLVisibilityBarrierReady + } + return false +} + +func (m *CNStore) GetViewMetadataIngressReady() bool { + if m != nil { + return m.ViewMetadataIngressReady + } + return false +} + +func (m *CNStore) GetDDLVisibilityDeployedProtocol() int64 { + if m != nil { + return m.DDLVisibilityDeployedProtocol + } + return 0 +} + +func (m *CNStore) GetDDLVisibilityActivationPrepared() bool { + if m != nil { + return m.DDLVisibilityActivationPrepared + } + return false +} + +func (m *CNStore) GetDDLVisibilityActivationFenced() bool { + if m != nil { + return m.DDLVisibilityActivationFenced + } + return false +} + +func (m *CNStore) GetDDLVisibilityFrontier() timestamp.Timestamp { + if m != nil { + return m.DDLVisibilityFrontier + } + return timestamp.Timestamp{} +} + type TNStore struct { UUID string `protobuf:"bytes,1,opt,name=UUID,proto3" json:"UUID,omitempty"` ServiceAddress string `protobuf:"bytes,2,opt,name=ServiceAddress,proto3" json:"ServiceAddress,omitempty"` @@ -832,16 +884,19 @@ func (m *TNStore) GetAutoIncrEpochFenceSupported() bool { } type LogStore struct { - UUID string `protobuf:"bytes,1,opt,name=UUID,proto3" json:"UUID,omitempty"` - ServiceAddress string `protobuf:"bytes,2,opt,name=ServiceAddress,proto3" json:"ServiceAddress,omitempty"` - Tick uint64 `protobuf:"varint,3,opt,name=Tick,proto3" json:"Tick,omitempty"` - State NodeState `protobuf:"varint,4,opt,name=State,proto3,enum=logservice.NodeState" json:"State,omitempty"` - Replicas []LogReplicaInfo `protobuf:"bytes,5,rep,name=Replicas,proto3" json:"Replicas"` - ConfigData *ConfigData `protobuf:"bytes,6,opt,name=ConfigData,proto3" json:"ConfigData,omitempty"` - Locality Locality `protobuf:"bytes,7,opt,name=Locality,proto3" json:"Locality"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + UUID string `protobuf:"bytes,1,opt,name=UUID,proto3" json:"UUID,omitempty"` + ServiceAddress string `protobuf:"bytes,2,opt,name=ServiceAddress,proto3" json:"ServiceAddress,omitempty"` + Tick uint64 `protobuf:"varint,3,opt,name=Tick,proto3" json:"Tick,omitempty"` + State NodeState `protobuf:"varint,4,opt,name=State,proto3,enum=logservice.NodeState" json:"State,omitempty"` + Replicas []LogReplicaInfo `protobuf:"bytes,5,rep,name=Replicas,proto3" json:"Replicas"` + ConfigData *ConfigData `protobuf:"bytes,6,opt,name=ConfigData,proto3" json:"ConfigData,omitempty"` + Locality Locality `protobuf:"bytes,7,opt,name=Locality,proto3" json:"Locality"` + // DDLVisibilityDeployedProtocolSupported proves the hosted HAKeeper RSM can + // persist and return the cluster-wide DDL deployment epoch. + DDLVisibilityDeployedProtocolSupported bool `protobuf:"varint,8,opt,name=DDLVisibilityDeployedProtocolSupported,proto3" json:"DDLVisibilityDeployedProtocolSupported,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *LogStore) Reset() { *m = LogStore{} } @@ -926,6 +981,13 @@ func (m *LogStore) GetLocality() Locality { return Locality{} } +func (m *LogStore) GetDDLVisibilityDeployedProtocolSupported() bool { + if m != nil { + return m.DDLVisibilityDeployedProtocolSupported + } + return false +} + // LogShardInfo contains information a log shard. type LogShardInfo struct { // ShardID is the ID of a Log shard. @@ -1155,6 +1217,69 @@ func (m *Resource) GetMemAvailable() uint64 { } // CNStoreHeartbeat is the periodic message sent tp the HAKeeper by CN stores. +type DDLVisibilityActivationTarget struct { + ServiceID string `protobuf:"bytes,1,opt,name=ServiceID,proto3" json:"ServiceID,omitempty"` + Generation uint64 `protobuf:"varint,2,opt,name=Generation,proto3" json:"Generation,omitempty"` + QueryAddress string `protobuf:"bytes,3,opt,name=QueryAddress,proto3" json:"QueryAddress,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DDLVisibilityActivationTarget) Reset() { *m = DDLVisibilityActivationTarget{} } +func (m *DDLVisibilityActivationTarget) String() string { return proto.CompactTextString(m) } +func (*DDLVisibilityActivationTarget) ProtoMessage() {} +func (*DDLVisibilityActivationTarget) Descriptor() ([]byte, []int) { + return fileDescriptor_fd1040c5381ab5a7, []int{6} +} +func (m *DDLVisibilityActivationTarget) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *DDLVisibilityActivationTarget) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DDLVisibilityActivationTarget.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *DDLVisibilityActivationTarget) XXX_Merge(src proto.Message) { + xxx_messageInfo_DDLVisibilityActivationTarget.Merge(m, src) +} +func (m *DDLVisibilityActivationTarget) XXX_Size() int { + return m.ProtoSize() +} +func (m *DDLVisibilityActivationTarget) XXX_DiscardUnknown() { + xxx_messageInfo_DDLVisibilityActivationTarget.DiscardUnknown(m) +} + +var xxx_messageInfo_DDLVisibilityActivationTarget proto.InternalMessageInfo + +func (m *DDLVisibilityActivationTarget) GetServiceID() string { + if m != nil { + return m.ServiceID + } + return "" +} + +func (m *DDLVisibilityActivationTarget) GetGeneration() uint64 { + if m != nil { + return m.Generation + } + return 0 +} + +func (m *DDLVisibilityActivationTarget) GetQueryAddress() string { + if m != nil { + return m.QueryAddress + } + return "" +} + type CNStoreHeartbeat struct { UUID string `protobuf:"bytes,1,opt,name=UUID,proto3" json:"UUID,omitempty"` ServiceAddress string `protobuf:"bytes,2,opt,name=ServiceAddress,proto3" json:"ServiceAddress,omitempty"` @@ -1184,17 +1309,27 @@ type CNStoreHeartbeat struct { ViewMetadataRevalidatedEpoch uint64 `protobuf:"varint,23,opt,name=ViewMetadataRevalidatedEpoch,proto3" json:"ViewMetadataRevalidatedEpoch,omitempty"` // ViewMetadataIngressReady is published only after every public CN ingress // listener has started successfully. Admission alone is not routability. - ViewMetadataIngressReady bool `protobuf:"varint,24,opt,name=ViewMetadataIngressReady,proto3" json:"ViewMetadataIngressReady,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + ViewMetadataIngressReady bool `protobuf:"varint,24,opt,name=ViewMetadataIngressReady,proto3" json:"ViewMetadataIngressReady,omitempty"` + DDLVisibilityBarrierReady bool `protobuf:"varint,25,opt,name=DDLVisibilityBarrierReady,proto3" json:"DDLVisibilityBarrierReady,omitempty"` + // DDLVisibilityDeployedProtocol reports only a locally committed deployment + // marker. Provisional activation must never advance the cluster-wide epoch. + DDLVisibilityDeployedProtocol int64 `protobuf:"varint,26,opt,name=DDLVisibilityDeployedProtocol,proto3" json:"DDLVisibilityDeployedProtocol,omitempty"` + // Non-empty only for the transition that atomically validates the complete + // CN membership tuple set and commits DDLVisibilityDeployedProtocol. + DDLVisibilityEpochCommitTargets []DDLVisibilityActivationTarget `protobuf:"bytes,27,rep,name=DDLVisibilityEpochCommitTargets,proto3" json:"DDLVisibilityEpochCommitTargets"` + DDLVisibilityActivationPrepared bool `protobuf:"varint,28,opt,name=DDLVisibilityActivationPrepared,proto3" json:"DDLVisibilityActivationPrepared,omitempty"` + DDLVisibilityActivationFenced bool `protobuf:"varint,29,opt,name=DDLVisibilityActivationFenced,proto3" json:"DDLVisibilityActivationFenced,omitempty"` + DDLVisibilityFrontier timestamp.Timestamp `protobuf:"bytes,30,opt,name=DDLVisibilityFrontier,proto3" json:"DDLVisibilityFrontier"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *CNStoreHeartbeat) Reset() { *m = CNStoreHeartbeat{} } func (m *CNStoreHeartbeat) String() string { return proto.CompactTextString(m) } func (*CNStoreHeartbeat) ProtoMessage() {} func (*CNStoreHeartbeat) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{6} + return fileDescriptor_fd1040c5381ab5a7, []int{7} } func (m *CNStoreHeartbeat) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1384,6 +1519,48 @@ func (m *CNStoreHeartbeat) GetViewMetadataIngressReady() bool { return false } +func (m *CNStoreHeartbeat) GetDDLVisibilityBarrierReady() bool { + if m != nil { + return m.DDLVisibilityBarrierReady + } + return false +} + +func (m *CNStoreHeartbeat) GetDDLVisibilityDeployedProtocol() int64 { + if m != nil { + return m.DDLVisibilityDeployedProtocol + } + return 0 +} + +func (m *CNStoreHeartbeat) GetDDLVisibilityEpochCommitTargets() []DDLVisibilityActivationTarget { + if m != nil { + return m.DDLVisibilityEpochCommitTargets + } + return nil +} + +func (m *CNStoreHeartbeat) GetDDLVisibilityActivationPrepared() bool { + if m != nil { + return m.DDLVisibilityActivationPrepared + } + return false +} + +func (m *CNStoreHeartbeat) GetDDLVisibilityActivationFenced() bool { + if m != nil { + return m.DDLVisibilityActivationFenced + } + return false +} + +func (m *CNStoreHeartbeat) GetDDLVisibilityFrontier() timestamp.Timestamp { + if m != nil { + return m.DDLVisibilityFrontier + } + return timestamp.Timestamp{} +} + // CNAllocateID is the periodic message sent tp the HAKeeper by CN stores. type CNAllocateID struct { Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` @@ -1400,7 +1577,7 @@ func (m *CNAllocateID) Reset() { *m = CNAllocateID{} } func (m *CNAllocateID) String() string { return proto.CompactTextString(m) } func (*CNAllocateID) ProtoMessage() {} func (*CNAllocateID) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{7} + return fileDescriptor_fd1040c5381ab5a7, []int{8} } func (m *CNAllocateID) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1472,17 +1649,20 @@ type LogStoreHeartbeat struct { CommandDeliverySupported bool `protobuf:"varint,9,opt,name=CommandDeliverySupported,proto3" json:"CommandDeliverySupported,omitempty"` // ViewMetadataAdmissionSupported proves that every HAKeeper RSM hosted by // this LogStore can decode the durable admission update and snapshot fields. - ViewMetadataAdmissionSupported bool `protobuf:"varint,10,opt,name=ViewMetadataAdmissionSupported,proto3" json:"ViewMetadataAdmissionSupported,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + ViewMetadataAdmissionSupported bool `protobuf:"varint,10,opt,name=ViewMetadataAdmissionSupported,proto3" json:"ViewMetadataAdmissionSupported,omitempty"` + // This gates the HAKeeper RSM schema used by the durable DDL deployment + // epoch across both voting and non-voting replicas. + DDLVisibilityDeployedProtocolSupported bool `protobuf:"varint,11,opt,name=DDLVisibilityDeployedProtocolSupported,proto3" json:"DDLVisibilityDeployedProtocolSupported,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *LogStoreHeartbeat) Reset() { *m = LogStoreHeartbeat{} } func (m *LogStoreHeartbeat) String() string { return proto.CompactTextString(m) } func (*LogStoreHeartbeat) ProtoMessage() {} func (*LogStoreHeartbeat) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{8} + return fileDescriptor_fd1040c5381ab5a7, []int{9} } func (m *LogStoreHeartbeat) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1581,6 +1761,13 @@ func (m *LogStoreHeartbeat) GetViewMetadataAdmissionSupported() bool { return false } +func (m *LogStoreHeartbeat) GetDDLVisibilityDeployedProtocolSupported() bool { + if m != nil { + return m.DDLVisibilityDeployedProtocolSupported + } + return false +} + // TNShardInfo contains information of a launched TN shard. type TNShardInfo struct { // ShardID uniquely identifies a TN shard. Each TN shard manages a Primary @@ -1599,7 +1786,7 @@ func (m *TNShardInfo) Reset() { *m = TNShardInfo{} } func (m *TNShardInfo) String() string { return proto.CompactTextString(m) } func (*TNShardInfo) ProtoMessage() {} func (*TNShardInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{9} + return fileDescriptor_fd1040c5381ab5a7, []int{10} } func (m *TNShardInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1674,7 +1861,7 @@ func (m *TNStoreHeartbeat) Reset() { *m = TNStoreHeartbeat{} } func (m *TNStoreHeartbeat) String() string { return proto.CompactTextString(m) } func (*TNStoreHeartbeat) ProtoMessage() {} func (*TNStoreHeartbeat) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{10} + return fileDescriptor_fd1040c5381ab5a7, []int{11} } func (m *TNStoreHeartbeat) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1819,7 +2006,7 @@ func (m *RSMState) Reset() { *m = RSMState{} } func (m *RSMState) String() string { return proto.CompactTextString(m) } func (*RSMState) ProtoMessage() {} func (*RSMState) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{11} + return fileDescriptor_fd1040c5381ab5a7, []int{12} } func (m *RSMState) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1953,7 +2140,7 @@ func (m *LogRecord) Reset() { *m = LogRecord{} } func (m *LogRecord) String() string { return proto.CompactTextString(m) } func (*LogRecord) ProtoMessage() {} func (*LogRecord) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{12} + return fileDescriptor_fd1040c5381ab5a7, []int{13} } func (m *LogRecord) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2022,7 +2209,7 @@ func (m *LogRequest) Reset() { *m = LogRequest{} } func (m *LogRequest) String() string { return proto.CompactTextString(m) } func (*LogRequest) ProtoMessage() {} func (*LogRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{13} + return fileDescriptor_fd1040c5381ab5a7, []int{14} } func (m *LogRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2125,7 +2312,7 @@ func (m *TsoRequest) Reset() { *m = TsoRequest{} } func (m *TsoRequest) String() string { return proto.CompactTextString(m) } func (*TsoRequest) ProtoMessage() {} func (*TsoRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{14} + return fileDescriptor_fd1040c5381ab5a7, []int{15} } func (m *TsoRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2178,7 +2365,7 @@ func (m *CNStoreLabel) Reset() { *m = CNStoreLabel{} } func (m *CNStoreLabel) String() string { return proto.CompactTextString(m) } func (*CNStoreLabel) ProtoMessage() {} func (*CNStoreLabel) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{15} + return fileDescriptor_fd1040c5381ab5a7, []int{16} } func (m *CNStoreLabel) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2242,7 +2429,7 @@ func (m *CNWorkState) Reset() { *m = CNWorkState{} } func (m *CNWorkState) String() string { return proto.CompactTextString(m) } func (*CNWorkState) ProtoMessage() {} func (*CNWorkState) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{16} + return fileDescriptor_fd1040c5381ab5a7, []int{17} } func (m *CNWorkState) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2301,7 +2488,7 @@ func (m *CNStateLabel) Reset() { *m = CNStateLabel{} } func (m *CNStateLabel) String() string { return proto.CompactTextString(m) } func (*CNStateLabel) ProtoMessage() {} func (*CNStateLabel) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{17} + return fileDescriptor_fd1040c5381ab5a7, []int{18} } func (m *CNStateLabel) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2378,7 +2565,7 @@ func (m *Request) Reset() { *m = Request{} } func (m *Request) String() string { return proto.CompactTextString(m) } func (*Request) ProtoMessage() {} func (*Request) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{18} + return fileDescriptor_fd1040c5381ab5a7, []int{19} } func (m *Request) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2540,7 +2727,7 @@ func (m *LogResponse) Reset() { *m = LogResponse{} } func (m *LogResponse) String() string { return proto.CompactTextString(m) } func (*LogResponse) ProtoMessage() {} func (*LogResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{19} + return fileDescriptor_fd1040c5381ab5a7, []int{20} } func (m *LogResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2608,7 +2795,7 @@ func (m *AllocateIDResponse) Reset() { *m = AllocateIDResponse{} } func (m *AllocateIDResponse) String() string { return proto.CompactTextString(m) } func (*AllocateIDResponse) ProtoMessage() {} func (*AllocateIDResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{20} + return fileDescriptor_fd1040c5381ab5a7, []int{21} } func (m *AllocateIDResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2666,7 +2853,7 @@ func (m *Response) Reset() { *m = Response{} } func (m *Response) String() string { return proto.CompactTextString(m) } func (*Response) ProtoMessage() {} func (*Response) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{21} + return fileDescriptor_fd1040c5381ab5a7, []int{22} } func (m *Response) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2790,7 +2977,7 @@ func (m *LogRecordResponse) Reset() { *m = LogRecordResponse{} } func (m *LogRecordResponse) String() string { return proto.CompactTextString(m) } func (*LogRecordResponse) ProtoMessage() {} func (*LogRecordResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{22} + return fileDescriptor_fd1040c5381ab5a7, []int{23} } func (m *LogRecordResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2837,7 +3024,7 @@ func (m *TsoResponse) Reset() { *m = TsoResponse{} } func (m *TsoResponse) String() string { return proto.CompactTextString(m) } func (*TsoResponse) ProtoMessage() {} func (*TsoResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{23} + return fileDescriptor_fd1040c5381ab5a7, []int{24} } func (m *TsoResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2887,7 +3074,7 @@ func (m *TaskTableUser) Reset() { *m = TaskTableUser{} } func (m *TaskTableUser) String() string { return proto.CompactTextString(m) } func (*TaskTableUser) ProtoMessage() {} func (*TaskTableUser) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{24} + return fileDescriptor_fd1040c5381ab5a7, []int{25} } func (m *TaskTableUser) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2948,7 +3135,7 @@ func (m *Replica) Reset() { *m = Replica{} } func (m *Replica) String() string { return proto.CompactTextString(m) } func (*Replica) ProtoMessage() {} func (*Replica) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{25} + return fileDescriptor_fd1040c5381ab5a7, []int{26} } func (m *Replica) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3027,7 +3214,7 @@ func (m *ConfigChange) Reset() { *m = ConfigChange{} } func (m *ConfigChange) String() string { return proto.CompactTextString(m) } func (*ConfigChange) ProtoMessage() {} func (*ConfigChange) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{26} + return fileDescriptor_fd1040c5381ab5a7, []int{27} } func (m *ConfigChange) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3089,7 +3276,7 @@ func (m *ShutdownStore) Reset() { *m = ShutdownStore{} } func (m *ShutdownStore) String() string { return proto.CompactTextString(m) } func (*ShutdownStore) ProtoMessage() {} func (*ShutdownStore) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{27} + return fileDescriptor_fd1040c5381ab5a7, []int{28} } func (m *ShutdownStore) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3148,7 +3335,7 @@ func (m *ScheduleCommand) Reset() { *m = ScheduleCommand{} } func (m *ScheduleCommand) String() string { return proto.CompactTextString(m) } func (*ScheduleCommand) ProtoMessage() {} func (*ScheduleCommand) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{28} + return fileDescriptor_fd1040c5381ab5a7, []int{29} } func (m *ScheduleCommand) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3268,7 +3455,7 @@ func (m *ScheduleCommandQuery) Reset() { *m = ScheduleCommandQuery{} } func (m *ScheduleCommandQuery) String() string { return proto.CompactTextString(m) } func (*ScheduleCommandQuery) ProtoMessage() {} func (*ScheduleCommandQuery) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{29} + return fileDescriptor_fd1040c5381ab5a7, []int{30} } func (m *ScheduleCommandQuery) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3336,7 +3523,7 @@ func (m *CommandDeliveryTargets) Reset() { *m = CommandDeliveryTargets{} func (m *CommandDeliveryTargets) String() string { return proto.CompactTextString(m) } func (*CommandDeliveryTargets) ProtoMessage() {} func (*CommandDeliveryTargets) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{30} + return fileDescriptor_fd1040c5381ab5a7, []int{31} } func (m *CommandDeliveryTargets) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3418,7 +3605,7 @@ func (m *JoinGossipCluster) Reset() { *m = JoinGossipCluster{} } func (m *JoinGossipCluster) String() string { return proto.CompactTextString(m) } func (*JoinGossipCluster) ProtoMessage() {} func (*JoinGossipCluster) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{31} + return fileDescriptor_fd1040c5381ab5a7, []int{32} } func (m *JoinGossipCluster) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3469,7 +3656,7 @@ func (m *CreateTaskService) Reset() { *m = CreateTaskService{} } func (m *CreateTaskService) String() string { return proto.CompactTextString(m) } func (*CreateTaskService) ProtoMessage() {} func (*CreateTaskService) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{32} + return fileDescriptor_fd1040c5381ab5a7, []int{33} } func (m *CreateTaskService) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3524,7 +3711,7 @@ func (m *DeleteCNStore) Reset() { *m = DeleteCNStore{} } func (m *DeleteCNStore) String() string { return proto.CompactTextString(m) } func (*DeleteCNStore) ProtoMessage() {} func (*DeleteCNStore) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{33} + return fileDescriptor_fd1040c5381ab5a7, []int{34} } func (m *DeleteCNStore) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3572,7 +3759,7 @@ func (m *DeleteProxyStore) Reset() { *m = DeleteProxyStore{} } func (m *DeleteProxyStore) String() string { return proto.CompactTextString(m) } func (*DeleteProxyStore) ProtoMessage() {} func (*DeleteProxyStore) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{34} + return fileDescriptor_fd1040c5381ab5a7, []int{35} } func (m *DeleteProxyStore) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3620,7 +3807,7 @@ func (m *AddLogShard) Reset() { *m = AddLogShard{} } func (m *AddLogShard) String() string { return proto.CompactTextString(m) } func (*AddLogShard) ProtoMessage() {} func (*AddLogShard) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{35} + return fileDescriptor_fd1040c5381ab5a7, []int{36} } func (m *AddLogShard) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3671,7 +3858,7 @@ func (m *BootstrapShard) Reset() { *m = BootstrapShard{} } func (m *BootstrapShard) String() string { return proto.CompactTextString(m) } func (*BootstrapShard) ProtoMessage() {} func (*BootstrapShard) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{36} + return fileDescriptor_fd1040c5381ab5a7, []int{37} } func (m *BootstrapShard) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3746,16 +3933,19 @@ type CommandBatch struct { // ViewMetadataAdmission is the authoritative snapshot for the heartbeat // sender. Old services ignore this additive field. ViewMetadataAdmission *ViewMetadataAdmission `protobuf:"bytes,5,opt,name=ViewMetadataAdmission,proto3" json:"ViewMetadataAdmission,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + // DDLVisibilityDeployedProtocol is returned from the same replicated + // heartbeat transition that conditionally publishes CN ingress. + DDLVisibilityDeployedProtocol int64 `protobuf:"varint,6,opt,name=DDLVisibilityDeployedProtocol,proto3" json:"DDLVisibilityDeployedProtocol,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *CommandBatch) Reset() { *m = CommandBatch{} } func (m *CommandBatch) String() string { return proto.CompactTextString(m) } func (*CommandBatch) ProtoMessage() {} func (*CommandBatch) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{37} + return fileDescriptor_fd1040c5381ab5a7, []int{38} } func (m *CommandBatch) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3819,6 +4009,13 @@ func (m *CommandBatch) GetViewMetadataAdmission() *ViewMetadataAdmission { return nil } +func (m *CommandBatch) GetDDLVisibilityDeployedProtocol() int64 { + if m != nil { + return m.DDLVisibilityDeployedProtocol + } + return 0 +} + // ViewMetadataAdmission is the durable cluster admission snapshot. Generation // and Ready describe the heartbeat sender when returned in CommandBatch; they // are zero/false in the global ClusterDetails copy. @@ -3842,7 +4039,7 @@ func (m *ViewMetadataAdmission) Reset() { *m = ViewMetadataAdmission{} } func (m *ViewMetadataAdmission) String() string { return proto.CompactTextString(m) } func (*ViewMetadataAdmission) ProtoMessage() {} func (*ViewMetadataAdmission) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{38} + return fileDescriptor_fd1040c5381ab5a7, []int{39} } func (m *ViewMetadataAdmission) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3943,7 +4140,7 @@ func (m *ViewMetadataAdmissionTargets) Reset() { *m = ViewMetadataAdmiss func (m *ViewMetadataAdmissionTargets) String() string { return proto.CompactTextString(m) } func (*ViewMetadataAdmissionTargets) ProtoMessage() {} func (*ViewMetadataAdmissionTargets) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{39} + return fileDescriptor_fd1040c5381ab5a7, []int{40} } func (m *ViewMetadataAdmissionTargets) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4021,25 +4218,30 @@ type CNStoreInfo struct { // CommandDeliveryAckSupported reports whether this CN can use the // non-destructive acknowledged schedule-command protocol. It is copied // from CNStoreHeartbeat so HAKeeper can gate activation and admission. - CommandDeliveryAckSupported bool `protobuf:"varint,18,opt,name=CommandDeliveryAckSupported,proto3" json:"CommandDeliveryAckSupported,omitempty"` - ViewMetadataAdmissionSupported bool `protobuf:"varint,19,opt,name=ViewMetadataAdmissionSupported,proto3" json:"ViewMetadataAdmissionSupported,omitempty"` - ViewMetadataAdmissionGeneration uint64 `protobuf:"varint,20,opt,name=ViewMetadataAdmissionGeneration,proto3" json:"ViewMetadataAdmissionGeneration,omitempty"` - ViewMetadataObservedEpoch uint64 `protobuf:"varint,21,opt,name=ViewMetadataObservedEpoch,proto3" json:"ViewMetadataObservedEpoch,omitempty"` - ViewMetadataCatalogFencedEpoch uint64 `protobuf:"varint,22,opt,name=ViewMetadataCatalogFencedEpoch,proto3" json:"ViewMetadataCatalogFencedEpoch,omitempty"` - ViewMetadataRefreshSupported bool `protobuf:"varint,23,opt,name=ViewMetadataRefreshSupported,proto3" json:"ViewMetadataRefreshSupported,omitempty"` - ViewMetadataRevalidatedEpoch uint64 `protobuf:"varint,24,opt,name=ViewMetadataRevalidatedEpoch,proto3" json:"ViewMetadataRevalidatedEpoch,omitempty"` - ViewMetadataAdmissionReady bool `protobuf:"varint,25,opt,name=ViewMetadataAdmissionReady,proto3" json:"ViewMetadataAdmissionReady,omitempty"` - ViewMetadataIngressReady bool `protobuf:"varint,26,opt,name=ViewMetadataIngressReady,proto3" json:"ViewMetadataIngressReady,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + CommandDeliveryAckSupported bool `protobuf:"varint,18,opt,name=CommandDeliveryAckSupported,proto3" json:"CommandDeliveryAckSupported,omitempty"` + ViewMetadataAdmissionSupported bool `protobuf:"varint,19,opt,name=ViewMetadataAdmissionSupported,proto3" json:"ViewMetadataAdmissionSupported,omitempty"` + ViewMetadataAdmissionGeneration uint64 `protobuf:"varint,20,opt,name=ViewMetadataAdmissionGeneration,proto3" json:"ViewMetadataAdmissionGeneration,omitempty"` + ViewMetadataObservedEpoch uint64 `protobuf:"varint,21,opt,name=ViewMetadataObservedEpoch,proto3" json:"ViewMetadataObservedEpoch,omitempty"` + ViewMetadataCatalogFencedEpoch uint64 `protobuf:"varint,22,opt,name=ViewMetadataCatalogFencedEpoch,proto3" json:"ViewMetadataCatalogFencedEpoch,omitempty"` + ViewMetadataRefreshSupported bool `protobuf:"varint,23,opt,name=ViewMetadataRefreshSupported,proto3" json:"ViewMetadataRefreshSupported,omitempty"` + ViewMetadataRevalidatedEpoch uint64 `protobuf:"varint,24,opt,name=ViewMetadataRevalidatedEpoch,proto3" json:"ViewMetadataRevalidatedEpoch,omitempty"` + ViewMetadataAdmissionReady bool `protobuf:"varint,25,opt,name=ViewMetadataAdmissionReady,proto3" json:"ViewMetadataAdmissionReady,omitempty"` + ViewMetadataIngressReady bool `protobuf:"varint,26,opt,name=ViewMetadataIngressReady,proto3" json:"ViewMetadataIngressReady,omitempty"` + DDLVisibilityBarrierReady bool `protobuf:"varint,27,opt,name=DDLVisibilityBarrierReady,proto3" json:"DDLVisibilityBarrierReady,omitempty"` + DDLVisibilityDeployedProtocol int64 `protobuf:"varint,28,opt,name=DDLVisibilityDeployedProtocol,proto3" json:"DDLVisibilityDeployedProtocol,omitempty"` + DDLVisibilityActivationPrepared bool `protobuf:"varint,29,opt,name=DDLVisibilityActivationPrepared,proto3" json:"DDLVisibilityActivationPrepared,omitempty"` + DDLVisibilityActivationFenced bool `protobuf:"varint,30,opt,name=DDLVisibilityActivationFenced,proto3" json:"DDLVisibilityActivationFenced,omitempty"` + DDLVisibilityFrontier timestamp.Timestamp `protobuf:"bytes,31,opt,name=DDLVisibilityFrontier,proto3" json:"DDLVisibilityFrontier"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *CNStoreInfo) Reset() { *m = CNStoreInfo{} } func (m *CNStoreInfo) String() string { return proto.CompactTextString(m) } func (*CNStoreInfo) ProtoMessage() {} func (*CNStoreInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{40} + return fileDescriptor_fd1040c5381ab5a7, []int{41} } func (m *CNStoreInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4243,20 +4445,59 @@ func (m *CNStoreInfo) GetViewMetadataIngressReady() bool { return false } +func (m *CNStoreInfo) GetDDLVisibilityBarrierReady() bool { + if m != nil { + return m.DDLVisibilityBarrierReady + } + return false +} + +func (m *CNStoreInfo) GetDDLVisibilityDeployedProtocol() int64 { + if m != nil { + return m.DDLVisibilityDeployedProtocol + } + return 0 +} + +func (m *CNStoreInfo) GetDDLVisibilityActivationPrepared() bool { + if m != nil { + return m.DDLVisibilityActivationPrepared + } + return false +} + +func (m *CNStoreInfo) GetDDLVisibilityActivationFenced() bool { + if m != nil { + return m.DDLVisibilityActivationFenced + } + return false +} + +func (m *CNStoreInfo) GetDDLVisibilityFrontier() timestamp.Timestamp { + if m != nil { + return m.DDLVisibilityFrontier + } + return timestamp.Timestamp{} +} + // CNState contains all CN details known to the HAKeeper. type CNState struct { // Stores is keyed by CN store UUID. - Stores map[string]CNStoreInfo `protobuf:"bytes,1,rep,name=Stores,proto3" json:"Stores" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Stores map[string]CNStoreInfo `protobuf:"bytes,1,rep,name=Stores,proto3" json:"Stores" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // DDLVisibilityDeployedProtocol is a monotonic cluster deployment epoch. + // Once a complete-target cut commits, markerless replacement/scale-out CNs + // must join that fence instead of reopening on an older protocol. + DDLVisibilityDeployedProtocol int64 `protobuf:"varint,2,opt,name=DDLVisibilityDeployedProtocol,proto3" json:"DDLVisibilityDeployedProtocol,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *CNState) Reset() { *m = CNState{} } func (m *CNState) String() string { return proto.CompactTextString(m) } func (*CNState) ProtoMessage() {} func (*CNState) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{41} + return fileDescriptor_fd1040c5381ab5a7, []int{42} } func (m *CNState) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4292,6 +4533,13 @@ func (m *CNState) GetStores() map[string]CNStoreInfo { return nil } +func (m *CNState) GetDDLVisibilityDeployedProtocol() int64 { + if m != nil { + return m.DDLVisibilityDeployedProtocol + } + return 0 +} + // TNStoreInfo contains information on a TN store. type TNStoreInfo struct { Tick uint64 `protobuf:"varint,1,opt,name=Tick,proto3" json:"Tick,omitempty"` @@ -4321,7 +4569,7 @@ func (m *TNStoreInfo) Reset() { *m = TNStoreInfo{} } func (m *TNStoreInfo) String() string { return proto.CompactTextString(m) } func (*TNStoreInfo) ProtoMessage() {} func (*TNStoreInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{42} + return fileDescriptor_fd1040c5381ab5a7, []int{43} } func (m *TNStoreInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4447,7 +4695,7 @@ func (m *TNState) Reset() { *m = TNState{} } func (m *TNState) String() string { return proto.CompactTextString(m) } func (*TNState) ProtoMessage() {} func (*TNState) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{43} + return fileDescriptor_fd1040c5381ab5a7, []int{44} } func (m *TNState) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4501,7 +4749,7 @@ func (m *ProxyStore) Reset() { *m = ProxyStore{} } func (m *ProxyStore) String() string { return proto.CompactTextString(m) } func (*ProxyStore) ProtoMessage() {} func (*ProxyStore) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{44} + return fileDescriptor_fd1040c5381ab5a7, []int{45} } func (m *ProxyStore) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4597,7 +4845,7 @@ func (m *ProxyState) Reset() { *m = ProxyState{} } func (m *ProxyState) String() string { return proto.CompactTextString(m) } func (*ProxyState) ProtoMessage() {} func (*ProxyState) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{45} + return fileDescriptor_fd1040c5381ab5a7, []int{46} } func (m *ProxyState) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4649,7 +4897,7 @@ func (m *ProxyHeartbeat) Reset() { *m = ProxyHeartbeat{} } func (m *ProxyHeartbeat) String() string { return proto.CompactTextString(m) } func (*ProxyHeartbeat) ProtoMessage() {} func (*ProxyHeartbeat) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{46} + return fileDescriptor_fd1040c5381ab5a7, []int{47} } func (m *ProxyHeartbeat) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4721,22 +4969,23 @@ func (m *ProxyHeartbeat) GetViewMetadataObservedEpoch() uint64 { } type ClusterDetails struct { - TNStores []TNStore `protobuf:"bytes,1,rep,name=TNStores,proto3" json:"TNStores"` - CNStores []CNStore `protobuf:"bytes,2,rep,name=CNStores,proto3" json:"CNStores"` - LogStores []LogStore `protobuf:"bytes,3,rep,name=LogStores,proto3" json:"LogStores"` - ProxyStores []ProxyStore `protobuf:"bytes,4,rep,name=ProxyStores,proto3" json:"ProxyStores"` - DeletedStores []DeletedStore `protobuf:"bytes,5,rep,name=DeletedStores,proto3" json:"DeletedStores"` - ViewMetadataAdmission *ViewMetadataAdmission `protobuf:"bytes,6,opt,name=ViewMetadataAdmission,proto3" json:"ViewMetadataAdmission,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + TNStores []TNStore `protobuf:"bytes,1,rep,name=TNStores,proto3" json:"TNStores"` + CNStores []CNStore `protobuf:"bytes,2,rep,name=CNStores,proto3" json:"CNStores"` + LogStores []LogStore `protobuf:"bytes,3,rep,name=LogStores,proto3" json:"LogStores"` + ProxyStores []ProxyStore `protobuf:"bytes,4,rep,name=ProxyStores,proto3" json:"ProxyStores"` + DeletedStores []DeletedStore `protobuf:"bytes,5,rep,name=DeletedStores,proto3" json:"DeletedStores"` + ViewMetadataAdmission *ViewMetadataAdmission `protobuf:"bytes,6,opt,name=ViewMetadataAdmission,proto3" json:"ViewMetadataAdmission,omitempty"` + DDLVisibilityDeployedProtocol int64 `protobuf:"varint,7,opt,name=DDLVisibilityDeployedProtocol,proto3" json:"DDLVisibilityDeployedProtocol,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *ClusterDetails) Reset() { *m = ClusterDetails{} } func (m *ClusterDetails) String() string { return proto.CompactTextString(m) } func (*ClusterDetails) ProtoMessage() {} func (*ClusterDetails) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{47} + return fileDescriptor_fd1040c5381ab5a7, []int{48} } func (m *ClusterDetails) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4807,6 +5056,13 @@ func (m *ClusterDetails) GetViewMetadataAdmission() *ViewMetadataAdmission { return nil } +func (m *ClusterDetails) GetDDLVisibilityDeployedProtocol() int64 { + if m != nil { + return m.DDLVisibilityDeployedProtocol + } + return 0 +} + // ClusterInfo provides a global view of all shards in the cluster. It // describes the logical sharding of the system, rather than physical // distribution of all replicas that belong to those shards. @@ -4822,7 +5078,7 @@ func (m *ClusterInfo) Reset() { *m = ClusterInfo{} } func (m *ClusterInfo) String() string { return proto.CompactTextString(m) } func (*ClusterInfo) ProtoMessage() {} func (*ClusterInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{48} + return fileDescriptor_fd1040c5381ab5a7, []int{49} } func (m *ClusterInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4882,7 +5138,7 @@ func (m *InitialClusterRequest) Reset() { *m = InitialClusterRequest{} } func (m *InitialClusterRequest) String() string { return proto.CompactTextString(m) } func (*InitialClusterRequest) ProtoMessage() {} func (*InitialClusterRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{49} + return fileDescriptor_fd1040c5381ab5a7, []int{50} } func (m *InitialClusterRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4973,7 +5229,7 @@ func (m *RestoreIDWatermarkRequest) Reset() { *m = RestoreIDWatermarkReq func (m *RestoreIDWatermarkRequest) String() string { return proto.CompactTextString(m) } func (*RestoreIDWatermarkRequest) ProtoMessage() {} func (*RestoreIDWatermarkRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{50} + return fileDescriptor_fd1040c5381ab5a7, []int{51} } func (m *RestoreIDWatermarkRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5025,26 +5281,27 @@ func (m *RestoreIDWatermarkRequest) GetLogServiceRecovery() bool { // LogStoreInfo contains information of all replicas found on a Log store. type LogStoreInfo struct { - Tick uint64 `protobuf:"varint,1,opt,name=Tick,proto3" json:"Tick,omitempty"` - RaftAddress string `protobuf:"bytes,2,opt,name=RaftAddress,proto3" json:"RaftAddress,omitempty"` - ServiceAddress string `protobuf:"bytes,3,opt,name=ServiceAddress,proto3" json:"ServiceAddress,omitempty"` - GossipAddress string `protobuf:"bytes,4,opt,name=GossipAddress,proto3" json:"GossipAddress,omitempty"` - Replicas []LogReplicaInfo `protobuf:"bytes,5,rep,name=Replicas,proto3" json:"Replicas"` - TaskServiceCreated bool `protobuf:"varint,6,opt,name=TaskServiceCreated,proto3" json:"TaskServiceCreated,omitempty"` - ConfigData *ConfigData `protobuf:"bytes,7,opt,name=ConfigData,proto3" json:"ConfigData,omitempty"` - Locality Locality `protobuf:"bytes,8,opt,name=Locality,proto3" json:"Locality"` - CommandDeliverySupported bool `protobuf:"varint,9,opt,name=CommandDeliverySupported,proto3" json:"CommandDeliverySupported,omitempty"` - ViewMetadataAdmissionSupported bool `protobuf:"varint,10,opt,name=ViewMetadataAdmissionSupported,proto3" json:"ViewMetadataAdmissionSupported,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Tick uint64 `protobuf:"varint,1,opt,name=Tick,proto3" json:"Tick,omitempty"` + RaftAddress string `protobuf:"bytes,2,opt,name=RaftAddress,proto3" json:"RaftAddress,omitempty"` + ServiceAddress string `protobuf:"bytes,3,opt,name=ServiceAddress,proto3" json:"ServiceAddress,omitempty"` + GossipAddress string `protobuf:"bytes,4,opt,name=GossipAddress,proto3" json:"GossipAddress,omitempty"` + Replicas []LogReplicaInfo `protobuf:"bytes,5,rep,name=Replicas,proto3" json:"Replicas"` + TaskServiceCreated bool `protobuf:"varint,6,opt,name=TaskServiceCreated,proto3" json:"TaskServiceCreated,omitempty"` + ConfigData *ConfigData `protobuf:"bytes,7,opt,name=ConfigData,proto3" json:"ConfigData,omitempty"` + Locality Locality `protobuf:"bytes,8,opt,name=Locality,proto3" json:"Locality"` + CommandDeliverySupported bool `protobuf:"varint,9,opt,name=CommandDeliverySupported,proto3" json:"CommandDeliverySupported,omitempty"` + ViewMetadataAdmissionSupported bool `protobuf:"varint,10,opt,name=ViewMetadataAdmissionSupported,proto3" json:"ViewMetadataAdmissionSupported,omitempty"` + DDLVisibilityDeployedProtocolSupported bool `protobuf:"varint,11,opt,name=DDLVisibilityDeployedProtocolSupported,proto3" json:"DDLVisibilityDeployedProtocolSupported,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *LogStoreInfo) Reset() { *m = LogStoreInfo{} } func (m *LogStoreInfo) String() string { return proto.CompactTextString(m) } func (*LogStoreInfo) ProtoMessage() {} func (*LogStoreInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{51} + return fileDescriptor_fd1040c5381ab5a7, []int{52} } func (m *LogStoreInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5143,6 +5400,13 @@ func (m *LogStoreInfo) GetViewMetadataAdmissionSupported() bool { return false } +func (m *LogStoreInfo) GetDDLVisibilityDeployedProtocolSupported() bool { + if m != nil { + return m.DDLVisibilityDeployedProtocolSupported + } + return false +} + type LogState struct { // Shards is keyed by ShardID, it contains details aggregated from all Log // stores. Each pb.LogShardInfo here contains data aggregated from @@ -5162,7 +5426,7 @@ func (m *LogState) Reset() { *m = LogState{} } func (m *LogState) String() string { return proto.CompactTextString(m) } func (*LogState) ProtoMessage() {} func (*LogState) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{52} + return fileDescriptor_fd1040c5381ab5a7, []int{53} } func (m *LogState) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5235,7 +5499,7 @@ func (m *CheckerState) Reset() { *m = CheckerState{} } func (m *CheckerState) String() string { return proto.CompactTextString(m) } func (*CheckerState) ProtoMessage() {} func (*CheckerState) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{53} + return fileDescriptor_fd1040c5381ab5a7, []int{54} } func (m *CheckerState) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5406,7 +5670,7 @@ func (m *DeletedStore) Reset() { *m = DeletedStore{} } func (m *DeletedStore) String() string { return proto.CompactTextString(m) } func (*DeletedStore) ProtoMessage() {} func (*DeletedStore) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{54} + return fileDescriptor_fd1040c5381ab5a7, []int{55} } func (m *DeletedStore) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5553,7 +5817,7 @@ func (m *HAKeeperRSMState) Reset() { *m = HAKeeperRSMState{} } func (m *HAKeeperRSMState) String() string { return proto.CompactTextString(m) } func (*HAKeeperRSMState) ProtoMessage() {} func (*HAKeeperRSMState) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{55} + return fileDescriptor_fd1040c5381ab5a7, []int{56} } func (m *HAKeeperRSMState) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5889,7 +6153,7 @@ func (m *ReplicaInfo) Reset() { *m = ReplicaInfo{} } func (m *ReplicaInfo) String() string { return proto.CompactTextString(m) } func (*ReplicaInfo) ProtoMessage() {} func (*ReplicaInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{56} + return fileDescriptor_fd1040c5381ab5a7, []int{57} } func (m *ReplicaInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5948,7 +6212,7 @@ func (m *ShardInfoQueryResult) Reset() { *m = ShardInfoQueryResult{} } func (m *ShardInfoQueryResult) String() string { return proto.CompactTextString(m) } func (*ShardInfoQueryResult) ProtoMessage() {} func (*ShardInfoQueryResult) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{57} + return fileDescriptor_fd1040c5381ab5a7, []int{58} } func (m *ShardInfoQueryResult) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6028,7 +6292,7 @@ func (m *BackupData) Reset() { *m = BackupData{} } func (m *BackupData) String() string { return proto.CompactTextString(m) } func (*BackupData) ProtoMessage() {} func (*BackupData) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{58} + return fileDescriptor_fd1040c5381ab5a7, []int{59} } func (m *BackupData) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6089,7 +6353,7 @@ func (m *ConfigItem) Reset() { *m = ConfigItem{} } func (m *ConfigItem) String() string { return proto.CompactTextString(m) } func (*ConfigItem) ProtoMessage() {} func (*ConfigItem) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{59} + return fileDescriptor_fd1040c5381ab5a7, []int{60} } func (m *ConfigItem) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6159,7 +6423,7 @@ func (m *ConfigData) Reset() { *m = ConfigData{} } func (m *ConfigData) String() string { return proto.CompactTextString(m) } func (*ConfigData) ProtoMessage() {} func (*ConfigData) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{60} + return fileDescriptor_fd1040c5381ab5a7, []int{61} } func (m *ConfigData) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6208,7 +6472,7 @@ func (m *Locality) Reset() { *m = Locality{} } func (m *Locality) String() string { return proto.CompactTextString(m) } func (*Locality) ProtoMessage() {} func (*Locality) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{61} + return fileDescriptor_fd1040c5381ab5a7, []int{62} } func (m *Locality) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6255,7 +6519,7 @@ func (m *CheckHealth) Reset() { *m = CheckHealth{} } func (m *CheckHealth) String() string { return proto.CompactTextString(m) } func (*CheckHealth) ProtoMessage() {} func (*CheckHealth) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{62} + return fileDescriptor_fd1040c5381ab5a7, []int{63} } func (m *CheckHealth) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6307,7 +6571,7 @@ func (m *ScheduleCommandID) Reset() { *m = ScheduleCommandID{} } func (m *ScheduleCommandID) String() string { return proto.CompactTextString(m) } func (*ScheduleCommandID) ProtoMessage() {} func (*ScheduleCommandID) Descriptor() ([]byte, []int) { - return fileDescriptor_fd1040c5381ab5a7, []int{63} + return fileDescriptor_fd1040c5381ab5a7, []int{64} } func (m *ScheduleCommandID) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -6370,6 +6634,7 @@ func init() { proto.RegisterMapType((map[uint64]string)(nil), "logservice.LogShardInfo.ReplicasEntry") proto.RegisterType((*LogReplicaInfo)(nil), "logservice.LogReplicaInfo") proto.RegisterType((*Resource)(nil), "logservice.Resource") + proto.RegisterType((*DDLVisibilityActivationTarget)(nil), "logservice.DDLVisibilityActivationTarget") proto.RegisterType((*CNStoreHeartbeat)(nil), "logservice.CNStoreHeartbeat") proto.RegisterType((*CNAllocateID)(nil), "logservice.CNAllocateID") proto.RegisterType((*LogStoreHeartbeat)(nil), "logservice.LogStoreHeartbeat") @@ -6465,378 +6730,395 @@ func init() { func init() { proto.RegisterFile("logservice.proto", fileDescriptor_fd1040c5381ab5a7) } var fileDescriptor_fd1040c5381ab5a7 = []byte{ - // 5923 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x3d, 0x4d, 0x6f, 0x23, 0xc9, - 0x75, 0xd3, 0x24, 0x25, 0x91, 0x8f, 0x92, 0xa6, 0x55, 0xfa, 0x18, 0x8e, 0x66, 0x46, 0xa3, 0xe5, - 0x8e, 0x77, 0x67, 0xe5, 0xb5, 0xc6, 0x99, 0xf1, 0x2e, 0xd6, 0xce, 0x78, 0x76, 0x29, 0x92, 0x3b, - 0xe2, 0x0c, 0x45, 0x69, 0x9b, 0xad, 0x5d, 0xdb, 0xc0, 0x42, 0x68, 0x89, 0x35, 0x1a, 0x5a, 0x14, - 0x9b, 0xee, 0x6e, 0xcd, 0x47, 0x72, 0x0b, 0xe2, 0x20, 0x88, 0x81, 0x20, 0x06, 0x8c, 0xd8, 0x49, - 0xec, 0x7c, 0xdd, 0x72, 0xf2, 0x25, 0xf9, 0x0b, 0x81, 0x4f, 0x86, 0xe1, 0x63, 0x80, 0x38, 0xb6, - 0x73, 0x09, 0xe0, 0x6b, 0x10, 0x23, 0x87, 0x00, 0x41, 0x7d, 0x75, 0x57, 0x75, 0x57, 0x93, 0xd4, - 0xc7, 0x3a, 0xb1, 0xe1, 0x93, 0x58, 0xef, 0xa3, 0x3e, 0xdf, 0x7b, 0xf5, 0xea, 0xd5, 0xab, 0x16, - 0x98, 0x3d, 0xf7, 0xd0, 0xc7, 0xde, 0xb3, 0xee, 0x01, 0x5e, 0x1f, 0x78, 0x6e, 0xe0, 0x22, 0x88, - 0x20, 0xcb, 0x9f, 0x39, 0xec, 0x06, 0x4f, 0x4f, 0xf6, 0xd7, 0x0f, 0xdc, 0xe3, 0x3b, 0x87, 0xee, - 0xa1, 0x7b, 0x87, 0x92, 0xec, 0x9f, 0x3c, 0xa1, 0x25, 0x5a, 0xa0, 0xbf, 0x18, 0xeb, 0xf2, 0xcd, - 0x43, 0xd7, 0x3d, 0xec, 0xe1, 0x88, 0x2a, 0xe8, 0x1e, 0x63, 0x3f, 0x70, 0x8e, 0x07, 0x9c, 0x60, - 0xf6, 0x18, 0x07, 0x4e, 0xc7, 0x09, 0x1c, 0x56, 0x2e, 0xff, 0x70, 0x0a, 0xa6, 0xaa, 0xad, 0x76, - 0xe0, 0x7a, 0x18, 0x21, 0xc8, 0xed, 0xee, 0x36, 0x6a, 0x25, 0x63, 0xd5, 0xb8, 0x5d, 0xb0, 0xe8, - 0x6f, 0xf4, 0x1a, 0xcc, 0xb6, 0x59, 0x57, 0x2a, 0x9d, 0x8e, 0x87, 0x7d, 0xbf, 0x94, 0xa1, 0xd8, - 0x18, 0x14, 0xad, 0x00, 0xb4, 0x3f, 0x68, 0x0a, 0x9a, 0x2c, 0xa5, 0x91, 0x20, 0x68, 0x1d, 0x50, - 0xd3, 0x3d, 0x38, 0x8a, 0xd5, 0x95, 0xa3, 0x74, 0x1a, 0x0c, 0xba, 0x05, 0x39, 0xcb, 0xed, 0xe1, - 0xd2, 0xe4, 0xaa, 0x71, 0x7b, 0xf6, 0xae, 0xb9, 0x1e, 0x76, 0xbb, 0xda, 0x22, 0x70, 0x8b, 0x62, - 0x49, 0x8f, 0xed, 0xee, 0xc1, 0x51, 0x69, 0x6a, 0xd5, 0xb8, 0x9d, 0xb3, 0xe8, 0x6f, 0xf4, 0x69, - 0x98, 0x68, 0x07, 0x4e, 0x80, 0x4b, 0x79, 0xca, 0xba, 0xb8, 0x2e, 0xcd, 0x6f, 0xcb, 0xed, 0x60, - 0x8a, 0xb4, 0x18, 0x0d, 0xfa, 0x22, 0x4c, 0x36, 0x9d, 0x7d, 0xdc, 0xf3, 0x4b, 0x85, 0xd5, 0xec, - 0xed, 0xe2, 0xdd, 0x9b, 0x32, 0x35, 0x9f, 0x97, 0x75, 0x46, 0x51, 0xef, 0x07, 0xde, 0xcb, 0x8d, - 0xdc, 0x0f, 0x7e, 0x72, 0xf3, 0x92, 0xc5, 0x99, 0xd0, 0xef, 0x40, 0xe1, 0x23, 0xd7, 0x3b, 0x62, - 0xed, 0x01, 0x6d, 0x6f, 0x3e, 0xea, 0x6a, 0x88, 0xb2, 0x22, 0x2a, 0x54, 0x86, 0xe9, 0x0f, 0x4e, - 0xb0, 0xf7, 0x52, 0x4c, 0x41, 0x91, 0x4e, 0x81, 0x02, 0x43, 0x6f, 0x03, 0x54, 0xdd, 0xfe, 0x93, - 0xee, 0x61, 0xcd, 0x09, 0x9c, 0xd2, 0xf4, 0xaa, 0x71, 0xbb, 0x78, 0x77, 0x49, 0xe9, 0x59, 0x88, - 0xb5, 0x24, 0x4a, 0xf4, 0x36, 0xe4, 0x2d, 0xec, 0xbb, 0x27, 0xde, 0x01, 0x2e, 0xcd, 0x50, 0xae, - 0x05, 0x99, 0x4b, 0xe0, 0xf8, 0x20, 0x42, 0x5a, 0xb4, 0x04, 0x93, 0xbb, 0x03, 0xbb, 0x7b, 0x8c, - 0x4b, 0xb3, 0xab, 0xc6, 0xed, 0xac, 0xc5, 0x4b, 0xe8, 0xb3, 0x30, 0xdf, 0x7e, 0xea, 0x78, 0x9d, - 0xd8, 0xaa, 0x5d, 0xa6, 0x5d, 0xd6, 0xa1, 0xd0, 0x32, 0xe4, 0xab, 0xee, 0xf1, 0x71, 0x37, 0x68, - 0xd4, 0x4a, 0x26, 0x25, 0x0b, 0xcb, 0xe8, 0x7d, 0x58, 0xf9, 0xb0, 0x8b, 0x9f, 0x6f, 0xf1, 0xe9, - 0xa9, 0x74, 0x8e, 0xbb, 0xbe, 0xdf, 0x75, 0xfb, 0xed, 0x93, 0xc1, 0xc0, 0xf5, 0x02, 0xdc, 0x29, - 0xcd, 0xad, 0x1a, 0xb7, 0xf3, 0xd6, 0x08, 0x2a, 0xb4, 0x09, 0x37, 0xb5, 0x14, 0x0f, 0x71, 0x1f, - 0x7b, 0x4e, 0xd0, 0x75, 0xfb, 0x25, 0x44, 0xe5, 0x61, 0x14, 0x19, 0xba, 0x0f, 0x57, 0x65, 0x92, - 0xed, 0x7d, 0x32, 0x53, 0xb8, 0x53, 0x1f, 0xb8, 0x07, 0x4f, 0x4b, 0xf3, 0xb4, 0x8e, 0x74, 0x02, - 0xf4, 0x00, 0x96, 0xb5, 0x0d, 0x58, 0xd8, 0xe9, 0xbc, 0x2c, 0x2d, 0xd0, 0xb1, 0x0c, 0xa1, 0x58, - 0x6e, 0x41, 0x51, 0x92, 0x2c, 0x64, 0x42, 0xf6, 0x08, 0xbf, 0xe4, 0xca, 0x47, 0x7e, 0xa2, 0x37, - 0x60, 0xe2, 0x99, 0xd3, 0x3b, 0xc1, 0x54, 0xe5, 0x8a, 0xb2, 0x64, 0x51, 0xbe, 0x66, 0xd7, 0x0f, - 0x2c, 0x46, 0xf1, 0x85, 0xcc, 0x3b, 0xc6, 0xa3, 0x5c, 0x7e, 0xc2, 0x9c, 0x2c, 0xff, 0x32, 0x0b, - 0x53, 0xf6, 0x05, 0x28, 0xb4, 0x50, 0xad, 0xac, 0x4e, 0xb5, 0x72, 0x63, 0xa8, 0xd6, 0x5b, 0x30, - 0x49, 0x25, 0xc4, 0x2f, 0x4d, 0x50, 0xd5, 0xba, 0x22, 0x53, 0xdb, 0x2d, 0x8a, 0x6b, 0xf4, 0x9f, - 0xb8, 0x42, 0xa5, 0x18, 0x31, 0xba, 0x0b, 0x0b, 0x4d, 0xf7, 0x30, 0x70, 0xba, 0x3d, 0xd2, 0x21, - 0xec, 0x89, 0x5e, 0x4e, 0xd2, 0x5e, 0x6a, 0x71, 0x29, 0xc6, 0x65, 0x2a, 0xd5, 0xb8, 0xa8, 0xfa, - 0x55, 0x18, 0x5b, 0xbf, 0xe2, 0xba, 0x0b, 0x1a, 0xdd, 0x4d, 0xd1, 0x99, 0x62, 0xba, 0xce, 0xbc, - 0x07, 0xd7, 0x2a, 0x27, 0x81, 0xdb, 0xe8, 0x1f, 0x78, 0x54, 0xb0, 0xde, 0xc7, 0xfd, 0x03, 0x1c, - 0x29, 0xc5, 0x34, 0x15, 0xa4, 0x61, 0x24, 0x8f, 0x72, 0xf9, 0xbc, 0x59, 0x28, 0xff, 0x53, 0x06, - 0xf2, 0x4d, 0xf7, 0xf0, 0xff, 0xc1, 0xd2, 0xdf, 0x27, 0x76, 0x68, 0xd0, 0xeb, 0x1e, 0x38, 0x62, - 0xf1, 0x97, 0x65, 0xfa, 0xa6, 0x7b, 0xc8, 0xd1, 0xd2, 0xfa, 0x87, 0x1c, 0xb1, 0xd5, 0x99, 0x3c, - 0x8d, 0xf5, 0x6b, 0xba, 0x07, 0x4e, 0xaf, 0x1b, 0xbc, 0xa4, 0x6b, 0x1f, 0xb3, 0x7e, 0x02, 0x27, - 0xda, 0x13, 0xe5, 0xf2, 0xb7, 0xb2, 0x30, 0x4d, 0xe6, 0x4d, 0x08, 0x24, 0x2a, 0xc1, 0x14, 0x2b, - 0xb0, 0xe9, 0xcb, 0x59, 0xa2, 0x88, 0x36, 0xa4, 0x81, 0x65, 0xe8, 0xc0, 0x5e, 0x8b, 0x0d, 0x2c, - 0xac, 0x65, 0x5d, 0x10, 0x52, 0xed, 0x96, 0x86, 0xb7, 0x00, 0x13, 0xcc, 0xc0, 0xb0, 0xe9, 0x65, - 0x05, 0x62, 0x38, 0x9b, 0xd8, 0xe9, 0x60, 0xaf, 0x51, 0xa3, 0x53, 0x9c, 0xb3, 0xc2, 0x32, 0x5d, - 0x0f, 0xec, 0x1d, 0x97, 0x26, 0xf8, 0x7a, 0x60, 0xef, 0x18, 0x7d, 0x0c, 0x73, 0x2d, 0xb7, 0xff, - 0xa1, 0x1b, 0x74, 0xfb, 0x87, 0x61, 0x97, 0x26, 0x69, 0x97, 0xee, 0xa4, 0x76, 0x29, 0xc1, 0xc1, - 0xfa, 0x96, 0xac, 0x69, 0xf9, 0x77, 0x61, 0x46, 0xa1, 0x91, 0xad, 0x53, 0x8e, 0x59, 0xa7, 0x05, - 0xd9, 0x3a, 0x15, 0x24, 0x43, 0xb4, 0x5c, 0x83, 0x25, 0x7d, 0x4b, 0xa7, 0xa9, 0xa5, 0xfc, 0x1d, - 0x03, 0x66, 0x55, 0x49, 0x41, 0xef, 0xab, 0x0b, 0x45, 0xeb, 0x29, 0xde, 0x2d, 0xa5, 0x8d, 0x77, - 0x23, 0x4f, 0x56, 0xfa, 0x47, 0x3f, 0xb9, 0x69, 0x58, 0xea, 0x02, 0x5f, 0x87, 0x82, 0xa8, 0xb6, - 0x46, 0x1b, 0xce, 0x59, 0x11, 0x00, 0xad, 0x42, 0xb1, 0xe1, 0x87, 0x03, 0xa0, 0xcb, 0x94, 0xb7, - 0x64, 0x50, 0xf9, 0x4f, 0x8c, 0x68, 0xa3, 0xa5, 0x5b, 0xde, 0xce, 0xae, 0xed, 0x06, 0x4e, 0x8f, - 0x0f, 0x2c, 0x2c, 0x13, 0x83, 0x51, 0xdd, 0xd9, 0xad, 0x3c, 0x73, 0xba, 0x3d, 0x67, 0xbf, 0xc7, - 0x06, 0x69, 0x58, 0x0a, 0x8c, 0xf0, 0x6f, 0xe1, 0x63, 0xc6, 0xcf, 0x44, 0x22, 0x2c, 0x13, 0xfe, - 0x2d, 0x7c, 0x1c, 0xf1, 0x33, 0xc9, 0x50, 0x60, 0xe5, 0xef, 0x16, 0xc0, 0xe4, 0x9e, 0xca, 0x26, - 0x76, 0xbc, 0x60, 0x1f, 0x3b, 0xc1, 0xaf, 0xa1, 0x2b, 0xb7, 0x0e, 0xc8, 0x76, 0x7c, 0xc1, 0x5b, - 0xf5, 0xb0, 0x43, 0x8c, 0xdf, 0x14, 0x9d, 0x7c, 0x0d, 0x26, 0x61, 0x8b, 0xf3, 0x1a, 0x5b, 0x7c, - 0x0b, 0x66, 0x1a, 0xfd, 0x6e, 0x10, 0xb9, 0x68, 0x05, 0x4a, 0xa4, 0x02, 0x09, 0xd5, 0x43, 0xd7, - 0xf7, 0xbb, 0x03, 0xd5, 0xac, 0xab, 0x40, 0xd2, 0x1e, 0x03, 0x3c, 0x72, 0xbb, 0x7d, 0xdc, 0xa1, - 0x06, 0x3d, 0x6f, 0x29, 0xb0, 0x5f, 0xb9, 0xdf, 0x96, 0xb2, 0xd7, 0xcc, 0x8e, 0xe7, 0x9f, 0x5d, - 0x8e, 0xf9, 0x67, 0x9f, 0x85, 0xf9, 0xca, 0xc1, 0x11, 0xee, 0x10, 0x80, 0xd3, 0xef, 0x6c, 0x38, - 0xc1, 0xc1, 0x53, 0xee, 0xc6, 0xe5, 0x2c, 0x1d, 0x8a, 0xec, 0x5c, 0x1c, 0x52, 0xc3, 0xbd, 0xee, - 0x33, 0x32, 0xf3, 0x07, 0x47, 0x71, 0x77, 0x6e, 0x18, 0xc9, 0x18, 0x3e, 0x21, 0xba, 0x28, 0x9f, - 0x70, 0xfe, 0x02, 0x7c, 0xc2, 0x85, 0x51, 0x3e, 0x61, 0x6c, 0x3c, 0x55, 0x27, 0x70, 0x7a, 0xee, - 0x21, 0xdd, 0xae, 0x79, 0x15, 0x8b, 0xb4, 0x8a, 0x11, 0x54, 0x68, 0x03, 0xae, 0xcb, 0x14, 0x16, - 0x7e, 0xe2, 0x61, 0xff, 0x69, 0x34, 0x2b, 0x4b, 0x74, 0x56, 0x86, 0xd2, 0x24, 0xeb, 0x78, 0xe6, - 0xf4, 0xba, 0x1d, 0xa2, 0x3c, 0xac, 0x27, 0x57, 0x68, 0x4f, 0x86, 0xd2, 0xa0, 0x2f, 0x40, 0x49, - 0xc6, 0x37, 0xfa, 0x87, 0x44, 0x8c, 0x98, 0x87, 0x5b, 0xa2, 0x7d, 0x48, 0xc5, 0x73, 0x7f, 0xd4, - 0x86, 0xe9, 0x6a, 0xab, 0xd2, 0xeb, 0xb9, 0x07, 0x4e, 0x80, 0x1b, 0x35, 0x8d, 0x9b, 0xbb, 0x00, - 0x13, 0x54, 0xa0, 0xb8, 0x25, 0x66, 0x05, 0x66, 0xa3, 0xbf, 0x76, 0x82, 0x7d, 0x22, 0xaa, 0xcc, - 0x08, 0x45, 0x80, 0xf2, 0x7f, 0x67, 0x61, 0x4e, 0xf8, 0x3a, 0xc3, 0xad, 0xde, 0x2a, 0x14, 0x2d, - 0xe7, 0x49, 0xa0, 0x9a, 0x3c, 0x19, 0xa4, 0xb1, 0x8b, 0x59, 0xad, 0x5d, 0x4c, 0xd8, 0x89, 0x9c, - 0xce, 0x4e, 0x9c, 0xcf, 0xf7, 0xd1, 0x5b, 0xc1, 0xc9, 0x54, 0x2b, 0xa8, 0x5a, 0x9c, 0xa9, 0x33, - 0xf9, 0x4a, 0xf9, 0xf1, 0x7d, 0x25, 0x22, 0x0f, 0x31, 0x75, 0x8e, 0x64, 0xb2, 0xc0, 0xe4, 0x21, - 0x0d, 0x3f, 0x86, 0xae, 0xc3, 0x38, 0xba, 0x5e, 0xae, 0x43, 0x51, 0x3a, 0x3e, 0x0c, 0xf1, 0xd6, - 0x86, 0x6e, 0xf3, 0xe5, 0xaf, 0x4f, 0x80, 0x69, 0x5f, 0xe4, 0xbe, 0x19, 0x1d, 0x78, 0xb2, 0xa7, - 0x39, 0xf0, 0xe8, 0x97, 0x3c, 0x97, 0xba, 0xe4, 0x69, 0x07, 0xa4, 0x89, 0x53, 0x1f, 0x90, 0x26, - 0xc7, 0x3c, 0x20, 0xe5, 0xcf, 0x7c, 0x40, 0x2a, 0x8c, 0x7f, 0x40, 0x82, 0xf4, 0x4d, 0x8b, 0xa8, - 0x30, 0x1e, 0xf4, 0x9c, 0x97, 0xb8, 0xd3, 0xf4, 0xfb, 0x74, 0xe7, 0xcd, 0x59, 0x32, 0xe8, 0xfc, - 0x47, 0xa8, 0xb4, 0xcd, 0x6f, 0xe6, 0xcc, 0x9b, 0xdf, 0xec, 0xc8, 0xcd, 0xef, 0x51, 0x2e, 0x3f, - 0x65, 0xe6, 0xcb, 0xdf, 0x9f, 0x80, 0xbc, 0xd5, 0xde, 0x62, 0xbe, 0x88, 0x09, 0x59, 0xdb, 0x77, - 0x85, 0x83, 0x6c, 0xfb, 0x2e, 0xb1, 0x8e, 0x8d, 0x7e, 0x07, 0xbf, 0x10, 0xd6, 0x91, 0x16, 0x88, - 0x2d, 0x6a, 0x62, 0xc7, 0xc7, 0x9b, 0x6e, 0x8f, 0x9d, 0x19, 0x98, 0xe7, 0xa8, 0x02, 0xc9, 0x72, - 0xd8, 0xde, 0x49, 0x9f, 0x58, 0x5e, 0x3a, 0x73, 0xdc, 0x7d, 0x94, 0x61, 0xe8, 0x11, 0x4c, 0x33, - 0xa6, 0xae, 0x1f, 0xb8, 0xde, 0x4b, 0x6e, 0xb3, 0x94, 0x63, 0x8d, 0xe8, 0xdd, 0xba, 0x4c, 0xc8, - 0x8e, 0x0e, 0x0a, 0x2f, 0x5b, 0xa8, 0xaf, 0x9d, 0x74, 0x3d, 0xd6, 0xdc, 0xa4, 0x58, 0xa8, 0x10, - 0x84, 0xde, 0x84, 0xb9, 0x8f, 0x2a, 0x4d, 0x0b, 0x1f, 0xb8, 0x64, 0x36, 0x6a, 0xdd, 0x43, 0xec, - 0x07, 0xfc, 0xa0, 0x9e, 0x44, 0xa0, 0xcf, 0xc1, 0xa2, 0x04, 0xa4, 0x2d, 0x56, 0xdd, 0x93, 0x7e, - 0x40, 0x25, 0x32, 0x67, 0xe9, 0x91, 0x64, 0x61, 0x24, 0x44, 0xd5, 0x3d, 0x1e, 0xf4, 0x30, 0xd9, - 0xd0, 0xfa, 0x81, 0xd7, 0xc5, 0x4c, 0x26, 0x73, 0xd6, 0x30, 0x12, 0xa2, 0x2e, 0x12, 0x7a, 0xc3, - 0xf1, 0x31, 0x19, 0x0e, 0x50, 0x46, 0x0d, 0x26, 0x46, 0xdf, 0x74, 0xfc, 0x20, 0x92, 0x53, 0x0d, - 0x06, 0x3d, 0x82, 0x55, 0x09, 0xba, 0xed, 0x75, 0x0f, 0xbb, 0x7d, 0xa7, 0xa7, 0x2e, 0xe8, 0x34, - 0xe5, 0x1e, 0x49, 0x47, 0x04, 0x57, 0x33, 0x14, 0x2a, 0xb8, 0x79, 0x4b, 0x87, 0x5a, 0x7e, 0x17, - 0xe6, 0x12, 0x0b, 0x39, 0xea, 0x64, 0x96, 0x93, 0x4f, 0x66, 0x1f, 0x43, 0x81, 0x6e, 0x63, 0x07, - 0xae, 0xd7, 0x21, 0x8c, 0x64, 0xb0, 0x9c, 0x91, 0x8c, 0x6e, 0x0d, 0x72, 0xf6, 0xcb, 0x01, 0xe3, - 0x9b, 0x55, 0xcd, 0x06, 0xe3, 0x21, 0x58, 0x8b, 0xd2, 0x10, 0x7b, 0x4b, 0x4d, 0x0c, 0x11, 0xdf, - 0x69, 0x8b, 0xfe, 0x2e, 0xff, 0x34, 0x03, 0x40, 0xeb, 0xa7, 0x9b, 0x3d, 0x21, 0x69, 0x39, 0xc7, - 0x58, 0x98, 0x64, 0xf2, 0x5b, 0xb6, 0xf9, 0x19, 0xd5, 0xe6, 0xf3, 0xee, 0x64, 0xa3, 0xee, 0x94, - 0x60, 0x6a, 0xcb, 0x79, 0xd1, 0xee, 0xfe, 0x9e, 0x38, 0x3e, 0x89, 0x22, 0xd9, 0x1f, 0x84, 0x59, - 0xae, 0xf1, 0xc3, 0x75, 0x04, 0xa0, 0xa7, 0xee, 0x56, 0xa3, 0xc6, 0xa5, 0x98, 0xfe, 0x46, 0x9f, - 0x83, 0x8c, 0xdd, 0xe6, 0xdb, 0xec, 0xf2, 0x3a, 0x8b, 0xb5, 0xaf, 0x8b, 0x58, 0xfb, 0xba, 0x2d, - 0x62, 0xed, 0xec, 0xe0, 0xf9, 0x67, 0xff, 0x76, 0xd3, 0xb0, 0x32, 0x76, 0x9b, 0x6c, 0x7c, 0x8d, - 0xfe, 0x41, 0xef, 0xa4, 0x83, 0xeb, 0x2f, 0x06, 0x44, 0x13, 0xf8, 0x26, 0xc4, 0xed, 0x1b, 0x66, - 0x87, 0x97, 0xbc, 0x35, 0x82, 0x8a, 0x38, 0xb9, 0xf5, 0x17, 0x94, 0x62, 0xd3, 0xf1, 0x3a, 0x35, - 0xf7, 0x79, 0x3f, 0x51, 0x11, 0xdb, 0x83, 0x47, 0x91, 0x95, 0xcb, 0x00, 0xb6, 0xef, 0x8a, 0x19, - 0x5e, 0x80, 0x09, 0xa6, 0x56, 0x6c, 0x11, 0x59, 0xa1, 0xfc, 0x0b, 0x83, 0x78, 0x6e, 0x74, 0x7f, - 0xa4, 0xe1, 0x46, 0xed, 0xde, 0x78, 0x0f, 0x0a, 0xdb, 0x03, 0xe1, 0x61, 0x67, 0x92, 0xa1, 0xa1, - 0x6a, 0x8b, 0xf2, 0x6e, 0x0f, 0xac, 0x88, 0x0e, 0x6d, 0x84, 0x41, 0x77, 0xb6, 0x51, 0xde, 0xd2, - 0x04, 0xdd, 0x29, 0x41, 0x7a, 0xe4, 0xfd, 0xa2, 0x83, 0xa7, 0xe5, 0x26, 0x14, 0xab, 0xad, 0xe8, - 0x4c, 0xa8, 0x1b, 0xeb, 0x1b, 0x22, 0x04, 0x96, 0x49, 0x0f, 0xf4, 0x33, 0x8a, 0xf2, 0xcf, 0xf8, - 0xdc, 0x39, 0xc1, 0x90, 0xb9, 0x1b, 0xbf, 0xbe, 0xd1, 0x33, 0x26, 0x1a, 0xfa, 0x15, 0xce, 0xd8, - 0x37, 0xf3, 0x30, 0x25, 0x24, 0x48, 0x71, 0xd6, 0x0d, 0xe1, 0x69, 0x71, 0x00, 0x5a, 0x87, 0xc9, - 0x2d, 0x1c, 0x3c, 0x75, 0x3b, 0x3a, 0x93, 0xc0, 0x30, 0xd4, 0x24, 0x70, 0x2a, 0x74, 0x5f, 0xd6, - 0x7f, 0xaa, 0xca, 0x31, 0xef, 0x23, 0xc2, 0xf2, 0x31, 0xca, 0xf6, 0xa2, 0x42, 0x83, 0x44, 0xa1, - 0x4b, 0x47, 0x95, 0xbe, 0x78, 0xf7, 0x46, 0x3c, 0x48, 0xa4, 0xf8, 0x7d, 0x96, 0xc2, 0x82, 0x1e, - 0x10, 0x61, 0x88, 0x6a, 0x98, 0xa0, 0x35, 0x5c, 0xd7, 0x48, 0x69, 0x54, 0x81, 0xcc, 0x40, 0xf8, - 0x6d, 0x89, 0x7f, 0x32, 0xc9, 0x6f, 0x27, 0xf8, 0x25, 0x06, 0xe2, 0x7e, 0x45, 0xea, 0xa9, 0xf3, - 0xea, 0x23, 0xac, 0x25, 0x2b, 0xf2, 0x7d, 0xf5, 0xac, 0xc5, 0x1d, 0xb7, 0x92, 0xda, 0xf1, 0x08, - 0x6f, 0xa9, 0x27, 0xb3, 0xfb, 0xaa, 0xbe, 0xf3, 0xb8, 0x78, 0x29, 0x4d, 0x39, 0x2d, 0xd5, 0x3a, - 0x7c, 0x5e, 0x51, 0x20, 0xba, 0x59, 0xc6, 0x5c, 0x60, 0x09, 0x6d, 0x29, 0xca, 0x76, 0x5f, 0x55, - 0x16, 0xba, 0x71, 0x6a, 0x1a, 0x16, 0x78, 0x4b, 0x55, 0xad, 0x77, 0x61, 0xa6, 0x86, 0xc9, 0xc6, - 0xc6, 0xbb, 0xc3, 0xe3, 0x2e, 0x57, 0x65, 0x76, 0x85, 0xc0, 0x52, 0xe9, 0xd1, 0x06, 0xcc, 0xee, - 0x78, 0xee, 0x8b, 0x97, 0xd1, 0x82, 0xcd, 0x70, 0x03, 0x2f, 0xd5, 0xa0, 0x52, 0x58, 0x31, 0x0e, - 0xb2, 0x0b, 0xc7, 0x43, 0x9e, 0xad, 0x93, 0x63, 0xea, 0x04, 0xe6, 0x2c, 0x1d, 0x0a, 0x6d, 0x48, - 0x01, 0xdc, 0xf0, 0x28, 0x76, 0x39, 0xfd, 0x28, 0x66, 0x25, 0xc9, 0xe9, 0x9c, 0x3f, 0xc5, 0x07, - 0x47, 0x9b, 0xd8, 0xe9, 0x05, 0x4f, 0x69, 0xa4, 0x26, 0x3e, 0xe7, 0x11, 0xda, 0x92, 0x69, 0x91, - 0x0d, 0x0b, 0xed, 0x83, 0xa7, 0xb8, 0x73, 0xd2, 0xc3, 0xdc, 0x45, 0xa5, 0x4e, 0x3a, 0x8d, 0xd9, - 0x14, 0xef, 0xae, 0xca, 0x75, 0xe8, 0xe8, 0x2c, 0x2d, 0x77, 0xd9, 0x85, 0x22, 0xd5, 0x44, 0x7f, - 0xe0, 0xf6, 0x7d, 0x3c, 0xe4, 0x68, 0xc6, 0xb7, 0xe9, 0x8c, 0xb2, 0x4d, 0x0b, 0xc7, 0x89, 0x6d, - 0xde, 0xa2, 0x38, 0x2c, 0x34, 0x5e, 0x5e, 0x07, 0x24, 0xc9, 0xb3, 0xd4, 0xee, 0xfb, 0x5d, 0x4f, - 0x32, 0x46, 0xa2, 0x58, 0xfe, 0xaf, 0x1c, 0x0d, 0xb5, 0x31, 0xb2, 0x8b, 0xb5, 0x5a, 0xd7, 0xa1, - 0x50, 0xf7, 0x3c, 0xd7, 0xab, 0xba, 0x1d, 0x4c, 0x87, 0x30, 0x63, 0x45, 0x00, 0xe2, 0x8a, 0xd3, - 0xc2, 0x16, 0xf6, 0x7d, 0xe7, 0x10, 0xf3, 0xd8, 0x81, 0x02, 0x43, 0x2b, 0x00, 0x0d, 0x7f, 0xb3, - 0xf2, 0x18, 0xe3, 0x01, 0xf6, 0xa8, 0xd5, 0xc9, 0x5b, 0x12, 0x04, 0xbd, 0xab, 0xcc, 0x2e, 0x37, - 0x2b, 0x57, 0x12, 0x86, 0x91, 0xa1, 0xb9, 0x65, 0x54, 0xd6, 0x83, 0x28, 0x9a, 0x74, 0x88, 0xe1, - 0x96, 0x45, 0x55, 0x34, 0x09, 0x6f, 0x29, 0xd4, 0x44, 0xda, 0xa8, 0xad, 0xe1, 0xcd, 0xe7, 0x93, - 0xcd, 0x4b, 0x68, 0x4b, 0xa6, 0x25, 0x2a, 0x56, 0xed, 0x9d, 0xf8, 0x01, 0xf6, 0x6a, 0x98, 0x9c, - 0x4e, 0x7d, 0x6e, 0x5c, 0x14, 0x15, 0x53, 0x29, 0xac, 0x18, 0x07, 0x7a, 0x00, 0x85, 0x28, 0xf2, - 0x0f, 0x1a, 0x31, 0x15, 0x48, 0x26, 0xa0, 0xd8, 0x3f, 0xe9, 0x05, 0x56, 0xc4, 0x82, 0x1e, 0x00, - 0x48, 0xa6, 0x91, 0xd9, 0x98, 0x15, 0xb9, 0x82, 0xa4, 0x20, 0x59, 0x10, 0x33, 0x8f, 0x44, 0x81, - 0xb0, 0xc7, 0x2c, 0xdc, 0xb4, 0x66, 0xf2, 0x24, 0xbc, 0xa5, 0x50, 0x97, 0x1f, 0xd1, 0x78, 0x15, - 0xf3, 0x7f, 0xc3, 0x69, 0x79, 0x8b, 0xec, 0xa0, 0x04, 0xe2, 0x97, 0x0c, 0xba, 0xaf, 0x2f, 0x26, - 0x16, 0x93, 0x60, 0xf9, 0x52, 0x0a, 0xda, 0xf2, 0xab, 0xca, 0x42, 0x10, 0xf7, 0xed, 0x43, 0xba, - 0x6f, 0x73, 0xf7, 0x8d, 0x16, 0xca, 0x0f, 0x61, 0xc6, 0x76, 0xfc, 0x23, 0xdb, 0xd9, 0xef, 0xe1, - 0x5d, 0x1f, 0x7b, 0x44, 0x8d, 0xc8, 0xdf, 0x7e, 0xe4, 0x4b, 0x87, 0x65, 0x82, 0xdb, 0x71, 0x7c, - 0xff, 0xb9, 0xeb, 0x75, 0x78, 0x70, 0x23, 0x2c, 0x97, 0xbf, 0x61, 0x90, 0x5e, 0x52, 0xbb, 0xa5, - 0x75, 0x63, 0xd2, 0x7d, 0x71, 0x25, 0xfe, 0x92, 0x8d, 0x5f, 0xb3, 0x84, 0xf7, 0x60, 0x39, 0xf9, - 0x1e, 0x6c, 0x85, 0xee, 0xfd, 0xaa, 0x53, 0x2e, 0x41, 0xca, 0x7f, 0x99, 0x21, 0x32, 0xdc, 0x7f, - 0xd2, 0x3d, 0xac, 0x3e, 0x75, 0xfa, 0x87, 0x18, 0xdd, 0x0b, 0x7b, 0xc7, 0xaf, 0x83, 0xe6, 0xd5, - 0x03, 0x07, 0x45, 0x45, 0x33, 0xc8, 0xc6, 0x71, 0x1f, 0x80, 0xb1, 0x4b, 0x07, 0x95, 0xeb, 0xc9, - 0xf8, 0x46, 0x44, 0x63, 0x49, 0xf4, 0xc8, 0x86, 0xd9, 0x46, 0xbf, 0x1b, 0x74, 0x9d, 0xde, 0x16, - 0x3e, 0xde, 0xc7, 0x9e, 0xf0, 0xca, 0xde, 0x4c, 0xab, 0x61, 0x5d, 0x25, 0x67, 0x47, 0xe7, 0x58, - 0x1d, 0xcb, 0x15, 0x98, 0xd7, 0x90, 0x9d, 0xea, 0xca, 0xec, 0x0d, 0x98, 0x69, 0x3f, 0x3d, 0x09, - 0x3a, 0xee, 0xf3, 0x3e, 0xdb, 0xda, 0xc8, 0xda, 0x90, 0x1f, 0xe1, 0x92, 0x89, 0x62, 0xf9, 0x1f, - 0x26, 0xe0, 0x72, 0xcc, 0x84, 0x6b, 0x57, 0xf7, 0x16, 0xcc, 0x6c, 0xb8, 0x6e, 0xe0, 0x07, 0x9e, - 0x33, 0x18, 0x74, 0xfb, 0x87, 0xb4, 0xd1, 0xbc, 0xa5, 0x02, 0x89, 0x69, 0xe0, 0x31, 0x1b, 0x3a, - 0xa1, 0x59, 0x3a, 0xa1, 0x8a, 0x69, 0x90, 0xd0, 0x96, 0x4c, 0xcb, 0x6c, 0x52, 0x34, 0x55, 0xdc, - 0x5d, 0x2b, 0xa5, 0x4d, 0xa5, 0xa5, 0xae, 0xfe, 0xbb, 0xb1, 0x11, 0x73, 0x5f, 0xed, 0xaa, 0x6a, - 0x18, 0x24, 0x02, 0x2b, 0x36, 0x43, 0x8f, 0x61, 0x8e, 0x05, 0xd6, 0xa4, 0x48, 0x1b, 0xb7, 0xac, - 0x8a, 0xcb, 0x98, 0x20, 0xb2, 0x92, 0x7c, 0x49, 0x57, 0x64, 0xea, 0x94, 0xae, 0xc8, 0x63, 0x98, - 0x7b, 0xe4, 0x76, 0xfb, 0x2c, 0xa2, 0xcc, 0xed, 0x1f, 0x37, 0xb4, 0x4a, 0x6f, 0x12, 0x44, 0x56, - 0x92, 0x0f, 0x6d, 0x82, 0xc9, 0x6a, 0xa7, 0xbe, 0x0a, 0xeb, 0x50, 0x21, 0xe9, 0x8a, 0xc6, 0x69, - 0xac, 0x04, 0x17, 0x59, 0xde, 0x4a, 0xa7, 0x23, 0xb4, 0x50, 0xe7, 0xdb, 0x49, 0x68, 0x4b, 0xa6, - 0x25, 0x96, 0x3f, 0x14, 0x15, 0xc6, 0x5d, 0x4c, 0x5a, 0x7e, 0x95, 0xc2, 0x8a, 0x71, 0x94, 0xb1, - 0xde, 0x57, 0xd1, 0xca, 0x6b, 0x4c, 0x12, 0x33, 0xe3, 0x4b, 0x62, 0xf9, 0xcf, 0x33, 0xb0, 0x14, - 0x0b, 0xd7, 0xd9, 0x8e, 0x77, 0x88, 0x03, 0x7a, 0xf9, 0xc7, 0x97, 0x88, 0x34, 0xc2, 0xac, 0x75, - 0xc1, 0x52, 0x60, 0x34, 0xd8, 0x26, 0xd3, 0x64, 0x18, 0x8d, 0x0c, 0x23, 0x76, 0xb6, 0xfe, 0x82, - 0x98, 0xa0, 0x6e, 0xc0, 0xef, 0x95, 0xc3, 0x32, 0x71, 0x21, 0x79, 0x7d, 0x76, 0xf7, 0x18, 0xbb, - 0x27, 0x81, 0xdd, 0x3d, 0x38, 0xf2, 0xb9, 0x75, 0xd4, 0xa1, 0x08, 0x87, 0xad, 0xe1, 0x60, 0x46, - 0x53, 0x87, 0x42, 0x9f, 0x83, 0xc5, 0x3a, 0x31, 0x17, 0x4e, 0x80, 0xab, 0x27, 0x9e, 0x87, 0xfb, - 0x01, 0xa5, 0xf1, 0xf9, 0x0d, 0x83, 0x1e, 0x59, 0xbe, 0xa3, 0x91, 0x4a, 0x36, 0x94, 0xae, 0x4f, - 0xaf, 0xc8, 0xd9, 0x74, 0x84, 0xe5, 0x72, 0x4f, 0xa3, 0x54, 0xe8, 0x1e, 0xe4, 0xc8, 0x7e, 0xc3, - 0xad, 0xb4, 0xa2, 0x13, 0xca, 0x46, 0xc5, 0x6d, 0x35, 0x25, 0xa6, 0x93, 0xea, 0xf8, 0x47, 0x35, - 0x27, 0x70, 0xf6, 0x1d, 0x5f, 0x98, 0x3c, 0x05, 0x46, 0xac, 0x9e, 0xaa, 0x45, 0xe9, 0x56, 0xef, - 0xcd, 0xa4, 0x4a, 0x0c, 0xa1, 0x7e, 0x5d, 0x11, 0xfb, 0x74, 0x6f, 0xb6, 0xfc, 0x4b, 0x23, 0x2e, - 0xe5, 0x67, 0xbd, 0x95, 0x40, 0x1f, 0xa6, 0xec, 0x2d, 0xeb, 0xe9, 0xfa, 0x32, 0xce, 0xee, 0x42, - 0x74, 0x85, 0xac, 0x21, 0xbf, 0x57, 0xa0, 0xbf, 0x2f, 0x62, 0xc7, 0xf9, 0x8b, 0x8c, 0xea, 0x52, - 0x86, 0xb9, 0x2a, 0x86, 0x94, 0xab, 0xf2, 0x45, 0x76, 0xe9, 0xec, 0xf4, 0x3b, 0x22, 0x6b, 0xe6, - 0xda, 0x90, 0xf3, 0x85, 0xb8, 0x73, 0x12, 0x2c, 0x64, 0x2a, 0x45, 0x38, 0x9e, 0x9f, 0x0c, 0x44, - 0x08, 0xbe, 0x0a, 0xc0, 0xa9, 0x88, 0xc2, 0xe5, 0x68, 0xd5, 0x37, 0x86, 0x54, 0xdd, 0xa8, 0x89, - 0x78, 0x41, 0xc4, 0x86, 0x3e, 0x82, 0x45, 0xed, 0x85, 0x13, 0xdf, 0x4a, 0x5e, 0x91, 0xeb, 0xd3, - 0x67, 0xf3, 0xe9, 0xf9, 0xcb, 0x7f, 0x95, 0x49, 0xa9, 0x99, 0x88, 0xc0, 0x8e, 0x87, 0x07, 0x8e, - 0xc7, 0x94, 0x87, 0xac, 0x48, 0x04, 0x20, 0xe3, 0xad, 0xf7, 0x89, 0x36, 0x74, 0xf8, 0x66, 0x2b, - 0x8a, 0x29, 0xa9, 0x43, 0x77, 0x61, 0x21, 0xbc, 0xb7, 0xa5, 0xc9, 0x85, 0x2c, 0xdc, 0xce, 0x97, - 0x5a, 0x8b, 0x43, 0xeb, 0x80, 0x34, 0x77, 0xd3, 0xcc, 0x72, 0x68, 0x30, 0xc4, 0x2d, 0x93, 0xae, - 0xd2, 0x59, 0x48, 0x54, 0x82, 0x90, 0x9e, 0xb1, 0x4b, 0x61, 0x96, 0xb0, 0xc1, 0x0a, 0xc4, 0x46, - 0x90, 0x41, 0x07, 0x01, 0xee, 0xf0, 0x10, 0x67, 0x58, 0x2e, 0xff, 0x8b, 0xa1, 0x5e, 0x4f, 0x87, - 0xb3, 0x23, 0x6c, 0xae, 0x6c, 0x2b, 0x8d, 0xf1, 0x6c, 0x65, 0x26, 0xdd, 0x56, 0xbe, 0x0d, 0x4b, - 0x91, 0xce, 0x2b, 0x4c, 0x6c, 0x2e, 0x53, 0xb0, 0xe9, 0x16, 0x33, 0x37, 0xcc, 0x62, 0xfe, 0x2b, - 0x40, 0x91, 0xf7, 0x82, 0x9e, 0x3d, 0x44, 0x46, 0x9d, 0x21, 0x65, 0xd4, 0xfd, 0x66, 0xa5, 0xe3, - 0x54, 0xc2, 0x08, 0x65, 0x9e, 0xaa, 0xe1, 0xab, 0x9a, 0xb0, 0x11, 0xcd, 0x41, 0x1b, 0x33, 0x99, - 0xba, 0x70, 0xa6, 0x64, 0x6a, 0xd0, 0x27, 0x01, 0xa9, 0xd7, 0xf6, 0xc5, 0x71, 0xd2, 0x7b, 0xa6, - 0x47, 0xa6, 0xf7, 0xcc, 0x9c, 0x29, 0xbd, 0x67, 0xf6, 0x4c, 0x69, 0xd9, 0x97, 0xc7, 0x49, 0xcb, - 0x36, 0xc7, 0x4b, 0xfb, 0x99, 0x8b, 0xa5, 0xfd, 0x8c, 0xb8, 0xc7, 0x44, 0x17, 0x91, 0xc4, 0x33, - 0x7f, 0x51, 0x49, 0x3c, 0x0b, 0x17, 0x90, 0xc4, 0xb3, 0x78, 0xfe, 0x24, 0x9e, 0xa5, 0x0b, 0x49, - 0xe2, 0xb9, 0x72, 0x01, 0x49, 0x3c, 0xa5, 0x31, 0x92, 0x78, 0x86, 0x27, 0xaa, 0x5f, 0x1d, 0x95, - 0xa8, 0x3e, 0x34, 0x09, 0x68, 0x79, 0x78, 0x12, 0xd0, 0x27, 0x94, 0xe4, 0xfe, 0x5d, 0x83, 0xbd, - 0x5a, 0xe1, 0x4f, 0x38, 0xb8, 0x49, 0x36, 0xf4, 0x4f, 0x38, 0x9c, 0x00, 0xaf, 0x33, 0x0a, 0xc5, - 0xea, 0x30, 0xd0, 0xb2, 0x05, 0x45, 0x09, 0xa9, 0xe9, 0xe0, 0x67, 0xd4, 0x0e, 0x5e, 0x49, 0x31, - 0x6c, 0xb2, 0x57, 0xf4, 0xc3, 0x1c, 0x4d, 0x51, 0xb9, 0x10, 0xf3, 0xff, 0xdb, 0xac, 0x92, 0x5f, - 0xe3, 0xac, 0x92, 0x11, 0xb6, 0x75, 0x66, 0xdc, 0x1c, 0x11, 0x22, 0xef, 0xf6, 0x38, 0xf2, 0x6e, - 0x7f, 0xb2, 0xf2, 0x6e, 0xeb, 0xe5, 0xfd, 0xef, 0xb3, 0x00, 0xd2, 0x89, 0x4a, 0x77, 0x2e, 0x17, - 0x2a, 0x90, 0x91, 0x54, 0xe0, 0x16, 0xcc, 0x10, 0xf5, 0xc6, 0x7d, 0xd5, 0xb9, 0x51, 0x81, 0x31, - 0xa9, 0xc9, 0x8d, 0x2d, 0x35, 0xa3, 0x77, 0xa5, 0x89, 0x8b, 0xda, 0x95, 0x26, 0x2f, 0x60, 0x57, - 0x9a, 0x3a, 0xdf, 0x73, 0xa3, 0xfc, 0x28, 0x2b, 0x5e, 0xfe, 0x3b, 0x23, 0x5c, 0x24, 0x22, 0x46, - 0xef, 0xc5, 0xc4, 0xa8, 0x9c, 0xb8, 0xed, 0x1a, 0x25, 0x49, 0x1f, 0x8c, 0x92, 0xa4, 0x37, 0x55, - 0x49, 0x5a, 0xd2, 0xb4, 0xe0, 0x7a, 0x58, 0x16, 0xa4, 0x1f, 0x67, 0xe2, 0x77, 0x71, 0x69, 0x41, - 0x49, 0x55, 0x70, 0x32, 0xa3, 0x05, 0x27, 0x7b, 0x81, 0x82, 0x93, 0xbb, 0x28, 0xc1, 0x99, 0xb8, - 0x00, 0xc1, 0x99, 0x1c, 0x21, 0x38, 0xe5, 0x6f, 0x67, 0xe3, 0xb7, 0x2f, 0xe8, 0x2d, 0xc8, 0x73, - 0x55, 0x16, 0xcb, 0x3f, 0xaf, 0x51, 0x73, 0xe1, 0x90, 0x0a, 0x52, 0xc2, 0x56, 0x15, 0x6c, 0x99, - 0x24, 0x5b, 0x55, 0x65, 0x13, 0xa4, 0xe8, 0x1d, 0x9a, 0x2f, 0xc4, 0xf9, 0xd8, 0x2e, 0xb6, 0xa0, - 0xbb, 0x8e, 0xe7, 0x8c, 0x11, 0x31, 0x7a, 0x00, 0xc5, 0x48, 0x50, 0xc4, 0x09, 0x3f, 0x45, 0x8e, - 0xc4, 0x85, 0x97, 0xc4, 0x80, 0x6a, 0x22, 0x34, 0xd4, 0xe1, 0x35, 0xb0, 0xec, 0xb6, 0x52, 0x32, - 0xfe, 0xd9, 0x91, 0xeb, 0x50, 0x99, 0xd2, 0x23, 0x04, 0x93, 0xe7, 0x8c, 0x10, 0xfc, 0x91, 0x01, - 0x45, 0xbe, 0x32, 0xd4, 0x4f, 0xf8, 0x3c, 0x5d, 0x16, 0xb6, 0xdb, 0x1b, 0x7c, 0xb7, 0x0f, 0xdd, - 0x21, 0x8e, 0x51, 0xae, 0x84, 0x42, 0x72, 0x74, 0x9f, 0xcd, 0x31, 0xe3, 0xcd, 0xf0, 0x51, 0x46, - 0xae, 0x94, 0x88, 0xcd, 0xca, 0xcc, 0x11, 0x43, 0xf9, 0x7b, 0x39, 0x58, 0xe4, 0xa1, 0x20, 0x11, - 0x50, 0xe6, 0x29, 0x05, 0xaf, 0xc1, 0x6c, 0xeb, 0xe4, 0x78, 0xfb, 0x49, 0x54, 0x39, 0x73, 0x62, - 0x62, 0x50, 0xa2, 0x92, 0x14, 0x12, 0xf6, 0x9f, 0x19, 0x7a, 0x15, 0x88, 0xd6, 0xc0, 0x14, 0x7c, - 0x61, 0x92, 0x34, 0x3b, 0x7f, 0x27, 0xe0, 0xe4, 0xf4, 0xd3, 0xc2, 0x2f, 0x82, 0xf0, 0xd2, 0x97, - 0x97, 0x90, 0x0d, 0x45, 0xf6, 0x6b, 0xe3, 0xe5, 0x63, 0x2c, 0xf2, 0x15, 0xef, 0xca, 0x6b, 0xa0, - 0x1d, 0xc9, 0xba, 0xc4, 0xc4, 0x42, 0x64, 0x72, 0x35, 0xe8, 0x89, 0xee, 0x3a, 0x9e, 0xbd, 0xa7, - 0x7a, 0x67, 0x8c, 0xba, 0xe3, 0xac, 0xf1, 0x87, 0x55, 0xe1, 0x95, 0x3d, 0xf5, 0x99, 0x0e, 0xc5, - 0x1d, 0x02, 0x4f, 0xcd, 0x13, 0xe7, 0xea, 0x24, 0x66, 0xf9, 0x01, 0x98, 0xf1, 0x8e, 0xeb, 0x53, - 0xe8, 0xf5, 0xb9, 0x7a, 0xca, 0x5b, 0x2c, 0xa5, 0x73, 0xa3, 0x6a, 0x51, 0xc2, 0x7c, 0xff, 0x63, - 0xc0, 0x55, 0x0b, 0xfb, 0x2c, 0x2e, 0xfa, 0x91, 0x13, 0x60, 0xef, 0xd8, 0xf1, 0x8e, 0x84, 0x8c, - 0x44, 0x2b, 0x65, 0x28, 0x2b, 0xf5, 0x25, 0x75, 0xa5, 0x98, 0x54, 0xbe, 0x1d, 0x3b, 0xfa, 0xea, - 0xeb, 0x1c, 0xb1, 0x5a, 0xfa, 0x59, 0xcc, 0x7e, 0x52, 0xb3, 0x58, 0xfe, 0x4f, 0xfe, 0x44, 0x70, - 0xa8, 0x47, 0xff, 0xdb, 0x97, 0x06, 0xbf, 0x69, 0x2f, 0x0d, 0xfe, 0x51, 0xbc, 0xa8, 0x25, 0x0e, - 0xd3, 0x83, 0xf0, 0x20, 0xc6, 0x4c, 0xf3, 0x6a, 0x62, 0x0b, 0xa3, 0xee, 0x12, 0x25, 0x51, 0xdd, - 0x25, 0x66, 0xfb, 0x1e, 0x84, 0x0e, 0x57, 0x66, 0x18, 0x7f, 0xaa, 0xbb, 0xd5, 0x86, 0xa2, 0x54, - 0xb9, 0x26, 0x4a, 0xbf, 0xae, 0xba, 0x5b, 0xa9, 0xcf, 0x22, 0x65, 0xf3, 0xd0, 0x1e, 0xe5, 0xc3, - 0x8d, 0xaa, 0x54, 0x77, 0x1c, 0xf8, 0x8f, 0xbc, 0x9a, 0x2a, 0xa1, 0xd5, 0x96, 0x77, 0x95, 0xad, - 0x4f, 0x7b, 0xb8, 0x8e, 0xd0, 0x62, 0x6f, 0x97, 0x37, 0xcb, 0x7b, 0xe1, 0x91, 0x88, 0xfb, 0x76, - 0xf3, 0x9a, 0x83, 0x90, 0xb8, 0xf8, 0x17, 0x87, 0xa7, 0xb7, 0xa3, 0x05, 0xe5, 0x47, 0x89, 0x05, - 0xdd, 0x32, 0x44, 0xd2, 0xc8, 0x17, 0xff, 0x5e, 0x18, 0x6f, 0xe0, 0xd7, 0x02, 0xf3, 0x9a, 0x28, - 0x83, 0x68, 0x4c, 0x44, 0x26, 0xee, 0x88, 0x04, 0x4f, 0x16, 0x6a, 0x55, 0xae, 0xbc, 0x44, 0x52, - 0x8f, 0x92, 0xe6, 0xd9, 0xe2, 0x3a, 0xc9, 0x6f, 0x2d, 0x78, 0xa2, 0xc9, 0x14, 0xe5, 0x5e, 0x89, - 0x5f, 0x98, 0xa9, 0x54, 0x96, 0x86, 0x13, 0xd5, 0x63, 0x39, 0x20, 0x5c, 0x01, 0x47, 0xde, 0xbd, - 0xc5, 0x32, 0x47, 0x84, 0x7d, 0xef, 0xf0, 0xdc, 0x79, 0x5e, 0x42, 0x8f, 0x55, 0xfb, 0x0e, 0x54, - 0xac, 0xdf, 0x48, 0x4b, 0x88, 0x19, 0x61, 0xd2, 0xef, 0xcb, 0xa7, 0x13, 0x7e, 0x49, 0xbc, 0xa4, - 0x3f, 0x93, 0x88, 0x4b, 0x1c, 0xe9, 0x34, 0x23, 0x87, 0x58, 0xa7, 0x4f, 0xf7, 0x82, 0x52, 0x97, - 0xb7, 0x37, 0x93, 0x9e, 0xb7, 0xb7, 0xa9, 0x73, 0x14, 0x66, 0x47, 0x1a, 0x36, 0x8d, 0x2b, 0x70, - 0x1f, 0xae, 0x26, 0xb7, 0xaa, 0x1d, 0xdc, 0xef, 0x74, 0xfb, 0x87, 0x34, 0xe2, 0x9b, 0xb7, 0xd2, - 0x09, 0xd0, 0x06, 0x5c, 0x57, 0xb6, 0x4d, 0xba, 0x91, 0x4a, 0x47, 0x0b, 0xf6, 0x6c, 0x73, 0x28, - 0x0d, 0x39, 0x52, 0x6a, 0x1a, 0xa0, 0x17, 0x51, 0xe1, 0xf3, 0xcd, 0x21, 0x14, 0xe8, 0x3d, 0xb8, - 0x96, 0xc4, 0x86, 0xaf, 0x29, 0x44, 0xe8, 0x78, 0x08, 0xc9, 0xb9, 0x37, 0xe6, 0x3f, 0x35, 0x60, - 0x5a, 0x76, 0xd6, 0xb5, 0xc7, 0xc5, 0xeb, 0x50, 0x60, 0x17, 0x3b, 0x22, 0x23, 0xa0, 0x60, 0x45, - 0x00, 0x54, 0x82, 0x29, 0x75, 0x37, 0x16, 0x45, 0x29, 0xfe, 0x9e, 0x53, 0xe2, 0xef, 0xcb, 0x90, - 0xaf, 0xb9, 0xcf, 0xfb, 0x14, 0x33, 0x41, 0x31, 0x61, 0xb9, 0xfc, 0xed, 0x32, 0x98, 0x42, 0xb7, - 0xc3, 0x57, 0x3d, 0xe1, 0x1b, 0x1e, 0x43, 0x7e, 0xc3, 0xa3, 0x0b, 0x89, 0x44, 0xae, 0x54, 0x56, - 0x71, 0xa5, 0xb6, 0x55, 0x55, 0x63, 0x07, 0xa1, 0xcf, 0xe8, 0x0c, 0x4a, 0xf8, 0x58, 0x67, 0xb8, - 0xba, 0xe9, 0xbe, 0x29, 0xf0, 0x7f, 0x6e, 0xaf, 0x3a, 0x60, 0xc6, 0x6e, 0x6c, 0xc5, 0x75, 0xd2, - 0xdd, 0xa1, 0x43, 0x8d, 0x33, 0xc9, 0xdb, 0x67, 0xa2, 0x46, 0xd4, 0x90, 0x8f, 0x4a, 0xec, 0xb3, - 0x3f, 0x9f, 0x1e, 0x5a, 0x7d, 0x48, 0xcd, 0xe6, 0x31, 0xe2, 0x96, 0xb7, 0x05, 0x18, 0x7b, 0x5b, - 0x90, 0x36, 0xae, 0xe2, 0x99, 0x36, 0xae, 0xe9, 0x53, 0x6c, 0x5c, 0xb1, 0x6d, 0x76, 0xe6, 0xd4, - 0xdb, 0x6c, 0x62, 0x0f, 0x99, 0x3d, 0xd3, 0x1e, 0xa2, 0x9a, 0xf7, 0xcb, 0xa7, 0x34, 0xef, 0x89, - 0x73, 0xbc, 0x79, 0x96, 0x73, 0x7c, 0x8a, 0xb1, 0x9f, 0x3b, 0xa5, 0xb1, 0x47, 0x17, 0x6e, 0xec, - 0xe7, 0xcf, 0x6b, 0xec, 0x17, 0xce, 0x6d, 0xec, 0x17, 0xcf, 0x6b, 0xec, 0x97, 0x46, 0x1a, 0x7b, - 0xf4, 0x76, 0x22, 0xbf, 0x4a, 0xe4, 0x39, 0xb0, 0x9b, 0xb0, 0x14, 0xac, 0xe6, 0x28, 0x10, 0x65, - 0x4f, 0x94, 0xb4, 0x47, 0x81, 0x28, 0x99, 0xe2, 0xab, 0xb0, 0x10, 0xc3, 0x89, 0x5b, 0xaf, 0xc4, - 0x61, 0x34, 0xa1, 0xf7, 0x3a, 0x46, 0x66, 0x02, 0xb4, 0x75, 0xa2, 0x41, 0x62, 0x7c, 0xd5, 0x96, - 0xb8, 0x25, 0x4b, 0x04, 0x12, 0x46, 0xb5, 0xc6, 0x59, 0x59, 0x7b, 0x29, 0xf5, 0x6a, 0x5a, 0xb4, - 0x79, 0x8b, 0xd7, 0x4e, 0xdf, 0xa2, 0x3d, 0xac, 0x45, 0x8e, 0x44, 0x9b, 0x70, 0x33, 0x86, 0xe1, - 0xc9, 0x38, 0x7e, 0xc5, 0xf7, 0xbb, 0x87, 0x7d, 0xdc, 0x29, 0x5d, 0x67, 0x6f, 0xd0, 0x46, 0x90, - 0xa1, 0x26, 0xbc, 0x12, 0x1f, 0x55, 0x98, 0x94, 0x13, 0xd6, 0x75, 0x83, 0xd6, 0x35, 0x9a, 0x30, - 0xf5, 0xc8, 0x17, 0x49, 0xca, 0xca, 0x90, 0x23, 0x5f, 0x24, 0x2f, 0x1b, 0x29, 0x59, 0x29, 0x42, - 0x52, 0x6f, 0x26, 0xef, 0x6c, 0xe3, 0x34, 0xa9, 0x91, 0x7a, 0x16, 0xaf, 0x5d, 0xa5, 0xba, 0x3a, - 0x84, 0x02, 0x3d, 0x82, 0x55, 0xed, 0x7d, 0xae, 0x9c, 0xdc, 0xf3, 0x0a, 0xed, 0xc7, 0x48, 0xba, - 0x31, 0xee, 0xb2, 0xcb, 0x63, 0xdd, 0x65, 0x7f, 0xdd, 0x80, 0x1b, 0xda, 0x2e, 0xd3, 0x30, 0x03, - 0x91, 0xb8, 0x57, 0xa9, 0xc4, 0xbd, 0x3b, 0x54, 0xe2, 0x86, 0xd6, 0xc0, 0x04, 0x6f, 0x78, 0x2b, - 0xe8, 0x0f, 0xd2, 0xd2, 0x86, 0x84, 0xaa, 0xdd, 0xa2, 0xdd, 0x78, 0x70, 0xfa, 0x6e, 0x28, 0x0a, - 0x37, 0xb4, 0x0d, 0xf4, 0x0d, 0x23, 0x25, 0xb4, 0x4f, 0xb7, 0x2c, 0xd6, 0x8f, 0x4f, 0xd1, 0x7e, - 0x54, 0x4e, 0xdf, 0x8f, 0xa8, 0x0e, 0xd6, 0x95, 0x51, 0x2d, 0xa1, 0x3f, 0x36, 0x52, 0x64, 0xbf, - 0xda, 0xe2, 0xb9, 0x54, 0xa5, 0xd7, 0x68, 0x67, 0xde, 0x3b, 0xcb, 0xa4, 0xf0, 0x2a, 0x58, 0x5f, - 0x46, 0xb4, 0x83, 0xbe, 0x69, 0xc0, 0x2b, 0xe9, 0xdd, 0x15, 0xbd, 0x79, 0x9d, 0xf6, 0xa6, 0x7a, - 0xc6, 0xa9, 0x51, 0x3a, 0x34, 0xba, 0xb5, 0x54, 0x8d, 0x16, 0x9b, 0xef, 0xed, 0x21, 0x1a, 0x2d, - 0xf6, 0xdf, 0x6f, 0x19, 0x50, 0x1e, 0x3a, 0x74, 0x96, 0x4a, 0xf6, 0x06, 0x1d, 0x58, 0xed, 0xec, - 0xd3, 0x4c, 0xab, 0x61, 0x23, 0x1b, 0xa3, 0x3d, 0xf4, 0x3d, 0x03, 0x3e, 0x35, 0x6a, 0x02, 0x58, - 0xcf, 0xd6, 0x68, 0xcf, 0x1e, 0x9e, 0x6b, 0xca, 0xa5, 0xce, 0x8d, 0xd7, 0xea, 0xb9, 0x83, 0xd7, - 0x1f, 0xc3, 0xa2, 0xd6, 0xb5, 0x3f, 0x65, 0x9c, 0x4a, 0x79, 0xd3, 0x24, 0x55, 0x7f, 0x9f, 0x7e, - 0x60, 0x2c, 0x25, 0xa8, 0x36, 0xb2, 0x73, 0x0f, 0xe1, 0x6a, 0xaa, 0x83, 0x30, 0xaa, 0xa2, 0xbc, - 0x5c, 0x51, 0x23, 0x91, 0x24, 0x20, 0x9b, 0xa2, 0x73, 0x56, 0x65, 0x9f, 0xb5, 0xaa, 0x9d, 0x14, - 0x89, 0x57, 0xac, 0xf5, 0xa9, 0x6a, 0xdc, 0x4e, 0xb1, 0x0d, 0x67, 0x1e, 0xad, 0x05, 0xb7, 0xc6, - 0xb1, 0xa0, 0xa7, 0xaa, 0xf3, 0x03, 0x78, 0x75, 0x0c, 0x43, 0x78, 0x2a, 0x41, 0xb1, 0xe1, 0xb5, - 0xf1, 0xac, 0xd9, 0xa9, 0x6a, 0xdd, 0x85, 0xd7, 0xc7, 0x34, 0x25, 0xa7, 0xaa, 0xf6, 0x4b, 0xb0, - 0x36, 0xbe, 0x1d, 0x38, 0x55, 0xa8, 0xa6, 0xc1, 0xf2, 0x6d, 0xc4, 0xb7, 0xfc, 0xce, 0xf1, 0xa5, - 0x9d, 0xf2, 0x5f, 0x67, 0x60, 0x41, 0xf7, 0xdc, 0x6f, 0x48, 0xd6, 0xfd, 0x4e, 0xe2, 0xcb, 0x8d, - 0xeb, 0xa3, 0x1e, 0x0f, 0xaa, 0x5f, 0x70, 0x4c, 0x5c, 0xa0, 0x5c, 0xc8, 0x77, 0x1c, 0x97, 0xed, - 0xd1, 0x1f, 0x5a, 0x1c, 0x96, 0x90, 0x23, 0xcd, 0xa8, 0x3c, 0xd7, 0xdf, 0x37, 0x00, 0x36, 0x9c, - 0x83, 0xa3, 0x93, 0x01, 0xbd, 0x83, 0x49, 0xbb, 0xa0, 0x6b, 0xe8, 0x2e, 0xe8, 0x5e, 0x57, 0x5e, - 0x1a, 0x84, 0x95, 0x0c, 0x8f, 0x27, 0x9d, 0x3b, 0x90, 0xf7, 0x87, 0x86, 0xb8, 0x5f, 0x6a, 0x04, - 0xf8, 0x58, 0xfb, 0xd1, 0x8f, 0x32, 0x4c, 0xf3, 0x2c, 0xeb, 0x0f, 0xa5, 0x5b, 0x4a, 0x05, 0x46, - 0x68, 0x6a, 0xf8, 0x89, 0x73, 0xd2, 0xe3, 0x34, 0x2c, 0xa2, 0xa7, 0xc0, 0xc8, 0x12, 0x35, 0xfa, - 0x01, 0xf6, 0xfa, 0x4e, 0x8f, 0x5f, 0xac, 0x85, 0xe5, 0xf2, 0xdf, 0x18, 0xf2, 0x35, 0x17, 0xfa, - 0x22, 0x4c, 0x55, 0xdd, 0x7e, 0x80, 0xe9, 0xb7, 0x31, 0x92, 0x69, 0xcd, 0x21, 0xe1, 0x3a, 0xa7, - 0x62, 0x13, 0x23, 0x78, 0x96, 0x2d, 0xfa, 0xb6, 0x2d, 0x44, 0x9c, 0x32, 0x45, 0x26, 0x9a, 0x0e, - 0x79, 0xa2, 0x7e, 0x3f, 0xba, 0x4f, 0x43, 0x6f, 0x45, 0x2f, 0x3f, 0x13, 0x99, 0x60, 0x82, 0x68, - 0x9d, 0x52, 0xb0, 0x8e, 0x31, 0xea, 0xe5, 0x77, 0x00, 0x22, 0xe0, 0xa9, 0xee, 0x81, 0x5f, 0x57, - 0x1e, 0x9c, 0x0f, 0x79, 0x11, 0xf3, 0x31, 0xcc, 0x25, 0xde, 0x5e, 0xa0, 0x5b, 0x30, 0xc3, 0xbe, - 0x61, 0x23, 0x9e, 0x73, 0x30, 0x26, 0x15, 0x48, 0x97, 0x99, 0xb3, 0x48, 0xdf, 0x3d, 0x52, 0x60, - 0x6b, 0xcf, 0x01, 0x76, 0x07, 0x1d, 0x27, 0x60, 0x11, 0xdc, 0x2b, 0x30, 0xaf, 0x7c, 0x13, 0x87, - 0xa1, 0xcc, 0x4b, 0x68, 0x11, 0xe6, 0xc4, 0xb7, 0x8e, 0x9a, 0xed, 0x16, 0x07, 0x1b, 0x68, 0x1e, - 0x2e, 0xef, 0xfa, 0xd8, 0xa3, 0xc3, 0xe7, 0xc0, 0x0c, 0x9a, 0x81, 0x82, 0xdd, 0xde, 0xe6, 0xc5, - 0x2c, 0x61, 0x0d, 0xbf, 0x5b, 0x14, 0xb2, 0xe6, 0xd6, 0xd6, 0xa1, 0x10, 0x7e, 0xed, 0x16, 0x5d, - 0x86, 0x62, 0xcb, 0xf5, 0x8e, 0x9d, 0x1e, 0x2d, 0x9a, 0x97, 0x90, 0x09, 0xd3, 0xfc, 0xf1, 0x00, - 0x83, 0x18, 0x6b, 0xbf, 0xc8, 0x01, 0x44, 0x6f, 0xc5, 0xd1, 0x2c, 0x80, 0xdd, 0xde, 0xde, 0xdb, - 0xdd, 0xa9, 0x55, 0xec, 0xba, 0x79, 0x09, 0x01, 0x4c, 0x56, 0x76, 0x76, 0xea, 0xad, 0x9a, 0x69, - 0xa0, 0x3c, 0xe4, 0xac, 0x7a, 0xa5, 0x66, 0x66, 0xd0, 0x34, 0xe4, 0x6d, 0x6b, 0xb7, 0x55, 0x25, - 0x34, 0x59, 0x52, 0xe9, 0xc3, 0xba, 0xbd, 0x17, 0x42, 0x72, 0xa8, 0x08, 0x53, 0xd5, 0xed, 0x56, - 0xab, 0x5e, 0xb5, 0xcd, 0x09, 0x52, 0x25, 0x2f, 0xec, 0x59, 0xdb, 0xe6, 0x24, 0x9a, 0x83, 0x99, - 0xe6, 0xf6, 0xc3, 0xbd, 0xcd, 0x7a, 0xc5, 0xb2, 0x37, 0xea, 0x15, 0xdb, 0x9c, 0x22, 0x35, 0x54, - 0x5b, 0x12, 0x24, 0x4f, 0x3b, 0x2a, 0x43, 0x0a, 0x08, 0xc1, 0x6c, 0x75, 0xb3, 0x5e, 0x7d, 0xbc, - 0xb7, 0x59, 0x79, 0x5c, 0xaf, 0xef, 0xd4, 0x2d, 0x13, 0xc8, 0xbc, 0x92, 0x96, 0xab, 0xcd, 0xdd, - 0xb6, 0x5d, 0xb7, 0xf6, 0x6a, 0x75, 0xbb, 0xd2, 0x68, 0xb6, 0xcd, 0x22, 0x21, 0x26, 0x88, 0xf6, - 0x66, 0xc5, 0xaa, 0xed, 0x35, 0x5a, 0xef, 0x6f, 0x9b, 0xd3, 0xb4, 0x82, 0xd6, 0x5e, 0xa5, 0xd9, - 0xdc, 0x26, 0xbd, 0xdc, 0x6b, 0xd4, 0xcc, 0x19, 0x32, 0x89, 0x72, 0x05, 0x6d, 0x9b, 0xf4, 0x7f, - 0x96, 0xce, 0x3f, 0x9d, 0x81, 0xbd, 0x6a, 0x6b, 0xaf, 0x59, 0xd9, 0xa8, 0x37, 0xcd, 0xcb, 0xa8, - 0x04, 0x0b, 0x11, 0xf0, 0xa3, 0x6d, 0xeb, 0x31, 0x27, 0x37, 0x49, 0xcd, 0x3b, 0x15, 0xbb, 0xba, - 0x49, 0x10, 0x6d, 0x7b, 0xdb, 0xaa, 0x9b, 0x73, 0xa4, 0x8a, 0x5a, 0xbd, 0x59, 0x67, 0xd4, 0x0c, - 0x88, 0x08, 0x70, 0xc7, 0xda, 0xfe, 0xd2, 0x97, 0xa5, 0x81, 0xcd, 0xa3, 0x57, 0xe0, 0x06, 0xaf, - 0xb7, 0xb5, 0xdd, 0xda, 0xfb, 0x70, 0xdb, 0x6e, 0xb4, 0x1e, 0xee, 0x59, 0xf5, 0x9d, 0x66, 0xa3, - 0x5a, 0xd9, 0x6b, 0xed, 0x6e, 0x99, 0x0b, 0x68, 0x05, 0x96, 0x93, 0x24, 0x64, 0x1c, 0xcd, 0x86, - 0xfd, 0x65, 0x73, 0x11, 0x2d, 0x80, 0xd9, 0xae, 0xdb, 0x7b, 0x56, 0xfd, 0x83, 0xdd, 0x86, 0x55, - 0xaf, 0xed, 0x35, 0xdb, 0x2d, 0x73, 0x89, 0x40, 0x1f, 0xc6, 0xa1, 0x57, 0xc4, 0xd4, 0x34, 0x2b, - 0x76, 0xbd, 0x6d, 0x53, 0x58, 0x89, 0x2c, 0x09, 0x85, 0xd5, 0x2b, 0xb5, 0xba, 0x45, 0x66, 0xe6, - 0x2a, 0x5d, 0x12, 0x36, 0xdd, 0xf5, 0x4a, 0xd3, 0xde, 0x34, 0x97, 0xc9, 0xa2, 0x93, 0xe5, 0xa7, - 0x2c, 0xd7, 0xd0, 0x55, 0x58, 0xe4, 0x5d, 0x6a, 0xd6, 0x2b, 0xed, 0xfa, 0xe6, 0x76, 0x93, 0xb3, - 0x5e, 0x27, 0x28, 0x3a, 0xf9, 0xd5, 0xcd, 0x7a, 0x6d, 0xb7, 0x59, 0xdf, 0xab, 0x6e, 0x6f, 0x6d, - 0x55, 0x5a, 0xb5, 0xb6, 0x79, 0x63, 0xad, 0x05, 0x10, 0x7d, 0x61, 0x89, 0x48, 0x06, 0x11, 0x73, - 0x06, 0x31, 0x2f, 0x91, 0x16, 0x84, 0x9d, 0x33, 0x0d, 0x22, 0xbc, 0x54, 0x69, 0x42, 0x05, 0x98, - 0xe3, 0x9f, 0x14, 0xb3, 0xf0, 0x57, 0xf1, 0x41, 0x80, 0x3b, 0x66, 0x76, 0x6d, 0x0d, 0x0a, 0xe1, - 0x07, 0x7c, 0x08, 0x7b, 0x1b, 0x07, 0xb4, 0x64, 0x5e, 0x22, 0xec, 0x2c, 0xb8, 0xca, 0x00, 0xc6, - 0xda, 0x3f, 0xe7, 0x00, 0x89, 0x23, 0x85, 0xa4, 0x9b, 0x44, 0xe2, 0xbb, 0x07, 0x47, 0xb2, 0x4a, - 0x4a, 0x5f, 0x4a, 0x09, 0x55, 0x92, 0x68, 0x6a, 0x02, 0x9c, 0x41, 0x4b, 0x34, 0xcf, 0x23, 0x0e, - 0xcf, 0x92, 0xd6, 0x1f, 0xe2, 0x20, 0xd4, 0xf4, 0x1c, 0x99, 0x94, 0x98, 0xbd, 0xe1, 0xa8, 0x09, - 0xb2, 0x22, 0x6d, 0xcc, 0x14, 0x92, 0xc3, 0x26, 0x89, 0xb0, 0xa9, 0x89, 0x3c, 0x1c, 0x33, 0x85, - 0x6e, 0xc2, 0xb5, 0x36, 0x0e, 0x92, 0x77, 0x13, 0x9c, 0x20, 0x8f, 0x96, 0x61, 0x89, 0x13, 0x84, - 0xc1, 0x6d, 0x8e, 0x2b, 0x90, 0x29, 0x64, 0xbf, 0xf9, 0xac, 0x99, 0x40, 0x06, 0x26, 0x40, 0xe1, - 0xb3, 0x15, 0xb3, 0x48, 0xd6, 0x7f, 0x87, 0xd8, 0x3b, 0x9e, 0x23, 0x67, 0x4e, 0x13, 0x5e, 0x0b, - 0x1f, 0xbb, 0xcf, 0xc4, 0x2b, 0x46, 0x73, 0x86, 0xf4, 0x52, 0x4d, 0x86, 0xe4, 0x0d, 0xcd, 0xa2, - 0x1b, 0x70, 0x95, 0xfd, 0xd6, 0x04, 0xad, 0xcd, 0xcb, 0xe8, 0x1a, 0x5c, 0x89, 0xa1, 0xc5, 0x6e, - 0xc0, 0xd4, 0x49, 0x9c, 0x7a, 0x78, 0x7d, 0x73, 0xe8, 0x3a, 0x94, 0x92, 0xa9, 0x38, 0x1c, 0x8b, - 0xd0, 0x2d, 0x58, 0x15, 0x41, 0xdc, 0x64, 0x78, 0x97, 0x53, 0xcd, 0x93, 0x99, 0x63, 0x01, 0xb0, - 0xd8, 0x09, 0x84, 0x13, 0x2c, 0xa0, 0x4f, 0xc1, 0x2b, 0x8c, 0x40, 0xeb, 0x60, 0x72, 0xb2, 0xc5, - 0xb5, 0xef, 0x18, 0x30, 0xa3, 0xdc, 0x36, 0x11, 0xbd, 0x16, 0x00, 0x9e, 0x8d, 0x62, 0x5e, 0x22, - 0x2b, 0x2e, 0x80, 0xca, 0x5b, 0x74, 0xd3, 0x20, 0x0d, 0x25, 0x50, 0xe2, 0xfc, 0x68, 0xe1, 0x03, - 0xdc, 0x7d, 0x86, 0x3b, 0x66, 0x86, 0xcc, 0x52, 0x82, 0xec, 0x7d, 0xa7, 0xdb, 0x23, 0xa2, 0x2f, - 0xb7, 0x69, 0x9d, 0xf4, 0xfb, 0xa4, 0xe2, 0xdc, 0xda, 0xbe, 0xee, 0xbe, 0x8b, 0x2c, 0x93, 0x02, - 0x8d, 0xfa, 0x18, 0xc7, 0x88, 0x9a, 0x8c, 0x04, 0xa6, 0x1d, 0xb8, 0x83, 0x01, 0xe9, 0xd5, 0xda, - 0x8f, 0x0d, 0x30, 0xe3, 0x5f, 0x1f, 0x20, 0x5a, 0x54, 0xe9, 0x88, 0x0f, 0x82, 0x99, 0x97, 0x22, - 0x61, 0x11, 0x20, 0x83, 0x48, 0x54, 0x3b, 0x70, 0xbc, 0x40, 0x40, 0x32, 0x44, 0x49, 0x48, 0xb5, - 0x02, 0x90, 0x25, 0xb5, 0x3c, 0xee, 0xf6, 0x7a, 0x5f, 0x71, 0x8f, 0xf7, 0xbb, 0x44, 0x69, 0xae, - 0xc0, 0x7c, 0xa5, 0xd3, 0x89, 0x8b, 0x90, 0x39, 0x41, 0x64, 0x9c, 0x55, 0x9f, 0xc0, 0x4d, 0x52, - 0x4d, 0x23, 0xed, 0x24, 0x50, 0x53, 0x64, 0x50, 0xa4, 0xc1, 0x04, 0x26, 0xbf, 0xb6, 0xa5, 0xbc, - 0xca, 0x26, 0x1d, 0x89, 0x04, 0xc9, 0xbc, 0x44, 0xf7, 0xde, 0x96, 0x28, 0x1a, 0xa4, 0x58, 0x0d, - 0x8b, 0x19, 0xaa, 0x2b, 0xf4, 0x2a, 0x88, 0x43, 0xb2, 0x1b, 0x8d, 0x1f, 0xfd, 0x6c, 0xe5, 0xd2, - 0x0f, 0x7e, 0xbe, 0x62, 0xfc, 0xe8, 0xe7, 0x2b, 0xc6, 0x4f, 0x7f, 0xbe, 0x72, 0xe9, 0x6f, 0xff, - 0x7d, 0xc5, 0xf8, 0xca, 0x3d, 0xe9, 0x5f, 0xa5, 0x1c, 0x3b, 0x81, 0xd7, 0x7d, 0xe1, 0x52, 0xc7, - 0x42, 0x14, 0xfa, 0xf8, 0xce, 0xe0, 0xe8, 0xf0, 0xce, 0x60, 0xff, 0x4e, 0xe4, 0x26, 0xed, 0x4f, - 0xd2, 0xaf, 0xb7, 0xdd, 0xfb, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf5, 0x3a, 0xb3, 0x7e, 0x86, - 0x65, 0x00, 0x00, + // 6194 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x7d, 0xdb, 0x6f, 0x1b, 0xd9, + 0x79, 0xb8, 0x87, 0xa4, 0x24, 0xea, 0xa3, 0x24, 0x8f, 0x8e, 0x2e, 0xa6, 0x65, 0x5b, 0xd6, 0x72, + 0x9d, 0x5d, 0xaf, 0xb2, 0x91, 0xf3, 0xb3, 0xb3, 0x8b, 0x4d, 0x7e, 0x8e, 0x77, 0x29, 0x92, 0xb6, + 0x68, 0xd3, 0x94, 0x76, 0x38, 0xf2, 0x26, 0x01, 0x16, 0xc2, 0x88, 0x3c, 0x96, 0x27, 0xa2, 0x38, + 0xcc, 0xcc, 0xc8, 0x97, 0xf6, 0xa9, 0x45, 0x53, 0x14, 0x0d, 0x5a, 0x34, 0x40, 0xd0, 0x06, 0x6d, + 0xd3, 0xdb, 0x5b, 0x9f, 0xf2, 0xd2, 0xff, 0xa0, 0x40, 0x91, 0x87, 0xa2, 0x48, 0xf3, 0x58, 0xa0, + 0xb9, 0x6c, 0x5f, 0x0a, 0xe4, 0xa5, 0x4f, 0xcd, 0x53, 0x80, 0xe2, 0xdc, 0x66, 0xce, 0x99, 0x0b, + 0x87, 0xba, 0x24, 0x4d, 0x8a, 0x3c, 0x89, 0xe7, 0x9c, 0xef, 0x3b, 0xd7, 0xef, 0x7e, 0xbe, 0x33, + 0x02, 0xbd, 0xef, 0x1c, 0x78, 0xd8, 0x7d, 0x6e, 0x77, 0xf1, 0xc6, 0xd0, 0x75, 0x7c, 0x07, 0x41, + 0x58, 0xb3, 0xf2, 0x99, 0x03, 0xdb, 0x7f, 0x76, 0xbc, 0xbf, 0xd1, 0x75, 0x8e, 0x6e, 0x1d, 0x38, + 0x07, 0xce, 0x2d, 0x0a, 0xb2, 0x7f, 0xfc, 0x94, 0x96, 0x68, 0x81, 0xfe, 0x62, 0xa8, 0x2b, 0xd7, + 0x0f, 0x1c, 0xe7, 0xa0, 0x8f, 0x43, 0x28, 0xdf, 0x3e, 0xc2, 0x9e, 0x6f, 0x1d, 0x0d, 0x39, 0xc0, + 0xdc, 0x11, 0xf6, 0xad, 0x9e, 0xe5, 0x5b, 0xbc, 0x7c, 0x31, 0x02, 0x50, 0xf9, 0x2f, 0x80, 0xa9, + 0x5a, 0xbb, 0xe3, 0x3b, 0x2e, 0x46, 0x08, 0x0a, 0xbb, 0xbb, 0xcd, 0x7a, 0x59, 0x5b, 0xd3, 0x6e, + 0x4e, 0x1b, 0xf4, 0x37, 0x7a, 0x03, 0xe6, 0x3a, 0x6c, 0x6e, 0xd5, 0x5e, 0xcf, 0xc5, 0x9e, 0x57, + 0xce, 0xd1, 0xd6, 0x48, 0x2d, 0x5a, 0x05, 0xe8, 0x7c, 0xd8, 0x12, 0x30, 0x79, 0x0a, 0x23, 0xd5, + 0xa0, 0x0d, 0x40, 0x2d, 0xa7, 0x7b, 0x18, 0xe9, 0xab, 0x40, 0xe1, 0x12, 0x5a, 0xd0, 0x0d, 0x28, + 0x18, 0x4e, 0x1f, 0x97, 0x27, 0xd7, 0xb4, 0x9b, 0x73, 0xb7, 0xf5, 0x8d, 0x60, 0x1d, 0xb5, 0x36, + 0xa9, 0x37, 0x68, 0x2b, 0x99, 0xb1, 0x69, 0x77, 0x0f, 0xcb, 0x53, 0x6b, 0xda, 0xcd, 0x82, 0x41, + 0x7f, 0xa3, 0x4f, 0xc3, 0x44, 0xc7, 0xb7, 0x7c, 0x5c, 0x2e, 0x52, 0xd4, 0xa5, 0x0d, 0x69, 0xc3, + 0xdb, 0x4e, 0x0f, 0xd3, 0x46, 0x83, 0xc1, 0xa0, 0x2f, 0xc2, 0x64, 0xcb, 0xda, 0xc7, 0x7d, 0xaf, + 0x3c, 0xbd, 0x96, 0xbf, 0x59, 0xba, 0x7d, 0x5d, 0x86, 0xe6, 0xfb, 0xb2, 0xc1, 0x20, 0x1a, 0x03, + 0xdf, 0x7d, 0xb5, 0x59, 0xf8, 0xde, 0x0f, 0xaf, 0x5f, 0x30, 0x38, 0x12, 0xfa, 0x7f, 0x30, 0xfd, + 0x91, 0xe3, 0x1e, 0xb2, 0xf1, 0x80, 0x8e, 0xb7, 0x10, 0x4e, 0x35, 0x68, 0x32, 0x42, 0x28, 0x54, + 0x81, 0x99, 0x0f, 0x8f, 0xb1, 0xfb, 0x4a, 0x6c, 0x41, 0x89, 0x6e, 0x81, 0x52, 0x87, 0xde, 0x05, + 0xa8, 0x39, 0x83, 0xa7, 0xf6, 0x41, 0xdd, 0xf2, 0xad, 0xf2, 0xcc, 0x9a, 0x76, 0xb3, 0x74, 0x7b, + 0x59, 0x99, 0x59, 0xd0, 0x6a, 0x48, 0x90, 0xe8, 0x5d, 0x28, 0x1a, 0xd8, 0x73, 0x8e, 0xdd, 0x2e, + 0x2e, 0xcf, 0x52, 0xac, 0x45, 0x19, 0x4b, 0xb4, 0xf1, 0x45, 0x04, 0xb0, 0x68, 0x19, 0x26, 0x77, + 0x87, 0xa6, 0x7d, 0x84, 0xcb, 0x73, 0x6b, 0xda, 0xcd, 0xbc, 0xc1, 0x4b, 0xe8, 0xb3, 0xb0, 0xd0, + 0x79, 0x66, 0xb9, 0xbd, 0xc8, 0xa9, 0x5d, 0xa4, 0x53, 0x4e, 0x6a, 0x42, 0x2b, 0x50, 0xac, 0x39, + 0x47, 0x47, 0xb6, 0xdf, 0xac, 0x97, 0x75, 0x0a, 0x16, 0x94, 0xd1, 0x7d, 0x58, 0x7d, 0x62, 0xe3, + 0x17, 0x8f, 0xf9, 0xf6, 0x54, 0x7b, 0x47, 0xb6, 0xe7, 0xd9, 0xce, 0xa0, 0x73, 0x3c, 0x1c, 0x3a, + 0xae, 0x8f, 0x7b, 0xe5, 0xf9, 0x35, 0xed, 0x66, 0xd1, 0xc8, 0x80, 0x42, 0x5b, 0x70, 0x3d, 0x11, + 0xe2, 0x01, 0x1e, 0x60, 0xd7, 0xf2, 0x6d, 0x67, 0x50, 0x46, 0x94, 0x1e, 0xb2, 0xc0, 0xd0, 0x5d, + 0xb8, 0x2c, 0x83, 0x6c, 0xef, 0x93, 0x9d, 0xc2, 0xbd, 0xc6, 0xd0, 0xe9, 0x3e, 0x2b, 0x2f, 0xd0, + 0x3e, 0xd2, 0x01, 0xd0, 0x3d, 0x58, 0x49, 0x1c, 0xc0, 0xc0, 0x56, 0xef, 0x55, 0x79, 0x91, 0xae, + 0x65, 0x04, 0x04, 0x19, 0xbd, 0x5e, 0x6f, 0x3d, 0xb1, 0x3d, 0x7b, 0xdf, 0xee, 0xdb, 0xfe, 0xab, + 0x4d, 0xcb, 0x75, 0x6d, 0xec, 0x32, 0xf4, 0x25, 0x8a, 0x9e, 0x0e, 0x80, 0xbe, 0x00, 0x65, 0xb9, + 0xef, 0xe6, 0xe0, 0x80, 0x1c, 0x00, 0x43, 0x5e, 0xa6, 0xc8, 0xa9, 0xed, 0xa8, 0x0e, 0xd7, 0x94, + 0x8e, 0xeb, 0x78, 0xd8, 0x77, 0x5e, 0xe1, 0xde, 0x0e, 0x11, 0x09, 0x5d, 0xa7, 0x5f, 0xbe, 0x44, + 0xc9, 0x60, 0x34, 0x10, 0x39, 0x07, 0x05, 0xa0, 0xda, 0xf5, 0xed, 0xe7, 0x74, 0x63, 0x77, 0x5c, + 0x3c, 0xb4, 0x5c, 0xdc, 0x2b, 0x97, 0xe9, 0x44, 0xb2, 0xc0, 0x62, 0xf3, 0x09, 0x41, 0xee, 0xe3, + 0x41, 0x17, 0xf7, 0xca, 0x97, 0x69, 0x3f, 0xa3, 0x81, 0xd0, 0x0e, 0x2c, 0x29, 0x00, 0xf7, 0x5d, + 0x67, 0xe0, 0xdb, 0xd8, 0x2d, 0xaf, 0x70, 0x56, 0x08, 0x65, 0x9f, 0x29, 0x7e, 0x71, 0x56, 0x48, + 0x46, 0x5c, 0x69, 0x43, 0x49, 0xe2, 0x7d, 0xa4, 0x43, 0xfe, 0x10, 0xbf, 0xe2, 0xe2, 0x91, 0xfc, + 0x44, 0x6f, 0xc1, 0xc4, 0x73, 0xab, 0x7f, 0x8c, 0xa9, 0x50, 0x2c, 0xc9, 0xbc, 0x4f, 0xf1, 0x5a, + 0xb6, 0xe7, 0x1b, 0x0c, 0xe2, 0x0b, 0xb9, 0xf7, 0xb4, 0x87, 0x85, 0xe2, 0x84, 0x3e, 0x59, 0xf9, + 0x59, 0x1e, 0xa6, 0xcc, 0x73, 0x10, 0xb9, 0x42, 0xf8, 0xe5, 0x93, 0x84, 0x5f, 0x61, 0x0c, 0xe1, + 0xf7, 0x0e, 0x4c, 0x52, 0x1e, 0xf6, 0xca, 0x13, 0x54, 0xf8, 0x5d, 0x92, 0xa1, 0xcd, 0x36, 0x6d, + 0x6b, 0x0e, 0x9e, 0x3a, 0x42, 0xe8, 0x31, 0x60, 0x74, 0x1b, 0x16, 0x5b, 0xce, 0x81, 0x6f, 0xd9, + 0x7d, 0x32, 0x21, 0xec, 0x8a, 0x59, 0x4e, 0xd2, 0x59, 0x26, 0xb6, 0xa5, 0x88, 0xff, 0xa9, 0x54, + 0xf1, 0xaf, 0x4a, 0xc0, 0xe9, 0xb1, 0x25, 0x60, 0x54, 0xba, 0x42, 0x82, 0x74, 0x4d, 0x91, 0x6a, + 0xa5, 0x74, 0xa9, 0xf6, 0x01, 0x5c, 0xa9, 0x1e, 0xfb, 0x4e, 0x73, 0xd0, 0x75, 0x29, 0xeb, 0x53, + 0x82, 0x0b, 0xc5, 0xd6, 0x0c, 0xa5, 0xce, 0x51, 0x20, 0x0f, 0x0b, 0xc5, 0xa2, 0x3e, 0x5d, 0xf9, + 0xa3, 0x3c, 0x14, 0x5b, 0xce, 0xc1, 0xaf, 0xc0, 0xd1, 0xdf, 0x25, 0x9a, 0x62, 0xd8, 0xb7, 0xbb, + 0x96, 0x38, 0xfc, 0x15, 0x19, 0xbe, 0xe5, 0x1c, 0xf0, 0x66, 0xe9, 0xfc, 0x03, 0x8c, 0xc8, 0xe9, + 0x4c, 0x9e, 0x44, 0x3f, 0xb5, 0x9c, 0xae, 0x45, 0x78, 0x8c, 0x9e, 0x7d, 0x44, 0x3f, 0x89, 0x36, + 0x31, 0x9e, 0x28, 0xa3, 0x27, 0xf0, 0xc6, 0x48, 0x51, 0x14, 0x1e, 0x45, 0x91, 0x1e, 0xc5, 0x98, + 0xd0, 0x95, 0x6f, 0xe5, 0x61, 0x86, 0x9c, 0x87, 0x20, 0x74, 0x54, 0x86, 0x29, 0x56, 0x60, 0xc7, + 0x52, 0x30, 0x44, 0x11, 0x6d, 0x4a, 0x1b, 0x96, 0xa3, 0x1b, 0xf6, 0x46, 0x64, 0xc3, 0x82, 0x5e, + 0x36, 0x04, 0x20, 0x95, 0x1a, 0xd2, 0xb6, 0x2d, 0xc2, 0x04, 0x53, 0x2d, 0xec, 0xd8, 0x58, 0x81, + 0xa8, 0xcc, 0x16, 0xb6, 0x7a, 0xd8, 0x6d, 0xd6, 0xe9, 0xd1, 0x15, 0x8c, 0xa0, 0x4c, 0xcf, 0x19, + 0xbb, 0x47, 0xe5, 0x09, 0x7e, 0xce, 0xd8, 0x3d, 0x42, 0x1f, 0xc3, 0x7c, 0xdb, 0x19, 0x3c, 0x71, + 0x7c, 0x7b, 0x70, 0x10, 0x4c, 0x69, 0x92, 0x4e, 0xe9, 0x56, 0xea, 0x94, 0x62, 0x18, 0x6c, 0x6e, + 0xf1, 0x9e, 0x56, 0xfe, 0x3f, 0xcc, 0x2a, 0x30, 0xb2, 0xd4, 0x2b, 0x30, 0xa9, 0xb7, 0x28, 0x4b, + 0xbd, 0x69, 0x49, 0xc0, 0xad, 0xd4, 0x61, 0x39, 0x79, 0xa4, 0x93, 0xf4, 0x52, 0xf9, 0xb6, 0x06, + 0x73, 0x2a, 0x05, 0xa2, 0xfb, 0xea, 0x41, 0xd1, 0x7e, 0x4a, 0xb7, 0xcb, 0x69, 0xeb, 0xdd, 0x2c, + 0x12, 0x0a, 0xfa, 0xfe, 0x0f, 0xaf, 0x6b, 0x86, 0x7a, 0xc0, 0x57, 0x61, 0x5a, 0x74, 0x5b, 0xa7, + 0x03, 0x17, 0x8c, 0xb0, 0x02, 0xad, 0x41, 0xa9, 0xe9, 0x05, 0x0b, 0xa0, 0xc7, 0x54, 0x34, 0xe4, + 0xaa, 0xca, 0x1f, 0x6a, 0xa1, 0x89, 0x45, 0x8d, 0x9d, 0x9d, 0x5d, 0xd3, 0xf1, 0xad, 0x3e, 0x5f, + 0x58, 0x50, 0x26, 0x82, 0xa8, 0xb6, 0xb3, 0x5b, 0x7d, 0x6e, 0xd9, 0x7d, 0x6b, 0xbf, 0xcf, 0x16, + 0xa9, 0x19, 0x4a, 0x1d, 0xc1, 0x7f, 0x8c, 0x8f, 0x18, 0x3e, 0x23, 0x89, 0xa0, 0x4c, 0xf0, 0x1f, + 0xe3, 0xa3, 0x10, 0x9f, 0x51, 0x86, 0x52, 0x57, 0xf9, 0x1d, 0x2d, 0x55, 0x6f, 0x9a, 0x96, 0x7b, + 0x80, 0x7d, 0xb2, 0x5c, 0x2e, 0x39, 0x02, 0x41, 0x13, 0x56, 0x10, 0x9b, 0x5d, 0xb2, 0x99, 0xd8, + 0x6e, 0x48, 0x35, 0x31, 0x61, 0x9a, 0x8f, 0x0b, 0xd3, 0xca, 0xcf, 0x67, 0x40, 0xe7, 0x76, 0xf2, + 0x16, 0xb6, 0x5c, 0x7f, 0x1f, 0x5b, 0xfe, 0xaf, 0xa1, 0x23, 0xb1, 0x01, 0xc8, 0xb4, 0x3c, 0x81, + 0x5b, 0x73, 0xb1, 0x45, 0xa4, 0xc9, 0x14, 0x25, 0x80, 0x84, 0x96, 0xd8, 0xd6, 0x14, 0x13, 0xf4, + 0xcc, 0x0d, 0x98, 0x6d, 0x0e, 0x6c, 0x3f, 0x74, 0x10, 0xa6, 0x29, 0x90, 0x5a, 0x49, 0xa0, 0x1e, + 0x38, 0x9e, 0x67, 0x0f, 0x55, 0x95, 0xa5, 0x56, 0x92, 0xf1, 0x58, 0xc5, 0x43, 0xc7, 0x1e, 0xe0, + 0x1e, 0x55, 0x56, 0x45, 0x43, 0xa9, 0xfb, 0xa5, 0x7b, 0x0d, 0x29, 0x7a, 0x74, 0x6e, 0x3c, 0xef, + 0xe0, 0x62, 0xc4, 0x3b, 0xf8, 0x2c, 0x2c, 0x54, 0xbb, 0x87, 0xb8, 0x47, 0x2a, 0xac, 0x41, 0x6f, + 0xd3, 0xf2, 0xbb, 0xcf, 0xb8, 0x13, 0x51, 0x30, 0x92, 0x9a, 0x88, 0x56, 0xe6, 0x35, 0x75, 0xdc, + 0xb7, 0x9f, 0x93, 0x9d, 0xef, 0x1e, 0x46, 0x9d, 0x89, 0x51, 0x20, 0x63, 0x78, 0x24, 0xe8, 0xbc, + 0x3c, 0x92, 0x85, 0x73, 0xf0, 0x48, 0x16, 0xb3, 0x3c, 0x92, 0xc8, 0x7a, 0x6a, 0x96, 0x6f, 0xf5, + 0x9d, 0x03, 0x66, 0x1e, 0xb3, 0x2e, 0x96, 0x68, 0x17, 0x19, 0x50, 0x68, 0x13, 0xae, 0xca, 0x10, + 0x06, 0x7e, 0xea, 0x62, 0xef, 0x59, 0xb8, 0x2b, 0xcc, 0xbf, 0x18, 0x09, 0x13, 0xef, 0xe3, 0xb9, + 0xd5, 0xb7, 0x7b, 0x84, 0x79, 0xd8, 0x4c, 0x2e, 0xd1, 0x99, 0x8c, 0x84, 0x19, 0xe9, 0xe3, 0x94, + 0x33, 0x7c, 0x9c, 0x91, 0xde, 0xd5, 0xe5, 0x2c, 0xef, 0x2a, 0xd3, 0x43, 0x5a, 0x19, 0xc7, 0x43, + 0x7a, 0x15, 0xf1, 0x90, 0xe8, 0xaa, 0x18, 0xc5, 0x33, 0x01, 0xed, 0x95, 0xaf, 0x50, 0xc5, 0xfd, + 0x96, 0xcc, 0x70, 0x23, 0x45, 0x3a, 0xe7, 0xc2, 0xac, 0x7e, 0xc7, 0x71, 0xce, 0xae, 0x9e, 0x93, + 0x73, 0x76, 0xed, 0x4c, 0xce, 0xd9, 0xea, 0x29, 0x9d, 0x33, 0xee, 0x4c, 0x99, 0x30, 0x53, 0x6b, + 0x57, 0xfb, 0x7d, 0xa7, 0x6b, 0xf9, 0x44, 0xa7, 0xc5, 0x7d, 0xb4, 0x45, 0x98, 0xa0, 0x12, 0x83, + 0x2b, 0x38, 0x56, 0x60, 0x86, 0xc0, 0xd7, 0x8e, 0xb1, 0x47, 0x64, 0x11, 0xd3, 0x32, 0x61, 0x45, + 0xe5, 0x5f, 0x0b, 0x30, 0x2f, 0x0c, 0xf5, 0xd1, 0x6a, 0x6d, 0x0d, 0x4a, 0x86, 0xf5, 0xd4, 0x57, + 0x75, 0x9a, 0x5c, 0x95, 0xa0, 0xf8, 0xf2, 0x89, 0x8a, 0x2f, 0xa6, 0x08, 0x0a, 0x49, 0x8a, 0xe0, + 0x6c, 0x86, 0x7b, 0xb2, 0x9a, 0x9b, 0x4c, 0x55, 0x73, 0xaa, 0x4a, 0x99, 0x3a, 0x95, 0xa1, 0x5f, + 0x3c, 0x81, 0xa1, 0xff, 0x05, 0x28, 0x47, 0xe4, 0x75, 0x28, 0x74, 0xa6, 0x19, 0xc3, 0xa7, 0xb5, + 0x8f, 0x21, 0xcc, 0x61, 0x2c, 0x61, 0x3e, 0xbe, 0xb3, 0x51, 0x3a, 0x91, 0xb3, 0xd1, 0x80, 0x92, + 0xe4, 0x53, 0x8f, 0x70, 0x35, 0x46, 0xda, 0xa8, 0x95, 0xaf, 0x4f, 0x80, 0x6e, 0x9e, 0xa7, 0xc1, + 0x15, 0x46, 0x01, 0xf2, 0x27, 0x89, 0x02, 0x24, 0x93, 0x52, 0x21, 0x95, 0x94, 0xd2, 0xa2, 0x06, + 0x13, 0x27, 0x8e, 0x1a, 0x4c, 0x8e, 0x19, 0x35, 0x28, 0x9e, 0x3a, 0x6a, 0x30, 0x3d, 0x7e, 0xd4, + 0x00, 0xd2, 0xad, 0x1d, 0x22, 0x1a, 0xf0, 0xb0, 0x6f, 0xbd, 0xc2, 0xbd, 0x96, 0x37, 0xa0, 0xd4, + 0x52, 0x30, 0xe4, 0xaa, 0xb3, 0xc7, 0x15, 0xd2, 0xac, 0xa6, 0xd9, 0x53, 0x5b, 0x4d, 0x73, 0x99, + 0x56, 0xd3, 0xc3, 0x42, 0x71, 0x4a, 0x2f, 0x56, 0xbe, 0x3b, 0x01, 0x45, 0xa3, 0xf3, 0x98, 0x19, + 0xb1, 0x3a, 0xe4, 0x4d, 0xcf, 0x11, 0xde, 0x9d, 0xe9, 0x39, 0x44, 0xea, 0x36, 0x07, 0x3d, 0xfc, + 0x52, 0x48, 0x5d, 0x5a, 0x20, 0x32, 0xae, 0x85, 0x2d, 0x0f, 0x6f, 0x39, 0x7d, 0xe6, 0xf0, 0x32, + 0xb7, 0x47, 0xad, 0x24, 0xc7, 0x61, 0xba, 0xc7, 0x03, 0x22, 0xd1, 0xe9, 0xce, 0x71, 0xdf, 0x47, + 0xae, 0x43, 0x0f, 0x61, 0x86, 0x21, 0xd9, 0x9e, 0xef, 0xb8, 0xaf, 0xb8, 0x2c, 0x54, 0x7c, 0x72, + 0x31, 0xbb, 0x0d, 0x19, 0x90, 0xf9, 0xbd, 0x0a, 0x2e, 0x3b, 0xa8, 0xaf, 0x1d, 0xdb, 0x2e, 0x1b, + 0x6e, 0x52, 0x1c, 0x54, 0x50, 0x85, 0xde, 0x86, 0xf9, 0x8f, 0xaa, 0x2d, 0x03, 0x77, 0x1d, 0xb2, + 0x1b, 0x75, 0xfb, 0x00, 0x7b, 0x3e, 0x8f, 0x5e, 0xc5, 0x1b, 0xd0, 0xe7, 0x60, 0x49, 0xaa, 0xa4, + 0x23, 0xd6, 0x9c, 0xe3, 0x81, 0x4f, 0x29, 0xb2, 0x60, 0x24, 0x37, 0x92, 0x83, 0x91, 0x1a, 0x6a, + 0xce, 0xd1, 0xb0, 0x8f, 0x89, 0x25, 0x34, 0xf0, 0x5d, 0x1b, 0x33, 0x9a, 0x2c, 0x18, 0xa3, 0x40, + 0x08, 0xbb, 0x48, 0xcd, 0x9b, 0x96, 0x87, 0xc9, 0x72, 0x80, 0x22, 0x26, 0xb4, 0x44, 0xe0, 0x5b, + 0x96, 0xe7, 0x87, 0x74, 0x9a, 0xd0, 0x82, 0x1e, 0xc2, 0x9a, 0x54, 0xbb, 0xed, 0xda, 0x07, 0xf6, + 0xc0, 0xea, 0xab, 0x07, 0x3a, 0x43, 0xb1, 0x33, 0xe1, 0x08, 0xe1, 0x26, 0x2c, 0x85, 0x12, 0x6e, + 0xd1, 0x48, 0x6a, 0x5a, 0x79, 0x1f, 0xe6, 0x63, 0x07, 0x99, 0x15, 0x56, 0x28, 0xc8, 0x61, 0x85, + 0x8f, 0x61, 0x9a, 0xaa, 0xc7, 0xae, 0xe3, 0xf6, 0x08, 0x22, 0x59, 0x2c, 0x47, 0x24, 0xab, 0x5b, + 0x87, 0x82, 0xf9, 0x6a, 0xc8, 0xf0, 0xe6, 0x54, 0xb1, 0xc1, 0x70, 0x48, 0xab, 0x41, 0x61, 0x88, + 0xbc, 0xa5, 0x22, 0x86, 0x90, 0xef, 0x8c, 0x41, 0x7f, 0x57, 0x7e, 0x9c, 0x03, 0xa0, 0xfd, 0x53, + 0x23, 0x82, 0x80, 0xb4, 0xad, 0x23, 0x2c, 0x44, 0x32, 0xf9, 0x2d, 0xcb, 0xfc, 0x9c, 0x2a, 0xf3, + 0xf9, 0x74, 0xf2, 0xe1, 0x74, 0xca, 0x30, 0xf5, 0xd8, 0x7a, 0xd9, 0xb1, 0x7f, 0x4b, 0xf8, 0xfe, + 0xa2, 0x48, 0xf4, 0x83, 0x10, 0xcb, 0x75, 0x1e, 0x19, 0x0a, 0x2b, 0x68, 0xc8, 0xa8, 0xdd, 0xac, + 0x73, 0x2a, 0xa6, 0xbf, 0xd1, 0xe7, 0x20, 0x67, 0x76, 0xb8, 0xfa, 0x5e, 0xd9, 0x60, 0x77, 0x86, + 0x1b, 0xe2, 0xce, 0x50, 0xb2, 0xb7, 0x68, 0xd4, 0xe4, 0x4f, 0x7e, 0x74, 0x5d, 0x33, 0x72, 0x66, + 0x87, 0x28, 0xd4, 0xe6, 0xa0, 0xdb, 0x3f, 0xee, 0xe1, 0xc6, 0xcb, 0x21, 0xe1, 0x04, 0xae, 0x84, + 0xb8, 0x7c, 0xc3, 0x1e, 0x8f, 0xb6, 0x65, 0x40, 0x11, 0x53, 0xb4, 0xf1, 0x92, 0x42, 0x6c, 0x59, + 0x6e, 0xaf, 0xee, 0xbc, 0x18, 0xc4, 0x3a, 0x62, 0xba, 0x3d, 0x0b, 0xac, 0x52, 0x01, 0x30, 0x3d, + 0x47, 0xec, 0xf0, 0x22, 0x4c, 0x30, 0xb6, 0x62, 0x87, 0xc8, 0x0a, 0x95, 0x9f, 0x6a, 0xc4, 0x22, + 0xa4, 0xfa, 0x91, 0xc6, 0xe0, 0x13, 0x75, 0xe3, 0x1d, 0x98, 0xde, 0x1e, 0xca, 0x81, 0x8f, 0x48, + 0xbc, 0xb4, 0xd6, 0xa6, 0xb8, 0xdb, 0x43, 0x23, 0x84, 0x43, 0x9b, 0xc1, 0x5d, 0x21, 0x53, 0x94, + 0x37, 0x12, 0xee, 0x0a, 0x29, 0x40, 0xfa, 0x85, 0xe1, 0x79, 0xdf, 0x28, 0x54, 0x5a, 0x50, 0xaa, + 0xb5, 0xc3, 0x60, 0x42, 0xd2, 0x5a, 0xdf, 0x12, 0x71, 0xe1, 0x5c, 0xfa, 0xfd, 0x24, 0x83, 0xa8, + 0xfc, 0x84, 0xef, 0x9d, 0xe5, 0x8f, 0xd8, 0xbb, 0xf1, 0xfb, 0xcb, 0xde, 0x31, 0x31, 0xd0, 0x2f, + 0x71, 0xc7, 0xbe, 0x59, 0x84, 0x29, 0x41, 0x41, 0x8a, 0x13, 0xa0, 0x09, 0x4b, 0x8b, 0x57, 0xa0, + 0x0d, 0x98, 0x7c, 0x8c, 0xfd, 0x67, 0x4e, 0x2f, 0x49, 0x24, 0xb0, 0x16, 0x2a, 0x12, 0x38, 0x14, + 0xba, 0x2b, 0xf3, 0x3f, 0x65, 0xe5, 0x88, 0xf5, 0x11, 0xb6, 0xf2, 0x35, 0xca, 0xf2, 0xa2, 0x4a, + 0x23, 0x9c, 0x81, 0x49, 0x47, 0x99, 0xbe, 0x74, 0xfb, 0x5a, 0x34, 0xc2, 0xa9, 0xd8, 0x7d, 0x86, + 0x82, 0x82, 0xee, 0x11, 0x62, 0x08, 0x7b, 0x98, 0xa0, 0x3d, 0x5c, 0x4d, 0xa0, 0xd2, 0xb0, 0x03, + 0x19, 0x81, 0xe0, 0x9b, 0x12, 0xfe, 0x64, 0x1c, 0xdf, 0x8c, 0xe1, 0x4b, 0x08, 0xc4, 0xfc, 0x0a, + 0xd9, 0x33, 0xc9, 0x5b, 0x08, 0x5b, 0x0d, 0x99, 0x91, 0xef, 0xaa, 0x3e, 0x1c, 0x37, 0xdc, 0xca, + 0xea, 0xc4, 0xc3, 0x76, 0x43, 0xf5, 0xf8, 0xee, 0xaa, 0xfc, 0xce, 0x2f, 0x8b, 0xca, 0x69, 0xcc, + 0x69, 0xa8, 0xd2, 0xe1, 0xf3, 0x0a, 0x03, 0x51, 0x65, 0x19, 0x31, 0x81, 0xa5, 0x66, 0x43, 0x61, + 0xb6, 0xbb, 0x2a, 0xb3, 0x50, 0xc5, 0x99, 0x30, 0xb0, 0x68, 0x37, 0x54, 0xd6, 0x7a, 0x1f, 0x66, + 0xeb, 0x98, 0x28, 0x36, 0x3e, 0x1d, 0x1e, 0xb0, 0xbb, 0xac, 0x44, 0x02, 0x64, 0x00, 0x43, 0x85, + 0x47, 0x9b, 0x30, 0xb7, 0xe3, 0x3a, 0x2f, 0x5f, 0x85, 0x07, 0x36, 0xcb, 0x05, 0xbc, 0xd4, 0x83, + 0x0a, 0x61, 0x44, 0x30, 0x88, 0x16, 0x8e, 0xc6, 0xeb, 0xdb, 0xc7, 0x47, 0xd4, 0x08, 0x2c, 0x18, + 0x49, 0x4d, 0x68, 0x53, 0xba, 0x7d, 0x08, 0x5c, 0xbc, 0x8b, 0xe9, 0x2e, 0x9e, 0x11, 0x07, 0xa7, + 0x7b, 0xfe, 0x0c, 0x77, 0x0f, 0xb7, 0xb0, 0xd5, 0xf7, 0x9f, 0xd1, 0x10, 0x5f, 0x74, 0xcf, 0xc3, + 0x66, 0x43, 0x86, 0x45, 0x26, 0x2c, 0x76, 0xba, 0xcf, 0x70, 0xef, 0xb8, 0x8f, 0xb9, 0x89, 0x4a, + 0x8d, 0x74, 0x1a, 0xec, 0x2b, 0xdd, 0x5e, 0x93, 0xfb, 0x48, 0x82, 0x33, 0x12, 0xb1, 0x2b, 0x0e, + 0x94, 0x28, 0x27, 0x7a, 0x43, 0x67, 0xe0, 0xe1, 0x11, 0xae, 0x19, 0x57, 0xd3, 0x39, 0x45, 0x4d, + 0x0b, 0xc3, 0x89, 0x29, 0x6f, 0x51, 0x1c, 0x75, 0xaf, 0x53, 0xd9, 0x00, 0x24, 0xd1, 0xb3, 0x34, + 0xee, 0x7d, 0xdb, 0x95, 0x84, 0x91, 0x28, 0x56, 0xfe, 0xbb, 0x40, 0x63, 0xb4, 0x0c, 0xec, 0x7c, + 0xa5, 0xd6, 0x55, 0x98, 0x6e, 0xb8, 0xae, 0xe3, 0xd6, 0x9c, 0x1e, 0xa6, 0x4b, 0x98, 0x35, 0xc2, + 0x0a, 0x62, 0x8a, 0xd3, 0xc2, 0x63, 0xec, 0x79, 0xd6, 0x01, 0xe6, 0x31, 0x09, 0xa5, 0x0e, 0xad, + 0x02, 0x34, 0xbd, 0xad, 0xea, 0x23, 0x8c, 0x87, 0xd8, 0xa5, 0x52, 0xa7, 0x68, 0x48, 0x35, 0xe8, + 0x7d, 0x65, 0x77, 0xb9, 0x58, 0xb9, 0x14, 0x13, 0x8c, 0xac, 0x99, 0x4b, 0x46, 0xe5, 0x3c, 0x08, + 0xa3, 0x49, 0x4e, 0x0c, 0x97, 0x2c, 0x2a, 0xa3, 0x49, 0xed, 0x86, 0x02, 0x4d, 0xa8, 0x8d, 0xca, + 0x1a, 0x3e, 0x7c, 0x31, 0x3e, 0xbc, 0xd4, 0x6c, 0xc8, 0xb0, 0x84, 0xc5, 0x6a, 0xfd, 0x63, 0xcf, + 0xc7, 0x6e, 0x1d, 0x13, 0xef, 0xd4, 0xe3, 0xc2, 0x45, 0x61, 0x31, 0x15, 0xc2, 0x88, 0x60, 0xa0, + 0x7b, 0x30, 0x1d, 0x5e, 0x5b, 0x41, 0x02, 0x99, 0x8a, 0x46, 0x46, 0xa0, 0xd8, 0x3b, 0xee, 0xfb, + 0x46, 0x88, 0x82, 0xee, 0x01, 0x48, 0xa2, 0x91, 0xc9, 0x98, 0x55, 0xb9, 0x83, 0x38, 0x21, 0x19, + 0x10, 0x11, 0x8f, 0x84, 0x81, 0xb0, 0xcb, 0x24, 0xdc, 0x4c, 0xc2, 0xe6, 0x49, 0xed, 0x86, 0x02, + 0x5d, 0x79, 0x48, 0xe3, 0x60, 0xcc, 0xfe, 0x0d, 0xb6, 0xe5, 0x1d, 0xa2, 0x41, 0x49, 0x8d, 0x57, + 0xd6, 0xa8, 0x5e, 0x5f, 0x8a, 0x1d, 0x26, 0x69, 0xe5, 0x47, 0x29, 0x60, 0x2b, 0xaf, 0x2b, 0x07, + 0x41, 0xcc, 0xb7, 0x27, 0x54, 0x6f, 0x73, 0xf3, 0x8d, 0x16, 0x2a, 0x0f, 0x60, 0xd6, 0xb4, 0xbc, + 0x43, 0xd3, 0xda, 0xef, 0xe3, 0x5d, 0x0f, 0xbb, 0x84, 0x8d, 0xc8, 0xdf, 0x41, 0x68, 0x4b, 0x07, + 0x65, 0xd2, 0xb6, 0x63, 0x79, 0xde, 0x0b, 0xc7, 0xed, 0xf1, 0xe0, 0x46, 0x50, 0xae, 0x7c, 0x43, + 0x23, 0xb3, 0xa4, 0x72, 0x2b, 0xd1, 0x8c, 0x49, 0xb7, 0xc5, 0x95, 0xf8, 0x4b, 0x3e, 0x7a, 0x47, + 0x18, 0x5c, 0xe2, 0x16, 0xe4, 0x4b, 0xdc, 0x55, 0xaa, 0xfb, 0x55, 0xa3, 0x5c, 0xaa, 0xa9, 0xfc, + 0x79, 0x8e, 0xd0, 0xf0, 0xe0, 0xa9, 0x7d, 0x50, 0x7b, 0x66, 0x0d, 0x0e, 0x30, 0xba, 0x13, 0xcc, + 0x8e, 0xdf, 0x65, 0x2e, 0xa8, 0x0e, 0x07, 0x6d, 0x0a, 0x77, 0x90, 0xad, 0xe3, 0x2e, 0x00, 0x43, + 0x97, 0x1c, 0x95, 0xab, 0xf1, 0xf8, 0x46, 0x08, 0x63, 0x48, 0xf0, 0xc8, 0x84, 0xb9, 0xe6, 0xc0, + 0xf6, 0x6d, 0xab, 0xff, 0x18, 0x1f, 0xed, 0x63, 0x57, 0x58, 0x65, 0x6f, 0xa7, 0xf5, 0xb0, 0xa1, + 0x82, 0x33, 0xd7, 0x39, 0xd2, 0xc7, 0x4a, 0x15, 0x16, 0x12, 0xc0, 0x4e, 0x74, 0xdf, 0xfb, 0x16, + 0xcc, 0x76, 0x9e, 0x1d, 0xfb, 0x3d, 0xe7, 0xc5, 0x80, 0xa9, 0x36, 0x72, 0x36, 0xe4, 0x47, 0x70, + 0x64, 0xa2, 0x58, 0xf9, 0xfb, 0x09, 0xb8, 0x18, 0x11, 0xe1, 0x89, 0xa7, 0x7b, 0x03, 0x66, 0x37, + 0x1d, 0xc7, 0xf7, 0x7c, 0xd7, 0x1a, 0x0e, 0xed, 0xc1, 0x01, 0x1d, 0xb4, 0x68, 0xa8, 0x95, 0x44, + 0x34, 0xf0, 0x98, 0x0d, 0xdd, 0xd0, 0x3c, 0xdd, 0x50, 0x45, 0x34, 0x48, 0xcd, 0x86, 0x0c, 0xcb, + 0x64, 0x52, 0xb8, 0x55, 0xdc, 0x5c, 0x2b, 0xa7, 0x6d, 0xa5, 0xa1, 0x9e, 0xfe, 0xfb, 0x91, 0x15, + 0x73, 0x5b, 0xed, 0xb2, 0x2a, 0x18, 0x24, 0x00, 0x23, 0xb2, 0x43, 0x8f, 0x60, 0x9e, 0x05, 0xd6, + 0xa4, 0x48, 0x1b, 0x97, 0xac, 0x8a, 0xc9, 0x18, 0x03, 0x32, 0xe2, 0x78, 0x71, 0x53, 0x64, 0xea, + 0x84, 0xa6, 0xc8, 0x23, 0x98, 0x7f, 0xe8, 0xd8, 0x03, 0x16, 0xa9, 0xe6, 0xf2, 0x8f, 0x0b, 0x5a, + 0x65, 0x36, 0x31, 0x20, 0x23, 0x8e, 0x87, 0xb6, 0x40, 0x67, 0xbd, 0x53, 0x5b, 0x85, 0x4d, 0x68, + 0x3a, 0x6e, 0x8a, 0x46, 0x61, 0x8c, 0x18, 0x16, 0x39, 0xde, 0x6a, 0xaf, 0x27, 0xb8, 0x30, 0xc9, + 0xb6, 0x93, 0x9a, 0x0d, 0x19, 0x96, 0x48, 0xfe, 0x80, 0x54, 0x18, 0x76, 0x29, 0x2e, 0xf9, 0x55, + 0x08, 0x23, 0x82, 0x51, 0xc1, 0xc9, 0xb6, 0x4a, 0x22, 0xbd, 0x46, 0x28, 0x31, 0x37, 0x3e, 0x25, + 0x56, 0xfe, 0x34, 0x07, 0xcb, 0x91, 0x70, 0x9d, 0xb8, 0x04, 0xaa, 0x04, 0xa6, 0x31, 0x19, 0x84, + 0x49, 0xeb, 0x69, 0x43, 0xa9, 0xa3, 0xc1, 0x36, 0x19, 0x26, 0xc7, 0x60, 0xe4, 0x3a, 0x22, 0x67, + 0x1b, 0x2f, 0x89, 0x08, 0xb2, 0x7d, 0x9e, 0x14, 0x11, 0x94, 0x89, 0x09, 0xc9, 0xfb, 0x33, 0xed, + 0x23, 0xec, 0x1c, 0xfb, 0xa6, 0xdd, 0x3d, 0xf4, 0xb8, 0x74, 0x4c, 0x6a, 0x22, 0x18, 0x66, 0x02, + 0x06, 0x13, 0x9a, 0x49, 0x4d, 0xe8, 0x73, 0xb0, 0xd4, 0x20, 0xe2, 0xc2, 0xf2, 0x71, 0xed, 0xd8, + 0x75, 0xf1, 0xc0, 0xa7, 0x30, 0x1e, 0xbf, 0xb9, 0x48, 0x6e, 0xac, 0xdc, 0x4a, 0xa0, 0x4a, 0xb6, + 0x14, 0xdb, 0xa3, 0xf9, 0x1d, 0x6c, 0x3b, 0x82, 0x72, 0xa5, 0x9f, 0xc0, 0x54, 0xe8, 0x0e, 0x14, + 0x88, 0xbe, 0xe1, 0x52, 0x5a, 0xe1, 0x09, 0x45, 0x51, 0x71, 0x59, 0x4d, 0x81, 0xe9, 0xa6, 0x5a, + 0xde, 0x61, 0xdd, 0xf2, 0xad, 0x7d, 0xcb, 0x13, 0x22, 0x4f, 0xa9, 0x23, 0x52, 0x4f, 0xe5, 0xa2, + 0x74, 0xa9, 0xf7, 0x76, 0x9c, 0x25, 0x46, 0x40, 0xbf, 0xa9, 0x90, 0x7d, 0xba, 0x35, 0x5b, 0xf9, + 0x99, 0x16, 0xa5, 0xf2, 0xd3, 0xde, 0x4a, 0xa0, 0x27, 0x29, 0xba, 0x65, 0x23, 0x9d, 0x5f, 0xc6, + 0xd1, 0x2e, 0x84, 0x57, 0xc8, 0x19, 0xf2, 0x7b, 0x05, 0xfa, 0xfb, 0x3c, 0x34, 0xce, 0x8f, 0x72, + 0xaa, 0x49, 0x19, 0x24, 0x5a, 0x69, 0x52, 0xa2, 0xd5, 0x17, 0x59, 0xb6, 0x82, 0x35, 0xe8, 0x89, + 0x94, 0xaf, 0x2b, 0x23, 0xfc, 0x0b, 0x71, 0x97, 0x25, 0x50, 0xc8, 0x56, 0x8a, 0x70, 0x3c, 0xf7, + 0x0c, 0x44, 0x08, 0xbe, 0x06, 0xc0, 0xa1, 0x08, 0xc3, 0x15, 0x68, 0xd7, 0xd7, 0x46, 0x74, 0xdd, + 0xac, 0x8b, 0x78, 0x41, 0x88, 0x86, 0x3e, 0x82, 0xa5, 0xc4, 0x8b, 0x2c, 0xae, 0x4a, 0x5e, 0x93, + 0xfb, 0x4b, 0x4e, 0x42, 0x4e, 0xc6, 0xcf, 0xbe, 0xfa, 0x9e, 0x1c, 0xe3, 0xea, 0xbb, 0xf2, 0x17, + 0xb9, 0x94, 0xf9, 0x11, 0x42, 0x62, 0x77, 0xcb, 0x8c, 0x05, 0xc9, 0xb9, 0x86, 0x15, 0x64, 0xd7, + 0x1a, 0x03, 0xc2, 0x53, 0x3d, 0xae, 0xb2, 0x45, 0x31, 0x25, 0x7b, 0xee, 0x36, 0x2c, 0x06, 0x69, + 0x03, 0x34, 0xb3, 0x9a, 0x05, 0xed, 0x39, 0xc1, 0x24, 0xb6, 0xa1, 0x0d, 0x40, 0x09, 0xa9, 0x11, + 0x4c, 0xfe, 0x24, 0xb4, 0x44, 0xf2, 0xa4, 0x26, 0x63, 0x79, 0x52, 0x8b, 0x30, 0xc1, 0xd2, 0x0a, + 0x58, 0xbe, 0x10, 0x2b, 0x10, 0x49, 0x43, 0x16, 0xed, 0x87, 0x69, 0x89, 0x41, 0xb9, 0xf2, 0x6f, + 0x9a, 0x9a, 0x1d, 0x11, 0xec, 0x8e, 0x90, 0xdc, 0xb2, 0xc4, 0xd5, 0xc6, 0x93, 0xb8, 0xb9, 0x74, + 0x89, 0xfb, 0x2e, 0x2c, 0x87, 0x92, 0x43, 0x41, 0x62, 0x7b, 0x99, 0xd2, 0x9a, 0x2e, 0x77, 0x0b, + 0xa3, 0xe4, 0xee, 0x3f, 0xce, 0x42, 0x89, 0xcf, 0x82, 0x7a, 0x30, 0x22, 0x59, 0x55, 0x93, 0x92, + 0x55, 0xff, 0x6f, 0x65, 0x83, 0x55, 0x83, 0x38, 0x67, 0x91, 0x32, 0xf3, 0xeb, 0x09, 0xc1, 0x27, + 0x9a, 0x86, 0x39, 0xe6, 0x4b, 0x92, 0xe9, 0x53, 0xbd, 0x24, 0x81, 0xe4, 0x1c, 0x34, 0x35, 0xa9, + 0xa0, 0x34, 0x4e, 0x76, 0xd9, 0x4c, 0x66, 0x76, 0xd9, 0xec, 0xa9, 0xb2, 0xcb, 0xe6, 0x4e, 0xf5, + 0x26, 0xe5, 0xe2, 0x38, 0x6f, 0x52, 0xf4, 0xf1, 0xb2, 0xce, 0xe6, 0x23, 0x59, 0x67, 0x19, 0xb7, + 0xa1, 0xe8, 0x3c, 0x72, 0xc8, 0x16, 0xce, 0x2b, 0x87, 0x6c, 0xf1, 0x1c, 0x72, 0xc8, 0x96, 0xce, + 0x9e, 0x43, 0xb6, 0x7c, 0x2e, 0x39, 0x64, 0x97, 0xce, 0x21, 0x87, 0xac, 0x3c, 0x46, 0x0e, 0xd9, + 0xe8, 0x57, 0x3a, 0x97, 0x33, 0x5f, 0xe9, 0x8c, 0xca, 0x41, 0x5b, 0x39, 0x4b, 0x0e, 0xda, 0x95, + 0x33, 0xe7, 0xa0, 0x5d, 0x3d, 0xa7, 0x57, 0x3a, 0xd7, 0xce, 0x29, 0x11, 0x6c, 0xf5, 0x4c, 0x89, + 0x60, 0xd7, 0x7f, 0xb5, 0x5e, 0xe9, 0x7c, 0xa2, 0xb1, 0x87, 0x91, 0xfc, 0x95, 0x20, 0x57, 0x7c, + 0x5a, 0xf2, 0x2b, 0x41, 0xcb, 0xc7, 0x1b, 0x0c, 0x42, 0x91, 0xed, 0xac, 0x2a, 0xfb, 0x20, 0x73, + 0x63, 0x1c, 0xe4, 0x8a, 0x01, 0x25, 0x69, 0x88, 0x84, 0x65, 0x7e, 0x46, 0x5d, 0xe6, 0xa5, 0x14, + 0x25, 0x24, 0xdb, 0xc1, 0xff, 0x52, 0xa0, 0x49, 0x49, 0xe7, 0xa2, 0xaa, 0x7f, 0x93, 0x47, 0xf4, + 0x6b, 0x9c, 0x47, 0x94, 0xa1, 0x07, 0x67, 0xc7, 0xcd, 0x0a, 0xfa, 0x4b, 0x8d, 0xbd, 0x6d, 0xcb, + 0xe4, 0x1a, 0x33, 0x8b, 0x6b, 0xce, 0x46, 0xef, 0x66, 0x32, 0xbd, 0xff, 0x5d, 0x1e, 0x40, 0xf2, + 0xa1, 0x93, 0x22, 0x31, 0x82, 0x05, 0x72, 0x12, 0x0b, 0xdc, 0x80, 0x59, 0x22, 0x24, 0xf0, 0x40, + 0x35, 0x44, 0xd5, 0xca, 0x08, 0xd5, 0x14, 0xc6, 0xa6, 0x9a, 0x6c, 0x0b, 0x62, 0xe2, 0xbc, 0x2c, + 0x88, 0xc9, 0x73, 0xb0, 0x20, 0xa6, 0xce, 0xf6, 0x2e, 0xb6, 0x98, 0xa5, 0x71, 0x2b, 0x7f, 0xab, + 0x05, 0x87, 0x44, 0xc8, 0xe8, 0x83, 0x08, 0x19, 0x55, 0x62, 0xf7, 0x9b, 0x59, 0x94, 0xf4, 0x61, + 0x16, 0x25, 0xbd, 0xad, 0x52, 0xd2, 0x72, 0xc2, 0x08, 0x8e, 0x8b, 0x65, 0x42, 0xfa, 0x41, 0x2e, + 0x7a, 0xfb, 0x9a, 0x16, 0x86, 0x56, 0x09, 0x27, 0x97, 0x4d, 0x38, 0xf9, 0x73, 0x24, 0x9c, 0xc2, + 0x79, 0x11, 0xce, 0xc4, 0x39, 0x10, 0xce, 0x64, 0x06, 0xe1, 0x54, 0xfe, 0x3d, 0x1f, 0xbd, 0x6f, + 0x43, 0xef, 0x40, 0x91, 0xb3, 0xb2, 0x38, 0xfe, 0x85, 0x04, 0x36, 0x17, 0xce, 0x83, 0x00, 0x25, + 0x68, 0x35, 0x81, 0x96, 0x8b, 0xa3, 0xd5, 0x54, 0x34, 0x01, 0x8a, 0xde, 0xa3, 0x19, 0x62, 0x1c, + 0x8f, 0x69, 0xb1, 0xc5, 0xa4, 0x04, 0x0c, 0x8e, 0x18, 0x02, 0xa3, 0x7b, 0x50, 0x0a, 0x09, 0x45, + 0xc4, 0x74, 0x52, 0xe8, 0x48, 0x5c, 0x71, 0x4a, 0x08, 0xa8, 0x2e, 0x82, 0x81, 0x3d, 0xde, 0x03, + 0xcb, 0x67, 0x2c, 0xc7, 0x23, 0xde, 0x3d, 0xb9, 0x0f, 0x15, 0x29, 0x3d, 0x26, 0x34, 0xf9, 0x8b, + 0x8e, 0x09, 0x4d, 0x8d, 0x13, 0x13, 0xfa, 0x7d, 0x0d, 0x4a, 0xfc, 0x7c, 0xa9, 0xb5, 0xf1, 0x79, + 0x7a, 0xb8, 0xcc, 0x66, 0xd0, 0xb8, 0xcd, 0x10, 0x98, 0x66, 0xbc, 0x45, 0xb9, 0x4a, 0x0c, 0xc0, + 0xd1, 0x5d, 0x76, 0x52, 0x0c, 0x37, 0xc7, 0xf7, 0x2a, 0x34, 0xeb, 0x44, 0x4c, 0x5f, 0x46, 0x0e, + 0x11, 0x2a, 0xdf, 0x29, 0xc0, 0x12, 0x0f, 0x21, 0x8a, 0x8b, 0x08, 0x9e, 0x8a, 0xf2, 0x06, 0xcc, + 0xb5, 0x8f, 0x8f, 0xb6, 0x9f, 0x86, 0x9d, 0x33, 0x53, 0x28, 0x52, 0x4b, 0x18, 0x9b, 0xd6, 0x04, + 0xf3, 0x67, 0xea, 0x42, 0xad, 0x44, 0xeb, 0xa0, 0x0b, 0xbc, 0x20, 0x69, 0x9f, 0x45, 0x5c, 0x62, + 0xf5, 0xc4, 0xdf, 0x6d, 0xe3, 0x97, 0x7e, 0x90, 0x2c, 0xc0, 0x4b, 0xc8, 0x84, 0x12, 0xfb, 0xb5, + 0xf9, 0xea, 0x11, 0x16, 0x79, 0xae, 0xb7, 0xe5, 0x93, 0x4c, 0x5c, 0xc9, 0x86, 0x84, 0xc4, 0x42, + 0xab, 0x72, 0x37, 0xe8, 0x69, 0x52, 0x1a, 0x07, 0x7b, 0x44, 0xfa, 0xde, 0x18, 0x7d, 0x47, 0x51, + 0xa3, 0xaf, 0x49, 0x83, 0x54, 0x0f, 0x6a, 0x79, 0x1d, 0x88, 0xbb, 0x27, 0x9e, 0xd2, 0x29, 0x22, + 0x29, 0xf1, 0x96, 0x95, 0x7b, 0xa0, 0x47, 0x27, 0x9e, 0xfc, 0xa4, 0x23, 0x39, 0xc7, 0x53, 0x79, + 0x80, 0xaa, 0x4c, 0x2e, 0xab, 0x17, 0x25, 0x3c, 0xfc, 0x73, 0x0d, 0x2e, 0x1b, 0xd8, 0x63, 0xf1, + 0xf4, 0x8f, 0x2c, 0x1f, 0xbb, 0x47, 0x96, 0x7b, 0x28, 0x68, 0x24, 0x3c, 0x29, 0x4d, 0x39, 0xa9, + 0x2f, 0xa9, 0x27, 0xc5, 0xa8, 0xf2, 0xdd, 0x48, 0xb0, 0x23, 0xb9, 0xcf, 0x8c, 0xd3, 0x4a, 0xde, + 0xc5, 0xfc, 0x2f, 0x6a, 0x17, 0x2b, 0xff, 0x5c, 0x60, 0xcf, 0x6d, 0x47, 0xfa, 0x05, 0xbf, 0x79, + 0xf9, 0xf2, 0x9b, 0x97, 0x2f, 0xe3, 0xbd, 0x7c, 0xf9, 0x87, 0x1c, 0xff, 0xec, 0x01, 0x31, 0xe7, + 0xee, 0x05, 0x6e, 0x22, 0x13, 0xf9, 0x6b, 0x31, 0x05, 0x4b, 0x8d, 0x39, 0x0a, 0xa2, 0x1a, 0x73, + 0x4c, 0xa6, 0xde, 0x0b, 0xcc, 0xc1, 0xdc, 0x28, 0xfc, 0x54, 0x63, 0xb0, 0x03, 0x25, 0xa9, 0xf3, + 0x84, 0x5b, 0xa3, 0x0d, 0xd5, 0x18, 0x4c, 0x7d, 0x63, 0x2e, 0x8b, 0x9d, 0x4e, 0x96, 0x85, 0x99, + 0xd5, 0x69, 0x92, 0xb3, 0xf2, 0x9f, 0x45, 0x35, 0x75, 0x27, 0x91, 0x0b, 0xdf, 0x57, 0x54, 0x6a, + 0xa2, 0xeb, 0x1f, 0x36, 0x0b, 0xcb, 0x43, 0x56, 0xc2, 0x77, 0x02, 0x87, 0x8d, 0x5b, 0x9e, 0x0b, + 0x09, 0x6e, 0x9a, 0x48, 0x44, 0x11, 0xae, 0xdd, 0xbb, 0xe1, 0x81, 0x72, 0x47, 0x67, 0x31, 0xe9, + 0x18, 0x42, 0x2a, 0xe7, 0x87, 0x7f, 0x27, 0x88, 0xa9, 0xf0, 0x6b, 0xaa, 0x85, 0x84, 0x48, 0x8a, + 0x18, 0x4c, 0x44, 0x5f, 0x6e, 0x89, 0x84, 0x63, 0x16, 0xb4, 0x57, 0xae, 0x60, 0x45, 0x92, 0x99, + 0x92, 0x76, 0xdc, 0xe6, 0xbc, 0xce, 0x6f, 0xd1, 0x78, 0xe2, 0xd3, 0x14, 0xc5, 0x5e, 0x8d, 0x5e, + 0xe0, 0xaa, 0x50, 0x46, 0x02, 0x26, 0x6a, 0x44, 0x72, 0x92, 0x38, 0x63, 0x67, 0xde, 0x05, 0x47, + 0x32, 0x99, 0x84, 0xde, 0xe8, 0xf1, 0xb7, 0x1c, 0xbc, 0x84, 0x1e, 0xa9, 0x7a, 0x03, 0xe2, 0x2f, + 0x42, 0x65, 0x2a, 0xc8, 0x50, 0x15, 0x77, 0x65, 0xdf, 0x89, 0x27, 0x2d, 0x2c, 0x27, 0x7b, 0x4c, + 0xe2, 0x52, 0x51, 0xf2, 0xb5, 0xe4, 0x60, 0xfd, 0xcc, 0xc9, 0x9e, 0x82, 0x27, 0xe5, 0x91, 0xce, + 0xa6, 0xe7, 0x91, 0x6e, 0x25, 0x19, 0x20, 0x73, 0x99, 0x02, 0x33, 0xc1, 0xc4, 0xb8, 0x0b, 0x97, + 0xe3, 0x2a, 0x70, 0x07, 0x0f, 0x7a, 0xf6, 0xe0, 0x80, 0xde, 0x1d, 0x14, 0x8d, 0x74, 0x00, 0xb4, + 0x09, 0x57, 0x15, 0x75, 0x4c, 0x15, 0xb4, 0xe4, 0xf8, 0xb0, 0xf7, 0xe7, 0x23, 0x61, 0x88, 0xc3, + 0x9b, 0x30, 0x80, 0x88, 0xae, 0xb2, 0x77, 0xe8, 0x23, 0x20, 0xd0, 0x07, 0x70, 0x25, 0xde, 0x1a, + 0xbc, 0xee, 0x11, 0x97, 0x10, 0x23, 0x40, 0xce, 0xac, 0xf0, 0xff, 0x58, 0x83, 0x19, 0xd9, 0x95, + 0x48, 0x74, 0x66, 0xaf, 0xc2, 0x34, 0xbb, 0x22, 0x14, 0x19, 0x2a, 0xd3, 0x46, 0x58, 0x81, 0xca, + 0x30, 0xa5, 0x6a, 0x79, 0x51, 0x94, 0x6e, 0x72, 0x0a, 0xca, 0x4d, 0xce, 0x0a, 0x14, 0xeb, 0xce, + 0x8b, 0x01, 0x6d, 0x99, 0xa0, 0x2d, 0x41, 0xb9, 0xf2, 0x67, 0x15, 0xd0, 0x05, 0x6f, 0x07, 0xaf, + 0xcc, 0x82, 0x37, 0x65, 0x9a, 0xfc, 0xa6, 0x2c, 0x29, 0x60, 0x13, 0x9a, 0x68, 0x79, 0xc5, 0x44, + 0xdb, 0x56, 0x59, 0x8d, 0xb9, 0x69, 0x9f, 0x49, 0x12, 0x28, 0xc1, 0xe3, 0xb1, 0xd1, 0xec, 0x96, + 0xf4, 0x81, 0x96, 0xff, 0x75, 0x79, 0xd5, 0x03, 0x3d, 0x92, 0x41, 0x20, 0x2e, 0x26, 0x6f, 0x8f, + 0x5c, 0x6a, 0x14, 0x49, 0x56, 0x9f, 0xb1, 0x1e, 0x51, 0x53, 0x76, 0xc1, 0xd8, 0xd7, 0xf3, 0x3e, + 0x3d, 0xb2, 0xfb, 0x00, 0x9a, 0xed, 0x63, 0x88, 0x2d, 0xab, 0x05, 0x18, 0x5b, 0x2d, 0x48, 0x8a, + 0xab, 0x74, 0x2a, 0xc5, 0x35, 0x73, 0x02, 0xc5, 0x15, 0x51, 0xb3, 0xb3, 0x27, 0x56, 0xb3, 0x31, + 0x1d, 0x32, 0x77, 0x2a, 0x1d, 0xa2, 0x8a, 0xf7, 0x8b, 0x27, 0x14, 0xef, 0xb1, 0x28, 0x83, 0x7e, + 0x9a, 0x28, 0x43, 0x8a, 0xb0, 0x9f, 0x3f, 0xa1, 0xb0, 0x47, 0xe7, 0x2e, 0xec, 0x17, 0xce, 0x2a, + 0xec, 0x17, 0xcf, 0x2c, 0xec, 0x97, 0xce, 0x2a, 0xec, 0x97, 0x33, 0x85, 0x3d, 0x7a, 0x37, 0x96, + 0xef, 0x27, 0x32, 0x66, 0xd8, 0x9d, 0x6a, 0x4a, 0x6b, 0x82, 0x8b, 0x11, 0xe6, 0xe1, 0x94, 0x13, + 0x5d, 0x8c, 0x30, 0x2d, 0xe7, 0xab, 0xb0, 0x18, 0x69, 0x13, 0xf7, 0xa7, 0x31, 0x27, 0x37, 0xc6, + 0xf7, 0x49, 0x88, 0x4c, 0x04, 0x24, 0xf6, 0x89, 0x86, 0xb1, 0xf5, 0xd5, 0xda, 0xe2, 0xbe, 0x35, + 0x16, 0xa0, 0xc8, 0x1a, 0x8d, 0xa3, 0xb2, 0xf1, 0x52, 0xfa, 0x4d, 0x18, 0xd1, 0x6c, 0x8b, 0x4b, + 0xda, 0x13, 0x8f, 0x68, 0x8e, 0x1a, 0x91, 0x37, 0xa2, 0x2d, 0xb8, 0x1e, 0x69, 0xe1, 0xc9, 0x61, + 0x5e, 0xd5, 0xf3, 0xec, 0x83, 0x41, 0xf8, 0x79, 0x8e, 0x0c, 0x30, 0xd4, 0x82, 0xd7, 0xa2, 0xab, + 0x0a, 0x92, 0xc4, 0x82, 0xbe, 0xd8, 0x0d, 0x6f, 0x36, 0x60, 0xaa, 0x2b, 0x19, 0x52, 0xca, 0xea, + 0x08, 0x57, 0x32, 0xa4, 0x97, 0xcd, 0x94, 0xfc, 0x26, 0x41, 0xa9, 0xd7, 0xe3, 0xb7, 0xff, 0x51, + 0x98, 0xd4, 0x7b, 0x04, 0x16, 0x4d, 0x5e, 0xa3, 0xbc, 0x3a, 0x02, 0x02, 0x3d, 0x84, 0xb5, 0xc4, + 0xcc, 0x00, 0x39, 0x4d, 0xec, 0x35, 0x3a, 0x8f, 0x4c, 0xb8, 0x31, 0xb2, 0x22, 0x2a, 0x63, 0x65, + 0x45, 0x7c, 0x5d, 0x83, 0x6b, 0x89, 0x53, 0xa6, 0xe1, 0x0b, 0x42, 0x71, 0xaf, 0x53, 0x8a, 0x7b, + 0x7f, 0x24, 0xc5, 0x8d, 0xec, 0x81, 0x11, 0xde, 0xe8, 0x51, 0xd0, 0xef, 0xa6, 0x25, 0xa0, 0x09, + 0x56, 0xbb, 0x41, 0xa7, 0x71, 0xef, 0xe4, 0xd3, 0x50, 0x18, 0x6e, 0xe4, 0x18, 0xe8, 0x1b, 0x5a, + 0xca, 0xc5, 0x03, 0x55, 0x59, 0x6c, 0x1e, 0x9f, 0xa2, 0xf3, 0xa8, 0x9e, 0x7c, 0x1e, 0x61, 0x1f, + 0x6c, 0x2a, 0x59, 0x23, 0xa1, 0x3f, 0xd0, 0x52, 0x68, 0xbf, 0xd6, 0x16, 0x1f, 0xeb, 0x79, 0x83, + 0x4e, 0xe6, 0x83, 0xd3, 0x6c, 0x0a, 0xef, 0x82, 0xcd, 0x25, 0x63, 0x1c, 0xf4, 0x4d, 0x0d, 0x5e, + 0x4b, 0x9f, 0xae, 0x98, 0xcd, 0x9b, 0x74, 0x36, 0xb5, 0x53, 0x6e, 0x8d, 0x32, 0xa1, 0xec, 0xd1, + 0x52, 0x39, 0x5a, 0x28, 0xdf, 0x9b, 0x23, 0x38, 0x5a, 0xe8, 0xdf, 0x6f, 0x69, 0x50, 0x19, 0xb9, + 0x74, 0x96, 0x94, 0xf8, 0x16, 0x5d, 0x58, 0xfd, 0xf4, 0xdb, 0x4c, 0xbb, 0x61, 0x2b, 0x1b, 0x63, + 0x3c, 0xf4, 0x1d, 0x0d, 0x3e, 0x95, 0xb5, 0x01, 0x6c, 0x66, 0xeb, 0x74, 0x66, 0x0f, 0xce, 0xb4, + 0xe5, 0xd2, 0xe4, 0xc6, 0x1b, 0xf5, 0xcc, 0x41, 0xf1, 0x8f, 0x61, 0x29, 0xd1, 0xb4, 0x3f, 0x61, + 0x9c, 0x4a, 0x79, 0x63, 0x27, 0x75, 0x7f, 0x97, 0x7e, 0xad, 0x31, 0x25, 0xa8, 0x96, 0x39, 0xb9, + 0x07, 0x70, 0x39, 0xd5, 0x40, 0xc8, 0xea, 0xa8, 0x28, 0x77, 0xd4, 0x8c, 0xa5, 0x30, 0xc8, 0xa2, + 0xe8, 0x8c, 0x5d, 0x99, 0xa7, 0xed, 0x6a, 0x27, 0x85, 0xe2, 0x15, 0x69, 0x7d, 0xa2, 0x1e, 0xb7, + 0x53, 0x64, 0xc3, 0xa9, 0x57, 0x6b, 0xc0, 0x8d, 0x71, 0x24, 0xe8, 0x89, 0xfa, 0xfc, 0x10, 0x5e, + 0x1f, 0x43, 0x10, 0x9e, 0x88, 0x50, 0x4c, 0x78, 0x63, 0x3c, 0x69, 0x76, 0xa2, 0x5e, 0x77, 0xe1, + 0xcd, 0x31, 0x45, 0xc9, 0x89, 0xba, 0xfd, 0x12, 0xac, 0x8f, 0x2f, 0x07, 0x4e, 0x14, 0xaa, 0x69, + 0xb2, 0x6c, 0x20, 0xf1, 0x61, 0xd4, 0x33, 0x7c, 0xf9, 0xa9, 0xf2, 0x57, 0x39, 0x58, 0x4c, 0x7a, + 0x7e, 0x3a, 0xe2, 0x15, 0xc8, 0x4e, 0xec, 0x33, 0xb8, 0x1b, 0x59, 0x8f, 0x59, 0xd5, 0xcf, 0xe1, + 0xc6, 0x2e, 0x66, 0xce, 0xe5, 0xa3, 0xb8, 0x2b, 0x66, 0xf6, 0x57, 0x6b, 0x47, 0xa5, 0x0b, 0x49, + 0x3b, 0x2a, 0xef, 0xf5, 0x77, 0x35, 0x80, 0x4d, 0xab, 0x7b, 0x78, 0x3c, 0xa4, 0x77, 0x3b, 0x69, + 0x17, 0x7f, 0xcd, 0xa4, 0x8b, 0xbf, 0x37, 0x95, 0x97, 0x2f, 0x41, 0x27, 0xa3, 0xe3, 0x49, 0x67, + 0x0e, 0xe4, 0xfd, 0x9e, 0x26, 0xee, 0xad, 0x9a, 0x3e, 0x3e, 0x4a, 0xfc, 0x08, 0x4d, 0x05, 0x66, + 0x78, 0xbe, 0xfe, 0x13, 0xe9, 0xf6, 0x53, 0xa9, 0x23, 0x30, 0x75, 0xfc, 0xd4, 0x3a, 0xee, 0x73, + 0x18, 0xfe, 0xe5, 0x57, 0xb9, 0x8e, 0x1c, 0x51, 0x73, 0xe0, 0x63, 0x77, 0x60, 0xf5, 0xf9, 0x85, + 0x5d, 0x50, 0xae, 0xfc, 0xb5, 0x26, 0x5f, 0x9f, 0xa1, 0x2f, 0xc2, 0x54, 0xcd, 0x19, 0xf8, 0x98, + 0x7e, 0xab, 0x25, 0x9e, 0x20, 0x1f, 0x00, 0x6e, 0x70, 0x28, 0xb6, 0x31, 0x02, 0x67, 0xc5, 0xa0, + 0x6f, 0x2d, 0x83, 0x86, 0x13, 0x26, 0xf0, 0x84, 0xdb, 0x21, 0x6f, 0xd4, 0x6f, 0x87, 0xf7, 0x74, + 0xe8, 0x9d, 0xf0, 0x25, 0x72, 0x2c, 0x4f, 0x4d, 0x00, 0x6d, 0x50, 0x08, 0x36, 0x31, 0x06, 0xbd, + 0xf2, 0x1e, 0x40, 0x58, 0x79, 0xa2, 0xfb, 0xe5, 0x37, 0x95, 0x0f, 0x20, 0x8c, 0x78, 0xa1, 0xf5, + 0x31, 0xcc, 0xc7, 0xde, 0x02, 0xa1, 0x1b, 0x30, 0xcb, 0xbe, 0xa9, 0x24, 0x9e, 0x17, 0x31, 0x24, + 0xb5, 0x92, 0x1e, 0x33, 0x47, 0x91, 0xbe, 0xc3, 0xa5, 0xd4, 0xad, 0xbf, 0x00, 0xd8, 0x1d, 0xf6, + 0x2c, 0x9f, 0x45, 0x70, 0x2f, 0xc1, 0x82, 0xf2, 0x8d, 0x26, 0xd6, 0xa4, 0x5f, 0x40, 0x4b, 0x30, + 0x2f, 0xbe, 0xbd, 0xd5, 0xea, 0xb4, 0x79, 0xb5, 0x86, 0x16, 0xe0, 0xe2, 0xae, 0x87, 0x5d, 0xba, + 0x7c, 0x5e, 0x99, 0x43, 0xb3, 0x30, 0x6d, 0x76, 0xb6, 0x79, 0x31, 0x4f, 0x50, 0x83, 0xef, 0x68, + 0x05, 0xa8, 0x85, 0xf5, 0x0d, 0x98, 0x0e, 0x3e, 0x49, 0x8e, 0x2e, 0x42, 0xa9, 0xed, 0xb8, 0x47, + 0x56, 0x9f, 0x16, 0xf5, 0x0b, 0x48, 0x87, 0x19, 0xfe, 0x0c, 0x85, 0xd5, 0x68, 0xeb, 0x3f, 0x2d, + 0x00, 0x84, 0xdf, 0x2e, 0x40, 0x73, 0x00, 0x66, 0x67, 0x7b, 0x6f, 0x77, 0xa7, 0x5e, 0x35, 0x1b, + 0xfa, 0x05, 0x04, 0x30, 0x59, 0xdd, 0xd9, 0x69, 0xb4, 0xeb, 0xba, 0x86, 0x8a, 0x50, 0x30, 0x1a, + 0xd5, 0xba, 0x9e, 0x43, 0x33, 0x50, 0x34, 0x8d, 0xdd, 0x76, 0x8d, 0xc0, 0xe4, 0x49, 0xa7, 0x0f, + 0x1a, 0xe6, 0x5e, 0x50, 0x53, 0x40, 0x25, 0x98, 0xaa, 0x6d, 0xb7, 0xdb, 0x8d, 0x9a, 0xa9, 0x4f, + 0x90, 0x2e, 0x79, 0x61, 0xcf, 0xd8, 0xd6, 0x27, 0xd1, 0x3c, 0xcc, 0xb6, 0xb6, 0x1f, 0xec, 0x6d, + 0x35, 0xaa, 0x86, 0xb9, 0xd9, 0xa8, 0x9a, 0xfa, 0x14, 0xe9, 0xa1, 0xd6, 0x96, 0x6a, 0x8a, 0x74, + 0xa2, 0x72, 0xcd, 0x34, 0x42, 0x30, 0x57, 0xdb, 0x6a, 0xd4, 0x1e, 0xed, 0x6d, 0x55, 0x1f, 0x35, + 0x1a, 0x3b, 0x0d, 0x43, 0x07, 0xb2, 0xaf, 0x64, 0xe4, 0x5a, 0x6b, 0xb7, 0x63, 0x36, 0x8c, 0xbd, + 0x7a, 0xc3, 0xac, 0x36, 0x5b, 0x1d, 0xbd, 0x44, 0x80, 0x49, 0x43, 0x67, 0xab, 0x6a, 0xd4, 0xf7, + 0x9a, 0xed, 0xfb, 0xdb, 0xfa, 0x0c, 0xed, 0xa0, 0xbd, 0x57, 0x6d, 0xb5, 0xb6, 0xc9, 0x2c, 0xf7, + 0x9a, 0x75, 0x7d, 0x96, 0x6c, 0xa2, 0xdc, 0x41, 0xc7, 0x24, 0xf3, 0x9f, 0xa3, 0xfb, 0x4f, 0x77, + 0x60, 0xaf, 0xd6, 0xde, 0x6b, 0x55, 0x37, 0x1b, 0x2d, 0xfd, 0x22, 0x2a, 0xc3, 0x62, 0x58, 0xf9, + 0xd1, 0xb6, 0xf1, 0x88, 0x83, 0xeb, 0xa4, 0xe7, 0x9d, 0xaa, 0x59, 0xdb, 0x22, 0x0d, 0x1d, 0x73, + 0xdb, 0x68, 0xe8, 0xf3, 0xa4, 0x8b, 0x7a, 0xa3, 0xd5, 0x60, 0xd0, 0xac, 0x12, 0x91, 0xca, 0x1d, + 0x63, 0xfb, 0x4b, 0x5f, 0x96, 0x16, 0xb6, 0x80, 0x5e, 0x83, 0x6b, 0xbc, 0xdf, 0xf6, 0x76, 0x7b, + 0xef, 0xc9, 0xb6, 0xd9, 0x6c, 0x3f, 0xd8, 0x33, 0x1a, 0x3b, 0xad, 0x66, 0xad, 0xba, 0xd7, 0xde, + 0x7d, 0xac, 0x2f, 0xa2, 0x55, 0x58, 0x89, 0x83, 0x90, 0x75, 0xb4, 0x9a, 0xe6, 0x97, 0xf5, 0x25, + 0xb4, 0x08, 0x7a, 0xa7, 0x61, 0xee, 0x19, 0x8d, 0x0f, 0x77, 0x9b, 0x46, 0xa3, 0xbe, 0xd7, 0xea, + 0xb4, 0xf5, 0x65, 0x52, 0xfb, 0x20, 0x5a, 0x7b, 0x49, 0x6c, 0x4d, 0xab, 0x6a, 0x36, 0x3a, 0x26, + 0xad, 0x2b, 0x93, 0x23, 0xa1, 0x75, 0x8d, 0x6a, 0xbd, 0x61, 0x90, 0x9d, 0xb9, 0x4c, 0x8f, 0x84, + 0x6d, 0x77, 0xa3, 0xda, 0x32, 0xb7, 0xf4, 0x15, 0x72, 0xe8, 0xe4, 0xf8, 0x29, 0xca, 0x15, 0x74, + 0x19, 0x96, 0xf8, 0x94, 0x5a, 0x8d, 0x6a, 0xa7, 0xb1, 0xb5, 0xdd, 0xe2, 0xa8, 0x57, 0x49, 0x13, + 0xdd, 0xfc, 0xda, 0x56, 0xa3, 0xbe, 0xdb, 0x6a, 0xec, 0xd5, 0xb6, 0x1f, 0x3f, 0xae, 0xb6, 0xeb, + 0x1d, 0xfd, 0xda, 0x7a, 0x1b, 0x20, 0xfc, 0xe2, 0x17, 0xa1, 0x0c, 0x42, 0xe6, 0xac, 0x46, 0xbf, + 0x40, 0x46, 0x10, 0x72, 0x4e, 0xd7, 0x08, 0xf1, 0x52, 0xa6, 0x09, 0x18, 0x60, 0x9e, 0x7f, 0xe2, + 0xce, 0xc0, 0x5f, 0xc5, 0x5d, 0x1f, 0xf7, 0xf4, 0xfc, 0xfa, 0x3a, 0x4c, 0x07, 0x1f, 0x94, 0x22, + 0xe8, 0x1d, 0xec, 0xd3, 0x92, 0x7e, 0x81, 0xa0, 0xb3, 0xe0, 0x2a, 0xab, 0xd0, 0xd6, 0xff, 0xa9, + 0x00, 0x48, 0xb8, 0x14, 0x12, 0x6f, 0x12, 0x8a, 0xb7, 0xbb, 0x87, 0x32, 0x4b, 0x4a, 0x5f, 0xee, + 0x09, 0x58, 0x92, 0x70, 0x6a, 0xac, 0x3a, 0x87, 0x96, 0x69, 0xfe, 0x48, 0xb4, 0x3e, 0x4f, 0x46, + 0x7f, 0x80, 0xfd, 0x80, 0xd3, 0x0b, 0x64, 0x53, 0x22, 0xf2, 0x86, 0x37, 0x4d, 0x90, 0x13, 0xe9, + 0x60, 0xc6, 0x90, 0xbc, 0x6e, 0x92, 0x10, 0x9b, 0x9a, 0x20, 0xc4, 0x5b, 0xa6, 0xd0, 0x75, 0xb8, + 0xd2, 0xc1, 0x7e, 0xfc, 0x6e, 0x82, 0x03, 0x14, 0xd1, 0x0a, 0x2c, 0x73, 0x80, 0x20, 0xb8, 0xcd, + 0xdb, 0xa6, 0xc9, 0x16, 0xb2, 0xdf, 0x7c, 0xd7, 0x74, 0x20, 0x0b, 0x13, 0x55, 0xc1, 0x03, 0x28, + 0xbd, 0x44, 0xce, 0x7f, 0x87, 0xc8, 0x3b, 0x9e, 0xc1, 0xa7, 0xcf, 0x10, 0x5c, 0x03, 0x1f, 0x39, + 0xcf, 0xc5, 0xab, 0x5a, 0x7d, 0x96, 0xcc, 0x52, 0x4d, 0xd5, 0xe4, 0x03, 0xcd, 0xa1, 0x6b, 0x70, + 0x99, 0xfd, 0x4e, 0x08, 0x5a, 0xeb, 0x17, 0xd1, 0x15, 0xb8, 0x14, 0x69, 0x16, 0xda, 0x80, 0xb1, + 0x93, 0xf0, 0x7a, 0x78, 0x7f, 0xf3, 0xe8, 0x2a, 0x94, 0xe3, 0x29, 0x3e, 0xbc, 0x15, 0xa1, 0x1b, + 0xb0, 0x26, 0x82, 0xb8, 0xf1, 0xf0, 0x2e, 0x87, 0x5a, 0x20, 0x3b, 0xc7, 0x02, 0x60, 0x11, 0x0f, + 0x84, 0x03, 0x2c, 0xa2, 0x4f, 0xc1, 0x6b, 0x0c, 0x20, 0xd1, 0xc0, 0xe4, 0x60, 0x4b, 0xeb, 0xdf, + 0xd6, 0x60, 0x56, 0xb9, 0x6d, 0x22, 0x7c, 0x2d, 0x2a, 0x78, 0x96, 0x8b, 0x7e, 0x81, 0x9c, 0xb8, + 0xa8, 0x54, 0xbe, 0x8d, 0xa0, 0x6b, 0x64, 0xa0, 0x58, 0x93, 0xf0, 0x1f, 0x0d, 0xdc, 0xc5, 0xf6, + 0x73, 0xdc, 0xd3, 0x73, 0x64, 0x97, 0x62, 0x60, 0xf7, 0x2d, 0xbb, 0x4f, 0x48, 0x5f, 0x1e, 0xd3, + 0x38, 0x1e, 0x0c, 0x48, 0xc7, 0x85, 0xf5, 0xfd, 0xa4, 0xfb, 0x2e, 0x72, 0x4c, 0x4a, 0x6d, 0x38, + 0xc7, 0x68, 0x8b, 0xe8, 0x49, 0x8b, 0xb5, 0x74, 0x7c, 0x67, 0x38, 0x24, 0xb3, 0x5a, 0xff, 0x81, + 0x06, 0x7a, 0xf4, 0x6b, 0x18, 0x84, 0x8b, 0xaa, 0x3d, 0xf1, 0x81, 0x3a, 0xfd, 0x42, 0x48, 0x2c, + 0xa2, 0x4a, 0x23, 0x14, 0xd5, 0xf1, 0x2d, 0xd7, 0x17, 0x35, 0x39, 0xc2, 0x24, 0xa4, 0x5b, 0x51, + 0x91, 0x27, 0xbd, 0x3c, 0xb2, 0xfb, 0xfd, 0xaf, 0x38, 0x47, 0xfb, 0x36, 0x61, 0x9a, 0x4b, 0xb0, + 0x50, 0xed, 0xf5, 0xa2, 0x24, 0xa4, 0x4f, 0x10, 0x1a, 0x67, 0xdd, 0xc7, 0xda, 0x26, 0x29, 0xa7, + 0x91, 0x71, 0x62, 0x4d, 0x53, 0x64, 0x51, 0x64, 0xc0, 0x58, 0x4b, 0x71, 0xfd, 0xb1, 0xf2, 0x95, + 0x00, 0x32, 0x91, 0x90, 0x90, 0xf4, 0x0b, 0x54, 0xf7, 0xb6, 0x45, 0x51, 0x23, 0xc5, 0x5a, 0x50, + 0xcc, 0x51, 0x5e, 0xa1, 0x57, 0x41, 0xbc, 0x26, 0xbf, 0xd9, 0xfc, 0xfe, 0x4f, 0x56, 0x2f, 0x7c, + 0xef, 0x93, 0x55, 0xed, 0xfb, 0x9f, 0xac, 0x6a, 0x3f, 0xfe, 0x64, 0xf5, 0xc2, 0xdf, 0xfc, 0xc7, + 0xaa, 0xf6, 0x95, 0x3b, 0xd2, 0xbf, 0x20, 0x3b, 0xb2, 0x7c, 0xd7, 0x7e, 0xe9, 0x50, 0xc3, 0x42, + 0x14, 0x06, 0xf8, 0xd6, 0xf0, 0xf0, 0xe0, 0xd6, 0x70, 0xff, 0x56, 0x68, 0x26, 0xed, 0x4f, 0xd2, + 0xaf, 0x09, 0xde, 0xf9, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x77, 0xe9, 0x02, 0x2f, 0xde, 0x6c, + 0x00, 0x00, } func (m *CNStore) Marshal() (dAtA []byte, err error) { @@ -6863,6 +7145,73 @@ func (m *CNStore) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + { + size, err := m.DDLVisibilityFrontier.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintLogservice(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xd2 + if m.DDLVisibilityActivationFenced { + i-- + if m.DDLVisibilityActivationFenced { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xc8 + } + if m.DDLVisibilityActivationPrepared { + i-- + if m.DDLVisibilityActivationPrepared { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xc0 + } + if m.DDLVisibilityDeployedProtocol != 0 { + i = encodeVarintLogservice(dAtA, i, uint64(m.DDLVisibilityDeployedProtocol)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xb8 + } + if m.ViewMetadataIngressReady { + i-- + if m.ViewMetadataIngressReady { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xb0 + } + if m.DDLVisibilityBarrierReady { + i-- + if m.DDLVisibilityBarrierReady { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xa8 + } if m.ViewMetadataAdmissionReady { i-- if m.ViewMetadataAdmissionReady { @@ -7165,6 +7514,16 @@ func (m *LogStore) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.DDLVisibilityDeployedProtocolSupported { + i-- + if m.DDLVisibilityDeployedProtocolSupported { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x40 + } { size, err := m.Locality.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -7409,6 +7768,52 @@ func (m *Resource) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *DDLVisibilityActivationTarget) Marshal() (dAtA []byte, err error) { + size := m.ProtoSize() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *DDLVisibilityActivationTarget) MarshalTo(dAtA []byte) (int, error) { + size := m.ProtoSize() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *DDLVisibilityActivationTarget) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.QueryAddress) > 0 { + i -= len(m.QueryAddress) + copy(dAtA[i:], m.QueryAddress) + i = encodeVarintLogservice(dAtA, i, uint64(len(m.QueryAddress))) + i-- + dAtA[i] = 0x1a + } + if m.Generation != 0 { + i = encodeVarintLogservice(dAtA, i, uint64(m.Generation)) + i-- + dAtA[i] = 0x10 + } + if len(m.ServiceID) > 0 { + i -= len(m.ServiceID) + copy(dAtA[i:], m.ServiceID) + i = encodeVarintLogservice(dAtA, i, uint64(len(m.ServiceID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *CNStoreHeartbeat) Marshal() (dAtA []byte, err error) { size := m.ProtoSize() dAtA = make([]byte, size) @@ -7433,6 +7838,77 @@ func (m *CNStoreHeartbeat) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + { + size, err := m.DDLVisibilityFrontier.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintLogservice(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xf2 + if m.DDLVisibilityActivationFenced { + i-- + if m.DDLVisibilityActivationFenced { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xe8 + } + if m.DDLVisibilityActivationPrepared { + i-- + if m.DDLVisibilityActivationPrepared { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xe0 + } + if len(m.DDLVisibilityEpochCommitTargets) > 0 { + for iNdEx := len(m.DDLVisibilityEpochCommitTargets) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.DDLVisibilityEpochCommitTargets[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintLogservice(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xda + } + } + if m.DDLVisibilityDeployedProtocol != 0 { + i = encodeVarintLogservice(dAtA, i, uint64(m.DDLVisibilityDeployedProtocol)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xd0 + } + if m.DDLVisibilityBarrierReady { + i-- + if m.DDLVisibilityBarrierReady { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xc8 + } if m.ViewMetadataIngressReady { i-- if m.ViewMetadataIngressReady { @@ -7699,6 +8175,16 @@ func (m *LogStoreHeartbeat) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.DDLVisibilityDeployedProtocolSupported { + i-- + if m.DDLVisibilityDeployedProtocolSupported { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x58 + } if m.ViewMetadataAdmissionSupported { i-- if m.ViewMetadataAdmissionSupported { @@ -8165,12 +8651,12 @@ func (m *LogRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x40 } - n13, err13 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.TS, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.TS):]) - if err13 != nil { - return 0, err13 + n15, err15 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.TS, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.TS):]) + if err15 != nil { + return 0, err15 } - i -= n13 - i = encodeVarintLogservice(dAtA, i, uint64(n13)) + i -= n15 + i = encodeVarintLogservice(dAtA, i, uint64(n15)) i-- dAtA[i] = 0x3a if m.TNID != 0 { @@ -9625,6 +10111,11 @@ func (m *CommandBatch) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.DDLVisibilityDeployedProtocol != 0 { + i = encodeVarintLogservice(dAtA, i, uint64(m.DDLVisibilityDeployedProtocol)) + i-- + dAtA[i] = 0x30 + } if m.ViewMetadataAdmission != nil { { size, err := m.ViewMetadataAdmission.MarshalToSizedBuffer(dAtA[:i]) @@ -9851,6 +10342,61 @@ func (m *CNStoreInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + { + size, err := m.DDLVisibilityFrontier.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintLogservice(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xfa + if m.DDLVisibilityActivationFenced { + i-- + if m.DDLVisibilityActivationFenced { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xf0 + } + if m.DDLVisibilityActivationPrepared { + i-- + if m.DDLVisibilityActivationPrepared { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xe8 + } + if m.DDLVisibilityDeployedProtocol != 0 { + i = encodeVarintLogservice(dAtA, i, uint64(m.DDLVisibilityDeployedProtocol)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xe0 + } + if m.DDLVisibilityBarrierReady { + i-- + if m.DDLVisibilityBarrierReady { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xd8 + } if m.ViewMetadataIngressReady { i-- if m.ViewMetadataIngressReady { @@ -10105,6 +10651,11 @@ func (m *CNState) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.DDLVisibilityDeployedProtocol != 0 { + i = encodeVarintLogservice(dAtA, i, uint64(m.DDLVisibilityDeployedProtocol)) + i-- + dAtA[i] = 0x10 + } if len(m.Stores) > 0 { for k := range m.Stores { v := m.Stores[k] @@ -10547,6 +11098,11 @@ func (m *ClusterDetails) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.DDLVisibilityDeployedProtocol != 0 { + i = encodeVarintLogservice(dAtA, i, uint64(m.DDLVisibilityDeployedProtocol)) + i-- + dAtA[i] = 0x38 + } if m.ViewMetadataAdmission != nil { { size, err := m.ViewMetadataAdmission.MarshalToSizedBuffer(dAtA[:i]) @@ -10863,6 +11419,16 @@ func (m *LogStoreInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.DDLVisibilityDeployedProtocolSupported { + i-- + if m.DDLVisibilityDeployedProtocolSupported { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x58 + } if m.ViewMetadataAdmissionSupported { i-- if m.ViewMetadataAdmissionSupported { @@ -12331,14 +12897,31 @@ func (m *CNStore) ProtoSize() (n int) { if m.ViewMetadataAdmissionReady { n += 3 } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) + if m.DDLVisibilityBarrierReady { + n += 3 } - return n -} - -func (m *TNStore) ProtoSize() (n int) { - if m == nil { + if m.ViewMetadataIngressReady { + n += 3 + } + if m.DDLVisibilityDeployedProtocol != 0 { + n += 2 + sovLogservice(uint64(m.DDLVisibilityDeployedProtocol)) + } + if m.DDLVisibilityActivationPrepared { + n += 3 + } + if m.DDLVisibilityActivationFenced { + n += 3 + } + l = m.DDLVisibilityFrontier.ProtoSize() + n += 2 + l + sovLogservice(uint64(l)) + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *TNStore) ProtoSize() (n int) { + if m == nil { return 0 } var l int @@ -12424,6 +13007,9 @@ func (m *LogStore) ProtoSize() (n int) { } l = m.Locality.ProtoSize() n += 1 + l + sovLogservice(uint64(l)) + if m.DDLVisibilityDeployedProtocolSupported { + n += 2 + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -12514,6 +13100,29 @@ func (m *Resource) ProtoSize() (n int) { return n } +func (m *DDLVisibilityActivationTarget) ProtoSize() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ServiceID) + if l > 0 { + n += 1 + l + sovLogservice(uint64(l)) + } + if m.Generation != 0 { + n += 1 + sovLogservice(uint64(m.Generation)) + } + l = len(m.QueryAddress) + if l > 0 { + n += 1 + l + sovLogservice(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + func (m *CNStoreHeartbeat) ProtoSize() (n int) { if m == nil { return 0 @@ -12598,6 +13207,26 @@ func (m *CNStoreHeartbeat) ProtoSize() (n int) { if m.ViewMetadataIngressReady { n += 3 } + if m.DDLVisibilityBarrierReady { + n += 3 + } + if m.DDLVisibilityDeployedProtocol != 0 { + n += 2 + sovLogservice(uint64(m.DDLVisibilityDeployedProtocol)) + } + if len(m.DDLVisibilityEpochCommitTargets) > 0 { + for _, e := range m.DDLVisibilityEpochCommitTargets { + l = e.ProtoSize() + n += 2 + l + sovLogservice(uint64(l)) + } + } + if m.DDLVisibilityActivationPrepared { + n += 3 + } + if m.DDLVisibilityActivationFenced { + n += 3 + } + l = m.DDLVisibilityFrontier.ProtoSize() + n += 2 + l + sovLogservice(uint64(l)) if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -12670,6 +13299,9 @@ func (m *LogStoreHeartbeat) ProtoSize() (n int) { if m.ViewMetadataAdmissionSupported { n += 2 } + if m.DDLVisibilityDeployedProtocolSupported { + n += 2 + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -13502,6 +14134,9 @@ func (m *CommandBatch) ProtoSize() (n int) { l = m.ViewMetadataAdmission.ProtoSize() n += 1 + l + sovLogservice(uint64(l)) } + if m.DDLVisibilityDeployedProtocol != 0 { + n += 1 + sovLogservice(uint64(m.DDLVisibilityDeployedProtocol)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -13662,6 +14297,20 @@ func (m *CNStoreInfo) ProtoSize() (n int) { if m.ViewMetadataIngressReady { n += 3 } + if m.DDLVisibilityBarrierReady { + n += 3 + } + if m.DDLVisibilityDeployedProtocol != 0 { + n += 2 + sovLogservice(uint64(m.DDLVisibilityDeployedProtocol)) + } + if m.DDLVisibilityActivationPrepared { + n += 3 + } + if m.DDLVisibilityActivationFenced { + n += 3 + } + l = m.DDLVisibilityFrontier.ProtoSize() + n += 2 + l + sovLogservice(uint64(l)) if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -13683,6 +14332,9 @@ func (m *CNState) ProtoSize() (n int) { n += mapEntrySize + 1 + sovLogservice(uint64(mapEntrySize)) } } + if m.DDLVisibilityDeployedProtocol != 0 { + n += 1 + sovLogservice(uint64(m.DDLVisibilityDeployedProtocol)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -13900,6 +14552,9 @@ func (m *ClusterDetails) ProtoSize() (n int) { l = m.ViewMetadataAdmission.ProtoSize() n += 1 + l + sovLogservice(uint64(l)) } + if m.DDLVisibilityDeployedProtocol != 0 { + n += 1 + sovLogservice(uint64(m.DDLVisibilityDeployedProtocol)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -14041,6 +14696,9 @@ func (m *LogStoreInfo) ProtoSize() (n int) { if m.ViewMetadataAdmissionSupported { n += 2 } + if m.DDLVisibilityDeployedProtocolSupported { + n += 2 + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -15182,6 +15840,138 @@ func (m *CNStore) Unmarshal(dAtA []byte) error { } } m.ViewMetadataAdmissionReady = bool(v != 0) + case 21: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DDLVisibilityBarrierReady", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.DDLVisibilityBarrierReady = bool(v != 0) + case 22: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ViewMetadataIngressReady", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ViewMetadataIngressReady = bool(v != 0) + case 23: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DDLVisibilityDeployedProtocol", wireType) + } + m.DDLVisibilityDeployedProtocol = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DDLVisibilityDeployedProtocol |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 24: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DDLVisibilityActivationPrepared", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.DDLVisibilityActivationPrepared = bool(v != 0) + case 25: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DDLVisibilityActivationFenced", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.DDLVisibilityActivationFenced = bool(v != 0) + case 26: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DDLVisibilityFrontier", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthLogservice + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthLogservice + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.DDLVisibilityFrontier.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipLogservice(dAtA[iNdEx:]) @@ -15809,6 +16599,26 @@ func (m *LogStore) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 8: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DDLVisibilityDeployedProtocolSupported", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.DDLVisibilityDeployedProtocolSupported = bool(v != 0) default: iNdEx = preIndex skippy, err := skipLogservice(dAtA[iNdEx:]) @@ -16426,6 +17236,140 @@ func (m *Resource) Unmarshal(dAtA []byte) error { } return nil } +func (m *DDLVisibilityActivationTarget) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DDLVisibilityActivationTarget: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DDLVisibilityActivationTarget: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ServiceID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLogservice + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLogservice + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ServiceID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Generation", wireType) + } + m.Generation = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Generation |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field QueryAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLogservice + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLogservice + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.QueryAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipLogservice(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthLogservice + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *CNStoreHeartbeat) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -16929,12 +17873,128 @@ func (m *CNStoreHeartbeat) Unmarshal(dAtA []byte) error { break } } - m.ViewMetadataAdmissionSupported = bool(v != 0) - case 19: + m.ViewMetadataAdmissionSupported = bool(v != 0) + case 19: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ViewMetadataAdmissionGeneration", wireType) + } + m.ViewMetadataAdmissionGeneration = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ViewMetadataAdmissionGeneration |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 20: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ViewMetadataObservedEpoch", wireType) + } + m.ViewMetadataObservedEpoch = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ViewMetadataObservedEpoch |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 21: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ViewMetadataCatalogFencedEpoch", wireType) + } + m.ViewMetadataCatalogFencedEpoch = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ViewMetadataCatalogFencedEpoch |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 22: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ViewMetadataRefreshSupported", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ViewMetadataRefreshSupported = bool(v != 0) + case 23: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ViewMetadataRevalidatedEpoch", wireType) + } + m.ViewMetadataRevalidatedEpoch = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ViewMetadataRevalidatedEpoch |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 24: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ViewMetadataIngressReady", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ViewMetadataIngressReady = bool(v != 0) + case 25: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field ViewMetadataAdmissionGeneration", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field DDLVisibilityBarrierReady", wireType) } - m.ViewMetadataAdmissionGeneration = 0 + var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowLogservice @@ -16944,16 +18004,17 @@ func (m *CNStoreHeartbeat) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.ViewMetadataAdmissionGeneration |= uint64(b&0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } } - case 20: + m.DDLVisibilityBarrierReady = bool(v != 0) + case 26: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field ViewMetadataObservedEpoch", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field DDLVisibilityDeployedProtocol", wireType) } - m.ViewMetadataObservedEpoch = 0 + m.DDLVisibilityDeployedProtocol = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowLogservice @@ -16963,16 +18024,16 @@ func (m *CNStoreHeartbeat) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.ViewMetadataObservedEpoch |= uint64(b&0x7F) << shift + m.DDLVisibilityDeployedProtocol |= int64(b&0x7F) << shift if b < 0x80 { break } } - case 21: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field ViewMetadataCatalogFencedEpoch", wireType) + case 27: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DDLVisibilityEpochCommitTargets", wireType) } - m.ViewMetadataCatalogFencedEpoch = 0 + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowLogservice @@ -16982,14 +18043,29 @@ func (m *CNStoreHeartbeat) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.ViewMetadataCatalogFencedEpoch |= uint64(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - case 22: + if msglen < 0 { + return ErrInvalidLengthLogservice + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthLogservice + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DDLVisibilityEpochCommitTargets = append(m.DDLVisibilityEpochCommitTargets, DDLVisibilityActivationTarget{}) + if err := m.DDLVisibilityEpochCommitTargets[len(m.DDLVisibilityEpochCommitTargets)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 28: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field ViewMetadataRefreshSupported", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field DDLVisibilityActivationPrepared", wireType) } var v int for shift := uint(0); ; shift += 7 { @@ -17006,12 +18082,12 @@ func (m *CNStoreHeartbeat) Unmarshal(dAtA []byte) error { break } } - m.ViewMetadataRefreshSupported = bool(v != 0) - case 23: + m.DDLVisibilityActivationPrepared = bool(v != 0) + case 29: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field ViewMetadataRevalidatedEpoch", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field DDLVisibilityActivationFenced", wireType) } - m.ViewMetadataRevalidatedEpoch = 0 + var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowLogservice @@ -17021,16 +18097,17 @@ func (m *CNStoreHeartbeat) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.ViewMetadataRevalidatedEpoch |= uint64(b&0x7F) << shift + v |= int(b&0x7F) << shift if b < 0x80 { break } } - case 24: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field ViewMetadataIngressReady", wireType) + m.DDLVisibilityActivationFenced = bool(v != 0) + case 30: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DDLVisibilityFrontier", wireType) } - var v int + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowLogservice @@ -17040,12 +18117,25 @@ func (m *CNStoreHeartbeat) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - v |= int(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - m.ViewMetadataIngressReady = bool(v != 0) + if msglen < 0 { + return ErrInvalidLengthLogservice + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthLogservice + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.DDLVisibilityFrontier.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipLogservice(dAtA[iNdEx:]) @@ -17522,6 +18612,26 @@ func (m *LogStoreHeartbeat) Unmarshal(dAtA []byte) error { } } m.ViewMetadataAdmissionSupported = bool(v != 0) + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DDLVisibilityDeployedProtocolSupported", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.DDLVisibilityDeployedProtocolSupported = bool(v != 0) default: iNdEx = preIndex skippy, err := skipLogservice(dAtA[iNdEx:]) @@ -22920,6 +24030,25 @@ func (m *CommandBatch) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DDLVisibilityDeployedProtocol", wireType) + } + m.DDLVisibilityDeployedProtocol = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DDLVisibilityDeployedProtocol |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipLogservice(dAtA[iNdEx:]) @@ -24022,6 +25151,118 @@ func (m *CNStoreInfo) Unmarshal(dAtA []byte) error { } } m.ViewMetadataIngressReady = bool(v != 0) + case 27: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DDLVisibilityBarrierReady", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.DDLVisibilityBarrierReady = bool(v != 0) + case 28: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DDLVisibilityDeployedProtocol", wireType) + } + m.DDLVisibilityDeployedProtocol = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DDLVisibilityDeployedProtocol |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 29: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DDLVisibilityActivationPrepared", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.DDLVisibilityActivationPrepared = bool(v != 0) + case 30: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DDLVisibilityActivationFenced", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.DDLVisibilityActivationFenced = bool(v != 0) + case 31: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DDLVisibilityFrontier", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthLogservice + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthLogservice + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.DDLVisibilityFrontier.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipLogservice(dAtA[iNdEx:]) @@ -24202,6 +25443,25 @@ func (m *CNState) Unmarshal(dAtA []byte) error { } m.Stores[mapkey] = *mapvalue iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DDLVisibilityDeployedProtocol", wireType) + } + m.DDLVisibilityDeployedProtocol = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DDLVisibilityDeployedProtocol |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipLogservice(dAtA[iNdEx:]) @@ -25655,6 +26915,25 @@ func (m *ClusterDetails) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 7: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DDLVisibilityDeployedProtocol", wireType) + } + m.DDLVisibilityDeployedProtocol = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DDLVisibilityDeployedProtocol |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipLogservice(dAtA[iNdEx:]) @@ -26693,6 +27972,26 @@ func (m *LogStoreInfo) Unmarshal(dAtA []byte) error { } } m.ViewMetadataAdmissionSupported = bool(v != 0) + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DDLVisibilityDeployedProtocolSupported", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLogservice + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.DDLVisibilityDeployedProtocolSupported = bool(v != 0) default: iNdEx = preIndex skippy, err := skipLogservice(dAtA[iNdEx:]) diff --git a/pkg/pb/logservice/logservice_test.go b/pkg/pb/logservice/logservice_test.go index ba264a35435b7..74537e2bc6181 100644 --- a/pkg/pb/logservice/logservice_test.go +++ b/pkg/pb/logservice/logservice_test.go @@ -35,23 +35,41 @@ func TestCNStateUpdate(t *testing.T) { state := CNState{Stores: map[string]CNStoreInfo{}} hb1 := CNStoreHeartbeat{ - UUID: "cn-a", - ServiceAddress: "addr-a", - Role: metadata.CNRole_AP, - CommandDeliveryAckSupported: true, + UUID: "cn-a", + ServiceAddress: "addr-a", + QueryAddress: "query-a", + Role: metadata.CNRole_AP, + CommandDeliveryAckSupported: true, + DDLVisibilityBarrierReady: true, + DDLVisibilityActivationPrepared: true, + DDLVisibilityActivationFenced: true, + DDLVisibilityDeployedProtocol: 38, + ViewMetadataAdmissionGeneration: 1, + ViewMetadataIngressReady: true, + DDLVisibilityEpochCommitTargets: []DDLVisibilityActivationTarget{{ + ServiceID: "cn-a", Generation: 1, QueryAddress: "query-a", + }}, } tick1 := uint64(100) state.Update(hb1, tick1) assert.Equal(t, state.Stores[hb1.UUID], CNStoreInfo{ - Tick: tick1, - ServiceAddress: hb1.ServiceAddress, - Role: metadata.CNRole_AP, - WorkState: metadata.WorkState_Working, - Labels: map[string]metadata.LabelList{}, - UpTime: state.Stores[hb1.UUID].UpTime, - CommandDeliveryAckSupported: true, + Tick: tick1, + ServiceAddress: hb1.ServiceAddress, + QueryAddress: hb1.QueryAddress, + Role: metadata.CNRole_AP, + WorkState: metadata.WorkState_Working, + Labels: map[string]metadata.LabelList{}, + UpTime: state.Stores[hb1.UUID].UpTime, + CommandDeliveryAckSupported: true, + DDLVisibilityBarrierReady: true, + DDLVisibilityActivationPrepared: true, + DDLVisibilityActivationFenced: true, + DDLVisibilityDeployedProtocol: 38, + ViewMetadataAdmissionGeneration: 1, + ViewMetadataIngressReady: true, }) + assert.Equal(t, int64(38), state.DDLVisibilityDeployedProtocol) hb2 := CNStoreHeartbeat{UUID: "cn-b", ServiceAddress: "addr-b", Role: metadata.CNRole_TP} tick2 := uint64(200) @@ -70,6 +88,8 @@ func TestCNStateUpdate(t *testing.T) { tick3 := uint64(300) state.Update(hb3, tick3) + assert.Equal(t, int64(38), state.DDLVisibilityDeployedProtocol, + "markerless/restarted heartbeats cannot lower the committed cluster epoch") assert.Equal(t, state.Stores[hb3.UUID], CNStoreInfo{ Tick: tick3, ServiceAddress: hb3.ServiceAddress, @@ -154,11 +174,12 @@ func TestLogStateUpdateStores(t *testing.T) { } hb1 := LogStoreHeartbeat{ - UUID: "log-a", - RaftAddress: "raft-a", - ServiceAddress: "addr-a", - GossipAddress: "gossip-a", - CommandDeliverySupported: true, + UUID: "log-a", + RaftAddress: "raft-a", + ServiceAddress: "addr-a", + GossipAddress: "gossip-a", + CommandDeliverySupported: true, + DDLVisibilityDeployedProtocolSupported: true, Replicas: []LogReplicaInfo{{ LogShardInfo: LogShardInfo{ ShardID: 1, @@ -173,12 +194,13 @@ func TestLogStateUpdateStores(t *testing.T) { tick1 := uint64(100) state.Update(hb1, tick1) assert.Equal(t, state.Stores[hb1.UUID], LogStoreInfo{ - Tick: tick1, - RaftAddress: hb1.RaftAddress, - ServiceAddress: hb1.ServiceAddress, - GossipAddress: hb1.GossipAddress, - Replicas: hb1.Replicas, - CommandDeliverySupported: true, + Tick: tick1, + RaftAddress: hb1.RaftAddress, + ServiceAddress: hb1.ServiceAddress, + GossipAddress: hb1.GossipAddress, + Replicas: hb1.Replicas, + CommandDeliverySupported: true, + DDLVisibilityDeployedProtocolSupported: true, }) hb2 := LogStoreHeartbeat{ @@ -518,6 +540,70 @@ func TestCNWorkStateUpdate(t *testing.T) { }) } +func TestCNStateAtomicallyRejectsEpochCommitAfterMarkerlessJoin(t *testing.T) { + state := NewCNState() + state.Update(CNStoreHeartbeat{ + UUID: "target-cn", QueryAddress: "target:6001", ViewMetadataAdmissionGeneration: 1, + DDLVisibilityBarrierReady: true, DDLVisibilityActivationPrepared: true, + DDLVisibilityActivationFenced: true, + }, 1) + commitTargets := []DDLVisibilityActivationTarget{{ + ServiceID: "target-cn", Generation: 1, QueryAddress: "target:6001", + }} + + // This join lands after the CN-side final scan but before the replicated + // epoch commit. The same RSM transition must see it and reject the stale set. + state.Update(CNStoreHeartbeat{ + UUID: "joining-cn", QueryAddress: "joining:6001", ViewMetadataAdmissionGeneration: 2, + DDLVisibilityBarrierReady: true, ViewMetadataIngressReady: true, + }, 2) + state.Update(CNStoreHeartbeat{ + UUID: "target-cn", QueryAddress: "target:6001", ViewMetadataAdmissionGeneration: 1, + DDLVisibilityBarrierReady: true, DDLVisibilityDeployedProtocol: 41, + DDLVisibilityEpochCommitTargets: commitTargets, + }, 3) + + assert.Equal(t, int64(0), state.DDLVisibilityDeployedProtocol) + assert.True(t, state.Stores["joining-cn"].ViewMetadataIngressReady) +} + +func TestCNStateAtomicallyRejectsEpochCommitFromUnfencedReplacement(t *testing.T) { + state := NewCNState() + state.Update(CNStoreHeartbeat{ + UUID: "target-cn", QueryAddress: "old:6001", ViewMetadataAdmissionGeneration: 1, + DDLVisibilityBarrierReady: true, DDLVisibilityActivationPrepared: true, + DDLVisibilityActivationFenced: true, + }, 1) + + // Replacement occurs after the old generation supplied Fenced. Even if the + // CN-side revalidation observes the new tuple, that tuple has no phase proof. + state.Update(CNStoreHeartbeat{ + UUID: "target-cn", QueryAddress: "new:6001", ViewMetadataAdmissionGeneration: 2, + DDLVisibilityBarrierReady: true, ViewMetadataIngressReady: true, + }, 2) + state.Update(CNStoreHeartbeat{ + UUID: "target-cn", QueryAddress: "new:6001", ViewMetadataAdmissionGeneration: 2, + DDLVisibilityBarrierReady: true, ViewMetadataIngressReady: true, + DDLVisibilityDeployedProtocol: 41, + DDLVisibilityEpochCommitTargets: []DDLVisibilityActivationTarget{{ + ServiceID: "target-cn", Generation: 2, QueryAddress: "new:6001", + }}, + }, 3) + + assert.Equal(t, int64(0), state.DDLVisibilityDeployedProtocol) + assert.True(t, state.Stores["target-cn"].ViewMetadataIngressReady) +} + +func TestCNStateRejectsMarkerlessIngressAfterCommittedDDLCut(t *testing.T) { + state := NewCNState() + state.DDLVisibilityDeployedProtocol = 41 + state.Update(CNStoreHeartbeat{ + UUID: "markerless-cn", ViewMetadataIngressReady: true, + }, 1) + assert.False(t, state.Stores["markerless-cn"].ViewMetadataIngressReady) + assert.Equal(t, int64(41), state.DDLVisibilityDeployedProtocol) +} + func TestCNStateLabelPatch(t *testing.T) { state := CNState{Stores: map[string]CNStoreInfo{}} diff --git a/pkg/pb/metadata/metadata.pb.go b/pkg/pb/metadata/metadata.pb.go index 6e597ae379440..5a92abfd7c78d 100644 --- a/pkg/pb/metadata/metadata.pb.go +++ b/pkg/pb/metadata/metadata.pb.go @@ -487,10 +487,13 @@ type CNStore struct { // UUID CNStore uuid id UUID string `protobuf:"bytes,1,opt,name=UUID,proto3" json:"UUID,omitempty"` // Role CN role - Role CNRole `protobuf:"varint,2,opt,name=Role,proto3,enum=metadata.CNRole" json:"Role,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Role CNRole `protobuf:"varint,2,opt,name=Role,proto3,enum=metadata.CNRole" json:"Role,omitempty"` + // DDLVisibilityDeployedProtocol is the durable activation level restored + // before this CN admits SQL after an ordinary restart. + DDLVisibilityDeployedProtocol int64 `protobuf:"varint,3,opt,name=DDLVisibilityDeployedProtocol,proto3" json:"DDLVisibilityDeployedProtocol,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *CNStore) Reset() { *m = CNStore{} } @@ -540,6 +543,13 @@ func (m *CNStore) GetRole() CNRole { return CNRole_TP } +func (m *CNStore) GetDDLVisibilityDeployedProtocol() int64 { + if m != nil { + return m.DDLVisibilityDeployedProtocol + } + return 0 +} + // CNService cn service metadata type CNService struct { // ServiceID service ID @@ -562,13 +572,19 @@ type CNService struct { // CPUTotal is cpu quota from logservicepb.CNStore CPUTotal uint64 `protobuf:"varint,11,opt,name=CPUTotal,proto3" json:"CPUTotal,omitempty"` // MemTotal is mem quota from logservicepb.CNStore - MemTotal uint64 `protobuf:"varint,12,opt,name=MemTotal,proto3" json:"MemTotal,omitempty"` - ViewMetadataAdmissionGeneration uint64 `protobuf:"varint,13,opt,name=ViewMetadataAdmissionGeneration,proto3" json:"ViewMetadataAdmissionGeneration,omitempty"` - ViewMetadataAdmissionReady bool `protobuf:"varint,14,opt,name=ViewMetadataAdmissionReady,proto3" json:"ViewMetadataAdmissionReady,omitempty"` - ViewMetadataObservedEpoch uint64 `protobuf:"varint,15,opt,name=ViewMetadataObservedEpoch,proto3" json:"ViewMetadataObservedEpoch,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + MemTotal uint64 `protobuf:"varint,12,opt,name=MemTotal,proto3" json:"MemTotal,omitempty"` + ViewMetadataAdmissionGeneration uint64 `protobuf:"varint,13,opt,name=ViewMetadataAdmissionGeneration,proto3" json:"ViewMetadataAdmissionGeneration,omitempty"` + ViewMetadataAdmissionReady bool `protobuf:"varint,14,opt,name=ViewMetadataAdmissionReady,proto3" json:"ViewMetadataAdmissionReady,omitempty"` + ViewMetadataObservedEpoch uint64 `protobuf:"varint,15,opt,name=ViewMetadataObservedEpoch,proto3" json:"ViewMetadataObservedEpoch,omitempty"` + // DDLVisibilityBarrierReady is published after QueryService is listening. + // A CN is not publicly routable until its startup DDL frontier fence completes. + DDLVisibilityBarrierReady bool `protobuf:"varint,16,opt,name=DDLVisibilityBarrierReady,proto3" json:"DDLVisibilityBarrierReady,omitempty"` + // ViewMetadataIngressReady is retained in raw control-plane inventory so + // protocol activation can verify withdrawal/publication without routing it. + ViewMetadataIngressReady bool `protobuf:"varint,17,opt,name=ViewMetadataIngressReady,proto3" json:"ViewMetadataIngressReady,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *CNService) Reset() { *m = CNService{} } @@ -702,6 +718,20 @@ func (m *CNService) GetViewMetadataObservedEpoch() uint64 { return 0 } +func (m *CNService) GetDDLVisibilityBarrierReady() bool { + if m != nil { + return m.DDLVisibilityBarrierReady + } + return false +} + +func (m *CNService) GetViewMetadataIngressReady() bool { + if m != nil { + return m.ViewMetadataIngressReady + } + return false +} + // TNService tn service metadata type TNService struct { // ServiceID service ID @@ -892,64 +922,68 @@ func init() { func init() { proto.RegisterFile("metadata.proto", fileDescriptor_56d9f74966f40d04) } var fileDescriptor_56d9f74966f40d04 = []byte{ - // 905 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xc1, 0x6e, 0xdb, 0x46, - 0x10, 0x35, 0x25, 0x5a, 0x22, 0x47, 0x8e, 0xca, 0x6c, 0xd0, 0x94, 0x75, 0x03, 0xd9, 0x60, 0x7b, - 0x70, 0x8d, 0x56, 0x4a, 0xdc, 0xa2, 0x28, 0x8a, 0xb6, 0x88, 0x2c, 0x27, 0x8e, 0x03, 0x85, 0x52, - 0x28, 0xca, 0x6d, 0x7a, 0x29, 0x28, 0x71, 0x43, 0x2f, 0x24, 0xed, 0x12, 0x2b, 0xca, 0x89, 0x7e, - 0xa1, 0xc7, 0x5e, 0x7b, 0xe9, 0xe7, 0xe4, 0xe8, 0x2f, 0x08, 0x5a, 0xf7, 0xd8, 0x9f, 0x08, 0xb8, - 0x24, 0x45, 0x9a, 0x96, 0x14, 0x5f, 0x72, 0xe2, 0xce, 0xbc, 0xb7, 0xb3, 0xf3, 0x66, 0x67, 0x16, - 0x84, 0xea, 0x04, 0x07, 0x8e, 0xeb, 0x04, 0x4e, 0xdd, 0xe7, 0x2c, 0x60, 0x48, 0x49, 0xec, 0xed, - 0xaf, 0x3d, 0x12, 0x9c, 0xcd, 0x06, 0xf5, 0x21, 0x9b, 0x34, 0x3c, 0xe6, 0xb1, 0x86, 0x20, 0x0c, - 0x66, 0x2f, 0x85, 0x25, 0x0c, 0xb1, 0x8a, 0x36, 0x1a, 0x27, 0x70, 0xcb, 0x36, 0x7b, 0x67, 0x0e, - 0x77, 0x2d, 0x3c, 0x64, 0xdc, 0x45, 0x3a, 0x94, 0x85, 0x79, 0x72, 0xa4, 0x4b, 0xbb, 0xd2, 0x9e, - 0x6c, 0x25, 0x26, 0xaa, 0x01, 0xb4, 0x99, 0x97, 0x80, 0x05, 0x01, 0x66, 0x3c, 0xc6, 0x1f, 0x12, - 0x94, 0xe3, 0x58, 0xe8, 0x38, 0x17, 0x56, 0xc4, 0xaa, 0x1c, 0x7c, 0x52, 0x5f, 0xe4, 0x7d, 0x05, - 0x3e, 0x54, 0xde, 0xbc, 0xdd, 0xd9, 0xb8, 0x78, 0xbb, 0x23, 0x59, 0xb9, 0x74, 0xee, 0x81, 0x6a, - 0x61, 0x7f, 0x4c, 0x86, 0xce, 0xe2, 0xcc, 0xd4, 0x11, 0x26, 0xdb, 0x74, 0x5d, 0x8e, 0xa7, 0x53, - 0xbd, 0xb8, 0x2b, 0xed, 0xa9, 0x56, 0x62, 0x1a, 0xa7, 0x50, 0x4d, 0x52, 0x7b, 0xaf, 0xb0, 0x7d, - 0xd0, 0xcc, 0xd9, 0x64, 0x80, 0x79, 0xe7, 0x65, 0x1c, 0x7a, 0x1a, 0x1f, 0x75, 0xcd, 0x6f, 0xfc, - 0x29, 0x81, 0x92, 0x04, 0x46, 0x4f, 0xf3, 0x87, 0xc4, 0x32, 0xf5, 0x54, 0xe6, 0x55, 0x3c, 0xa3, - 0x33, 0x9f, 0xde, 0x7a, 0xa1, 0xf7, 0x40, 0x35, 0x19, 0x3d, 0x65, 0x01, 0xa1, 0x9e, 0x90, 0xaa, - 0x58, 0xa9, 0xc3, 0x30, 0x45, 0xe1, 0x03, 0xc6, 0x31, 0x42, 0x20, 0xf7, 0xfb, 0xb1, 0x44, 0xd5, - 0x12, 0x6b, 0xd4, 0x80, 0x92, 0x38, 0x29, 0x54, 0x55, 0xdc, 0xab, 0x1c, 0xdc, 0xbe, 0x76, 0x0b, - 0x87, 0x72, 0x98, 0x97, 0x15, 0xd3, 0x8c, 0x6e, 0xa4, 0x71, 0x65, 0xc0, 0xfb, 0xb9, 0x80, 0xe8, - 0xba, 0xde, 0x5c, 0xc4, 0x16, 0x94, 0x5b, 0x6b, 0x32, 0xfc, 0x02, 0x64, 0x8b, 0x8d, 0xb1, 0xd0, - 0x5d, 0x3d, 0xd0, 0xd2, 0x70, 0x2d, 0x33, 0xf4, 0x5b, 0x02, 0x35, 0xfe, 0xdf, 0x04, 0xb5, 0x65, - 0xf6, 0x30, 0x3f, 0x27, 0x43, 0x1c, 0x96, 0x24, 0x5e, 0x2e, 0x82, 0xa5, 0x0e, 0x54, 0x07, 0xd4, - 0x66, 0xc3, 0x51, 0xec, 0x48, 0x9a, 0xa4, 0x20, 0x68, 0x4b, 0x10, 0xf4, 0x1d, 0xdc, 0xed, 0x12, - 0x1f, 0x8f, 0x09, 0xc5, 0xb9, 0x3d, 0x51, 0x63, 0xad, 0x40, 0xc3, 0xa1, 0xe8, 0x3d, 0x6f, 0x27, - 0x5c, 0x59, 0x70, 0x33, 0x1e, 0xf4, 0x13, 0x94, 0xda, 0xce, 0x00, 0x8f, 0xa7, 0x7a, 0x49, 0x94, - 0x6a, 0x27, 0xab, 0x2d, 0x8e, 0x55, 0x8f, 0x18, 0x8f, 0x68, 0xc0, 0xe7, 0x49, 0xdd, 0x22, 0x17, - 0x7a, 0x00, 0xea, 0x2f, 0x8c, 0x8f, 0x7a, 0x81, 0x13, 0x60, 0xbd, 0x2c, 0xaa, 0x73, 0x27, 0x8d, - 0xb0, 0x80, 0xac, 0x94, 0x85, 0x0c, 0xd8, 0x7a, 0x3e, 0xc3, 0x7c, 0x9e, 0xe4, 0xa4, 0x88, 0x9c, - 0xae, 0xf8, 0xd0, 0x7d, 0xb8, 0x23, 0x2e, 0x26, 0x27, 0x55, 0x15, 0xd4, 0x65, 0x10, 0xda, 0x06, - 0xa5, 0xc5, 0x26, 0x13, 0x12, 0x9c, 0x1c, 0xe9, 0x20, 0x68, 0x0b, 0x5b, 0x60, 0xdd, 0xbe, 0xcd, - 0x02, 0x67, 0xac, 0x57, 0x44, 0xe7, 0x2e, 0xec, 0x10, 0x7b, 0x86, 0x27, 0x11, 0xb6, 0x15, 0x61, - 0x89, 0x8d, 0x9e, 0xc0, 0xce, 0x29, 0xc1, 0xaf, 0x9e, 0xc5, 0x72, 0x9a, 0xee, 0x84, 0x4c, 0xa7, - 0x84, 0xd1, 0x63, 0x4c, 0x31, 0x77, 0x02, 0xc2, 0xa8, 0x7e, 0x4b, 0x6c, 0x79, 0x1f, 0x0d, 0xfd, - 0x0c, 0xdb, 0x4b, 0x29, 0x16, 0x76, 0xdc, 0xb9, 0x5e, 0x15, 0xf3, 0xb2, 0x86, 0x81, 0x7e, 0x84, - 0x4f, 0xb3, 0x68, 0x67, 0x30, 0xc5, 0xfc, 0x1c, 0xbb, 0x8f, 0x7c, 0x36, 0x3c, 0xd3, 0x3f, 0x12, - 0x39, 0xac, 0x26, 0x6c, 0x9b, 0x50, 0xc9, 0xdc, 0x20, 0xd2, 0xa0, 0x38, 0xc2, 0xf3, 0xb8, 0x25, - 0xc3, 0x25, 0xfa, 0x12, 0x36, 0xcf, 0x9d, 0xf1, 0x2c, 0xea, 0xef, 0x4a, 0xf6, 0x06, 0xc5, 0xbe, - 0x36, 0x99, 0x06, 0x56, 0xc4, 0xf8, 0xa1, 0xf0, 0xbd, 0xf4, 0x54, 0x56, 0x36, 0xb5, 0x92, 0xf1, - 0x97, 0x0c, 0xaa, 0x7d, 0xc3, 0x6e, 0xff, 0x0a, 0x6e, 0xdb, 0xaf, 0xe9, 0xd2, 0x66, 0xbf, 0x0e, - 0xa0, 0x6f, 0xe1, 0xe3, 0x36, 0xf3, 0x6c, 0x87, 0x8c, 0x97, 0xb6, 0xfa, 0x72, 0x70, 0xc5, 0x44, - 0xc9, 0x2b, 0x27, 0x2a, 0x7d, 0x75, 0x4a, 0x37, 0x7a, 0x75, 0x32, 0xa3, 0x52, 0xce, 0x8f, 0x8a, - 0x7d, 0x83, 0x51, 0xf9, 0x30, 0x7d, 0xff, 0x10, 0x3e, 0x6b, 0xce, 0x02, 0x76, 0x42, 0x87, 0x5c, - 0x5c, 0xf6, 0x63, 0x4c, 0x87, 0xb8, 0x37, 0xf3, 0x7d, 0xc6, 0x03, 0xec, 0x8a, 0x51, 0x50, 0xac, - 0x75, 0x94, 0x0f, 0xd4, 0x1d, 0x9f, 0x83, 0xba, 0x40, 0xd1, 0xdd, 0x45, 0xe5, 0xa4, 0xdd, 0xe2, - 0x9e, 0x9a, 0x94, 0x64, 0xbf, 0x09, 0x95, 0x58, 0x8e, 0x3d, 0xf7, 0x31, 0x2a, 0x41, 0xa1, 0x65, - 0x6a, 0x1b, 0xe1, 0xd7, 0x36, 0x35, 0x09, 0x95, 0xa1, 0xd8, 0xee, 0x1c, 0x6b, 0x05, 0xa4, 0xc2, - 0x66, 0xd7, 0xea, 0xfc, 0xfa, 0x42, 0x2b, 0xa2, 0x2a, 0x40, 0xf7, 0x85, 0xfd, 0xa4, 0x63, 0xfe, - 0xde, 0x3f, 0x7a, 0xac, 0xc9, 0xfb, 0x3a, 0x94, 0xa2, 0x37, 0x58, 0xec, 0xea, 0x46, 0xbb, 0x9b, - 0x5d, 0x4d, 0xda, 0x7f, 0x98, 0x79, 0x9a, 0x50, 0x05, 0xca, 0x7d, 0x3a, 0xa2, 0xec, 0x15, 0xd5, - 0x36, 0x42, 0x23, 0x44, 0x08, 0xf5, 0x34, 0x09, 0x6d, 0x81, 0x72, 0xc4, 0x1d, 0x42, 0x43, 0xab, - 0x10, 0x42, 0xc2, 0xc2, 0xae, 0x56, 0x3c, 0x3c, 0xbe, 0xf8, 0xb7, 0x26, 0xbd, 0xb9, 0xac, 0x49, - 0x17, 0x97, 0x35, 0xe9, 0x9f, 0xcb, 0xda, 0xc6, 0xdf, 0xff, 0xd5, 0xa4, 0xdf, 0x1e, 0x64, 0x7e, - 0x60, 0x26, 0x4e, 0xc0, 0xc9, 0x6b, 0xc6, 0x89, 0x47, 0x68, 0x62, 0x50, 0xdc, 0xf0, 0x47, 0x5e, - 0xc3, 0x1f, 0x34, 0x92, 0x3a, 0x0d, 0x4a, 0xe2, 0x5f, 0xe6, 0x9b, 0x77, 0x01, 0x00, 0x00, 0xff, - 0xff, 0xe7, 0xa7, 0x5a, 0x4c, 0x16, 0x09, 0x00, 0x00, + // 968 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0x51, 0x73, 0xdb, 0x44, + 0x10, 0x8e, 0x6c, 0xc7, 0xb6, 0xd6, 0x69, 0x50, 0xae, 0x43, 0x11, 0xa1, 0x38, 0x19, 0xc3, 0x43, + 0xc8, 0x80, 0xdd, 0x06, 0x86, 0x61, 0x3a, 0xc0, 0xd4, 0xb1, 0xdb, 0xd4, 0x1d, 0x57, 0x76, 0x65, + 0x39, 0x50, 0x5e, 0x18, 0x59, 0xba, 0x2a, 0x37, 0x96, 0xef, 0x34, 0x27, 0x39, 0xad, 0x7f, 0x02, + 0x3c, 0xf2, 0xca, 0x0b, 0x3f, 0xa7, 0x8f, 0xf9, 0x05, 0x1d, 0x08, 0xc3, 0xff, 0x60, 0x74, 0x92, + 0x6c, 0x45, 0xb1, 0xdd, 0xbc, 0xf4, 0xc9, 0xb7, 0xfb, 0x7d, 0xbb, 0xb7, 0xbb, 0xda, 0x4f, 0x16, + 0x6c, 0x4f, 0x70, 0x60, 0xda, 0x66, 0x60, 0xd6, 0x3d, 0xce, 0x02, 0x86, 0xca, 0x89, 0xbd, 0xfb, + 0x95, 0x43, 0x82, 0xb3, 0xe9, 0xa8, 0x6e, 0xb1, 0x49, 0xc3, 0x61, 0x0e, 0x6b, 0x08, 0xc2, 0x68, + 0xfa, 0x52, 0x58, 0xc2, 0x10, 0xa7, 0x28, 0xb0, 0xd6, 0x81, 0x5b, 0x86, 0x36, 0x38, 0x33, 0xb9, + 0xad, 0x63, 0x8b, 0x71, 0x1b, 0xa9, 0x50, 0x12, 0x66, 0xa7, 0xad, 0x4a, 0xfb, 0xd2, 0x41, 0x41, + 0x4f, 0x4c, 0x54, 0x05, 0xe8, 0x32, 0x27, 0x01, 0x73, 0x02, 0x4c, 0x79, 0x6a, 0xbf, 0x4b, 0x50, + 0x8a, 0x73, 0xa1, 0x93, 0x4c, 0x5a, 0x91, 0xab, 0x72, 0xf4, 0x51, 0x7d, 0x5e, 0xf7, 0x15, 0xf8, + 0xb8, 0xfc, 0xe6, 0xed, 0xde, 0xc6, 0xc5, 0xdb, 0x3d, 0x49, 0xcf, 0x94, 0x73, 0x17, 0x64, 0x1d, + 0x7b, 0x2e, 0xb1, 0xcc, 0xf9, 0x9d, 0x0b, 0x47, 0x58, 0x6c, 0xd3, 0xb6, 0x39, 0xf6, 0x7d, 0x35, + 0xbf, 0x2f, 0x1d, 0xc8, 0x7a, 0x62, 0xd6, 0x4e, 0x61, 0x3b, 0x29, 0xed, 0x9d, 0x8d, 0x1d, 0x82, + 0xa2, 0x4d, 0x27, 0x23, 0xcc, 0x7b, 0x2f, 0xe3, 0xd4, 0x7e, 0x7c, 0xd5, 0x35, 0x7f, 0xed, 0x0f, + 0x09, 0xca, 0x49, 0x62, 0xf4, 0x34, 0x7b, 0x49, 0xdc, 0xa6, 0xba, 0x68, 0xf3, 0x2a, 0x9e, 0xea, + 0x33, 0x5b, 0xde, 0xfa, 0x46, 0xef, 0x82, 0xac, 0x31, 0x7a, 0xca, 0x02, 0x42, 0x1d, 0xd1, 0x6a, + 0x59, 0x5f, 0x38, 0x6a, 0x9a, 0x18, 0x7c, 0xc0, 0x38, 0x46, 0x08, 0x0a, 0xc3, 0x61, 0xdc, 0xa2, + 0xac, 0x8b, 0x33, 0x6a, 0x40, 0x51, 0xdc, 0x14, 0x76, 0x95, 0x3f, 0xa8, 0x1c, 0xed, 0x5c, 0x7b, + 0x0a, 0xc7, 0x85, 0xb0, 0x2e, 0x3d, 0xa6, 0xd5, 0xfa, 0x51, 0x8f, 0x2b, 0x13, 0xde, 0xcb, 0x24, + 0x44, 0xd7, 0xfb, 0xcd, 0x64, 0xfc, 0x4d, 0x82, 0x52, 0x6b, 0x4d, 0x89, 0x9f, 0x43, 0x41, 0x67, + 0x2e, 0x16, 0x8d, 0x6f, 0x1f, 0x29, 0x8b, 0x7c, 0x2d, 0x2d, 0xf4, 0xeb, 0x02, 0x45, 0x6d, 0xf8, + 0xb4, 0xdd, 0xee, 0x9e, 0x12, 0x9f, 0x8c, 0x88, 0x4b, 0x82, 0x59, 0x1b, 0x7b, 0x2e, 0x9b, 0x61, + 0xbb, 0x1f, 0xae, 0xb2, 0xc5, 0x5c, 0x31, 0x99, 0xbc, 0xbe, 0x9e, 0x54, 0xfb, 0xaf, 0x08, 0x72, + 0x4b, 0x1b, 0x60, 0x7e, 0x4e, 0x2c, 0x1c, 0x4e, 0x36, 0x3e, 0xce, 0x4b, 0x5a, 0x38, 0x50, 0x1d, + 0x50, 0x97, 0x59, 0xe3, 0xd8, 0x91, 0xec, 0x5a, 0x4e, 0xd0, 0x96, 0x20, 0xe8, 0x5b, 0xb8, 0xd3, + 0x27, 0x1e, 0x76, 0x09, 0xc5, 0x99, 0x98, 0x68, 0x3f, 0x57, 0xa0, 0xa1, 0xb6, 0x06, 0xcf, 0xbb, + 0x09, 0xb7, 0x20, 0xb8, 0x29, 0x0f, 0xfa, 0x01, 0x8a, 0x5d, 0x73, 0x84, 0x5d, 0x5f, 0x2d, 0x8a, + 0x89, 0xef, 0xa5, 0x27, 0x14, 0xe7, 0xaa, 0x47, 0x8c, 0x47, 0x34, 0xe0, 0xb3, 0x64, 0xfc, 0x91, + 0x0b, 0xdd, 0x07, 0xf9, 0x27, 0xc6, 0xc7, 0x83, 0xc0, 0x0c, 0xb0, 0x5a, 0x12, 0x33, 0xbe, 0xbd, + 0xc8, 0x30, 0x87, 0xf4, 0x05, 0x0b, 0xd5, 0x60, 0xeb, 0xf9, 0x14, 0xf3, 0x59, 0x52, 0x53, 0x59, + 0xd4, 0x74, 0xc5, 0x87, 0xee, 0xc1, 0x6d, 0xf1, 0x7c, 0x33, 0xad, 0xca, 0x82, 0xba, 0x0c, 0x42, + 0xbb, 0x50, 0x6e, 0xb1, 0xc9, 0x84, 0x04, 0x9d, 0xb6, 0x0a, 0x82, 0x36, 0xb7, 0x05, 0xd6, 0x1f, + 0x1a, 0x2c, 0x30, 0x5d, 0xb5, 0x22, 0x04, 0x30, 0xb7, 0x43, 0xec, 0x19, 0x9e, 0x44, 0xd8, 0x56, + 0x84, 0x25, 0x36, 0x7a, 0x02, 0x7b, 0xa7, 0x04, 0xbf, 0x7a, 0x16, 0xb7, 0xd3, 0xb4, 0x27, 0xc4, + 0xf7, 0x09, 0xa3, 0x27, 0x98, 0x62, 0x6e, 0x06, 0x84, 0x51, 0xf5, 0x96, 0x08, 0x79, 0x17, 0x0d, + 0xfd, 0x08, 0xbb, 0x4b, 0x29, 0x3a, 0x36, 0xed, 0x99, 0xba, 0x2d, 0x64, 0xb7, 0x86, 0x81, 0xbe, + 0x87, 0x8f, 0xd3, 0x68, 0x6f, 0xe4, 0x63, 0x7e, 0x8e, 0xed, 0x47, 0x1e, 0xb3, 0xce, 0xd4, 0x0f, + 0x44, 0x0d, 0xab, 0x09, 0x61, 0xf4, 0x95, 0xc5, 0x3d, 0x36, 0x39, 0x27, 0x98, 0x47, 0x97, 0x2b, + 0xe2, 0xf2, 0xd5, 0x04, 0xf4, 0x00, 0xd4, 0x74, 0xea, 0x0e, 0x75, 0xc2, 0x81, 0x47, 0xc1, 0x3b, + 0x22, 0x78, 0x25, 0xbe, 0xab, 0x41, 0x25, 0xb5, 0x3b, 0x48, 0x81, 0xfc, 0x18, 0xcf, 0x62, 0x31, + 0x84, 0x47, 0xf4, 0x05, 0x6c, 0x9e, 0x9b, 0xee, 0x34, 0xd2, 0x67, 0x25, 0xbd, 0x3b, 0x22, 0xae, + 0x4b, 0xfc, 0x40, 0x8f, 0x18, 0x0f, 0x72, 0xdf, 0x49, 0x4f, 0x0b, 0xe5, 0x4d, 0xa5, 0x58, 0xfb, + 0xb3, 0x00, 0xb2, 0x71, 0x43, 0x9d, 0x7d, 0x09, 0x3b, 0xc6, 0x6b, 0xba, 0x54, 0x66, 0xd7, 0x01, + 0xf4, 0x0d, 0x7c, 0xd8, 0x65, 0x8e, 0x61, 0x12, 0x77, 0xa9, 0xc8, 0x96, 0x83, 0x2b, 0xb4, 0x5c, + 0x58, 0xa9, 0xe5, 0xc5, 0x6b, 0xb3, 0x78, 0xa3, 0xd7, 0x66, 0x4a, 0xa4, 0xa5, 0xac, 0x48, 0x8d, + 0x1b, 0x88, 0xf4, 0xfd, 0x28, 0xee, 0x21, 0x7c, 0xd2, 0x9c, 0x06, 0xac, 0x43, 0x2d, 0x2e, 0xd6, + 0xec, 0x31, 0xa6, 0x16, 0x1e, 0x4c, 0x3d, 0x8f, 0xf1, 0x00, 0xdb, 0x42, 0x84, 0x65, 0x7d, 0x1d, + 0xe5, 0x3d, 0x6d, 0xc7, 0x67, 0x20, 0xcf, 0x51, 0x74, 0x67, 0x3e, 0x39, 0x69, 0x3f, 0x7f, 0x20, + 0x27, 0x23, 0x39, 0x6c, 0x42, 0x25, 0x6e, 0xc7, 0x98, 0x79, 0x18, 0x15, 0x21, 0xd7, 0xd2, 0x94, + 0x8d, 0xf0, 0xd7, 0xd0, 0x14, 0x09, 0x95, 0x20, 0xdf, 0xed, 0x9d, 0x28, 0x39, 0x24, 0xc3, 0x66, + 0x5f, 0xef, 0xfd, 0xfc, 0x42, 0xc9, 0xa3, 0x6d, 0x80, 0xfe, 0x0b, 0xe3, 0x49, 0x4f, 0xfb, 0x75, + 0xd8, 0x7e, 0xac, 0x14, 0x0e, 0x55, 0x28, 0x46, 0xff, 0x21, 0x22, 0xaa, 0x1f, 0x45, 0x37, 0xfb, + 0x8a, 0x74, 0xf8, 0x30, 0xf5, 0x52, 0x44, 0x15, 0x28, 0x0d, 0xe9, 0x98, 0xb2, 0x57, 0x54, 0xd9, + 0x08, 0x8d, 0x10, 0x21, 0xd4, 0x51, 0x24, 0xb4, 0x05, 0xe5, 0x36, 0x37, 0x09, 0x0d, 0xad, 0x5c, + 0x08, 0x09, 0x0b, 0xdb, 0x4a, 0xfe, 0xf8, 0xe4, 0xe2, 0x9f, 0xaa, 0xf4, 0xe6, 0xb2, 0x2a, 0x5d, + 0x5c, 0x56, 0xa5, 0xbf, 0x2f, 0xab, 0x1b, 0x7f, 0xfd, 0x5b, 0x95, 0x7e, 0xb9, 0x9f, 0xfa, 0x02, + 0x9b, 0x98, 0x01, 0x27, 0xaf, 0x19, 0x27, 0x0e, 0xa1, 0x89, 0x41, 0x71, 0xc3, 0x1b, 0x3b, 0x0d, + 0x6f, 0xd4, 0x48, 0xe6, 0x34, 0x2a, 0x8a, 0x8f, 0xb1, 0xaf, 0xff, 0x0f, 0x00, 0x00, 0xff, 0xff, + 0xae, 0x08, 0xa0, 0x2d, 0xd7, 0x09, 0x00, 0x00, } func (m *TNShardRecord) Marshal() (dAtA []byte, err error) { @@ -1247,6 +1281,11 @@ func (m *CNStore) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.DDLVisibilityDeployedProtocol != 0 { + i = encodeVarintMetadata(dAtA, i, uint64(m.DDLVisibilityDeployedProtocol)) + i-- + dAtA[i] = 0x18 + } if m.Role != 0 { i = encodeVarintMetadata(dAtA, i, uint64(m.Role)) i-- @@ -1286,6 +1325,30 @@ func (m *CNService) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.ViewMetadataIngressReady { + i-- + if m.ViewMetadataIngressReady { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x88 + } + if m.DDLVisibilityBarrierReady { + i-- + if m.DDLVisibilityBarrierReady { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x80 + } if m.ViewMetadataObservedEpoch != 0 { i = encodeVarintMetadata(dAtA, i, uint64(m.ViewMetadataObservedEpoch)) i-- @@ -1695,6 +1758,9 @@ func (m *CNStore) ProtoSize() (n int) { if m.Role != 0 { n += 1 + sovMetadata(uint64(m.Role)) } + if m.DDLVisibilityDeployedProtocol != 0 { + n += 1 + sovMetadata(uint64(m.DDLVisibilityDeployedProtocol)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -1762,6 +1828,12 @@ func (m *CNService) ProtoSize() (n int) { if m.ViewMetadataObservedEpoch != 0 { n += 1 + sovMetadata(uint64(m.ViewMetadataObservedEpoch)) } + if m.DDLVisibilityBarrierReady { + n += 3 + } + if m.ViewMetadataIngressReady { + n += 3 + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -2596,6 +2668,25 @@ func (m *CNStore) Unmarshal(dAtA []byte) error { break } } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DDLVisibilityDeployedProtocol", wireType) + } + m.DDLVisibilityDeployedProtocol = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetadata + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DDLVisibilityDeployedProtocol |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipMetadata(dAtA[iNdEx:]) @@ -3115,6 +3206,46 @@ func (m *CNService) Unmarshal(dAtA []byte) error { break } } + case 16: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DDLVisibilityBarrierReady", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetadata + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.DDLVisibilityBarrierReady = bool(v != 0) + case 17: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ViewMetadataIngressReady", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMetadata + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.ViewMetadataIngressReady = bool(v != 0) default: iNdEx = preIndex skippy, err := skipMetadata(dAtA[iNdEx:]) diff --git a/pkg/pb/query/query.pb.go b/pkg/pb/query/query.pb.go index 89b5c0c082ad4..332f1b65f4163 100644 --- a/pkg/pb/query/query.pb.go +++ b/pkg/pb/query/query.pb.go @@ -109,6 +109,9 @@ const ( CmdMethod_IcebergCacheInvalidate CmdMethod = 38 // MongoDBClientRetire drains obsolete MongoDB driver clients after catalog DDL. CmdMethod_MongoDBClientRetire CmdMethod = 39 + // SyncCommitV2 waits for local logtail application with the request context. + // Unlike legacy SyncCommit, cancellation is returned to the sender. + CmdMethod_SyncCommitV2 CmdMethod = 40 ) var CmdMethod_name = map[int32]string{ @@ -152,6 +155,7 @@ var CmdMethod_name = map[int32]string{ 37: "ISCPDrainConsumer", 38: "IcebergCacheInvalidate", 39: "MongoDBClientRetire", + 40: "SyncCommitV2", } var CmdMethod_value = map[string]int32{ @@ -195,6 +199,7 @@ var CmdMethod_value = map[string]int32{ "ISCPDrainConsumer": 37, "IcebergCacheInvalidate": 38, "MongoDBClientRetire": 39, + "SyncCommitV2": 40, } func (x CmdMethod) String() string { @@ -658,6 +663,12 @@ var xxx_messageInfo_GetProtocolVersionRequest proto.InternalMessageInfo type GetProtocolVersionResponse struct { // ProtocolVersion is the version of the protocol Version int64 `protobuf:"varint,1,opt,name=Version,proto3" json:"Version,omitempty"` + // DDLVisibilityActivationPrepared means this CN has blocked and drained DDL + // producers before exposing the v36 sender capability. + DDLVisibilityActivationPrepared bool `protobuf:"varint,2,opt,name=DDLVisibilityActivationPrepared,proto3" json:"DDLVisibilityActivationPrepared,omitempty"` + // DDLVisibilityActivationFenced means this CN applied the converged peer + // frontier while every participant's DDL producers were blocked. + DDLVisibilityActivationFenced bool `protobuf:"varint,3,opt,name=DDLVisibilityActivationFenced,proto3" json:"DDLVisibilityActivationFenced,omitempty"` } func (m *GetProtocolVersionResponse) Reset() { *m = GetProtocolVersionResponse{} } @@ -700,9 +711,29 @@ func (m *GetProtocolVersionResponse) GetVersion() int64 { return 0 } +func (m *GetProtocolVersionResponse) GetDDLVisibilityActivationPrepared() bool { + if m != nil { + return m.DDLVisibilityActivationPrepared + } + return false +} + +func (m *GetProtocolVersionResponse) GetDDLVisibilityActivationFenced() bool { + if m != nil { + return m.DDLVisibilityActivationFenced + } + return false +} + type SetProtocolVersionRequest struct { // ProtocolVersion is the version of the protocol Version int64 `protobuf:"varint,1,opt,name=Version,proto3" json:"Version,omitempty"` + // DDLVisibilityActivationTargets is the complete CN set participating in a + // coordinated live activation. It is empty for ordinary version updates. + DDLVisibilityActivationTargets []string `protobuf:"bytes,2,rep,name=DDLVisibilityActivationTargets,proto3" json:"DDLVisibilityActivationTargets,omitempty"` + // The authoritative raw-inventory identity selected by the control plane. + DDLVisibilityTargetGeneration uint64 `protobuf:"varint,3,opt,name=DDLVisibilityTargetGeneration,proto3" json:"DDLVisibilityTargetGeneration,omitempty"` + DDLVisibilityTargetQueryAddress string `protobuf:"bytes,4,opt,name=DDLVisibilityTargetQueryAddress,proto3" json:"DDLVisibilityTargetQueryAddress,omitempty"` } func (m *SetProtocolVersionRequest) Reset() { *m = SetProtocolVersionRequest{} } @@ -745,6 +776,27 @@ func (m *SetProtocolVersionRequest) GetVersion() int64 { return 0 } +func (m *SetProtocolVersionRequest) GetDDLVisibilityActivationTargets() []string { + if m != nil { + return m.DDLVisibilityActivationTargets + } + return nil +} + +func (m *SetProtocolVersionRequest) GetDDLVisibilityTargetGeneration() uint64 { + if m != nil { + return m.DDLVisibilityTargetGeneration + } + return 0 +} + +func (m *SetProtocolVersionRequest) GetDDLVisibilityTargetQueryAddress() string { + if m != nil { + return m.DDLVisibilityTargetQueryAddress + } + return "" +} + type SetProtocolVersionResponse struct { // ProtocolVersion is the version of the protocol Version int64 `protobuf:"varint,1,opt,name=Version,proto3" json:"Version,omitempty"` @@ -5964,286 +6016,292 @@ func init() { func init() { proto.RegisterFile("query.proto", fileDescriptor_5c6ac9b241082464) } var fileDescriptor_5c6ac9b241082464 = []byte{ - // 4451 bytes of a gzipped FileDescriptorProto + // 4555 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x5c, 0xcd, 0x73, 0xdb, 0x48, - 0x76, 0x37, 0xad, 0x2f, 0xf2, 0x89, 0x92, 0xa0, 0xb6, 0x3e, 0x20, 0x59, 0xa6, 0x64, 0x8c, 0xc7, - 0x96, 0xed, 0x1d, 0x69, 0xe2, 0x1d, 0x7b, 0xf6, 0x63, 0x92, 0x1a, 0x89, 0xb2, 0x6c, 0xda, 0x92, - 0x25, 0x37, 0xe9, 0x8f, 0x9d, 0xad, 0x9a, 0x2a, 0x88, 0x6c, 0x51, 0x58, 0x91, 0x00, 0x07, 0x00, - 0x3d, 0xd2, 0x54, 0xe5, 0x1f, 0xc8, 0x21, 0xb5, 0x7f, 0x42, 0x52, 0x95, 0xaa, 0xdc, 0x72, 0x4c, - 0x55, 0x8e, 0x7b, 0xdb, 0xe3, 0x1e, 0x72, 0xd8, 0x5c, 0x92, 0xd4, 0xcc, 0xff, 0x90, 0x6b, 0xb6, - 0xba, 0xd1, 0xdd, 0x40, 0x03, 0x0d, 0xd0, 0xf3, 0xb1, 0x17, 0x17, 0xfb, 0xf5, 0x7b, 0xbf, 0x6e, - 0xf4, 0xc7, 0xeb, 0xf7, 0xeb, 0xd7, 0x32, 0x4c, 0x7f, 0x35, 0x24, 0xfe, 0xe5, 0xd6, 0xc0, 0xf7, - 0x42, 0x0f, 0x4d, 0xb0, 0xc2, 0x6a, 0x35, 0x08, 0xed, 0x70, 0x18, 0x44, 0xc2, 0x55, 0xe8, 0x79, - 0xed, 0x73, 0xfe, 0xbb, 0x12, 0x5e, 0xb8, 0xfc, 0xe7, 0x5c, 0xe8, 0xf4, 0x49, 0x10, 0xda, 0xfd, - 0x81, 0x10, 0x50, 0xab, 0xc0, 0x71, 0x4f, 0x3d, 0x61, 0x38, 0xe8, 0xd9, 0x42, 0xfb, 0xa3, 0xae, - 0x13, 0x9e, 0x0d, 0x4f, 0xb6, 0xda, 0x5e, 0x7f, 0xbb, 0xeb, 0x75, 0xbd, 0x6d, 0x26, 0x3e, 0x19, - 0x9e, 0xb2, 0x12, 0x2b, 0xb0, 0x5f, 0x5c, 0x7d, 0xbd, 0xeb, 0x79, 0xdd, 0x1e, 0x89, 0xb5, 0x52, - 0x8d, 0x59, 0xb7, 0xa0, 0xfa, 0x92, 0xf6, 0x15, 0x93, 0xaf, 0x86, 0x24, 0x08, 0xd1, 0x02, 0x4c, - 0xb0, 0xb2, 0x59, 0xda, 0x28, 0x6d, 0x56, 0x70, 0x54, 0xb0, 0x5e, 0xc0, 0x52, 0xf3, 0xcc, 0xfb, - 0xfa, 0xd8, 0xf7, 0xda, 0x24, 0x08, 0x0e, 0x9c, 0x20, 0x14, 0xfa, 0x4b, 0x30, 0xd9, 0x22, 0xae, - 0xed, 0x86, 0xdc, 0x80, 0x97, 0xd0, 0x1a, 0x54, 0x9a, 0x97, 0x01, 0xaf, 0xba, 0xba, 0x51, 0xda, - 0x2c, 0xe3, 0x58, 0x60, 0xbd, 0x81, 0xf9, 0xe6, 0xa5, 0xdb, 0xae, 0x7b, 0xfd, 0xbe, 0x23, 0xa1, - 0x76, 0x61, 0xf6, 0xc0, 0x0e, 0x49, 0x10, 0x46, 0xe2, 0x56, 0x93, 0x41, 0x4e, 0x3f, 0x58, 0xd8, - 0x8a, 0x3b, 0xdd, 0x12, 0xbf, 0x76, 0xc7, 0xff, 0xf8, 0xdf, 0xeb, 0x57, 0x70, 0xca, 0xc2, 0xfa, - 0x02, 0x50, 0x12, 0x38, 0x18, 0x78, 0x6e, 0x40, 0xd0, 0x1e, 0xcc, 0xd5, 0x87, 0xbe, 0x4f, 0xdc, - 0xef, 0x03, 0x9d, 0x36, 0xb1, 0x10, 0x18, 0x4f, 0x48, 0xa8, 0xf4, 0xd9, 0xfa, 0x0d, 0xcc, 0x27, - 0x64, 0x3f, 0x69, 0x73, 0xdb, 0xb0, 0x58, 0xf7, 0x7c, 0xb2, 0x37, 0xec, 0x0f, 0xea, 0x9e, 0x7b, - 0xea, 0x74, 0x13, 0x43, 0xbe, 0xd3, 0x0e, 0x1d, 0xcf, 0x15, 0x43, 0x1e, 0x95, 0x2c, 0x13, 0x96, - 0xd2, 0x06, 0x51, 0x87, 0xac, 0xeb, 0xb0, 0xf2, 0x84, 0x84, 0xc7, 0x74, 0xc2, 0xdb, 0x5e, 0xef, - 0x35, 0xf1, 0x03, 0xc7, 0x73, 0xc5, 0x27, 0x3c, 0x82, 0x55, 0x5d, 0x25, 0xff, 0x16, 0x13, 0xa6, - 0xb8, 0x88, 0xb5, 0x36, 0x86, 0x45, 0xd1, 0x7a, 0x08, 0x2b, 0xcd, 0x3c, 0xd0, 0x02, 0xb3, 0x47, - 0xb0, 0xda, 0xfc, 0x21, 0xcd, 0xfd, 0x0c, 0x66, 0xf1, 0xd0, 0x6d, 0xd9, 0xc1, 0xb9, 0x68, 0x63, - 0x15, 0xca, 0xb4, 0x58, 0xf7, 0x3a, 0x84, 0x29, 0x4f, 0x60, 0x59, 0xb6, 0xee, 0xc2, 0x9c, 0xd4, - 0xe6, 0xd0, 0x4b, 0x30, 0x89, 0x49, 0x30, 0xec, 0xc9, 0x95, 0x1a, 0x95, 0xe8, 0xb0, 0xd1, 0xef, - 0x77, 0x06, 0xa4, 0xe7, 0xb8, 0xa4, 0xe1, 0x9e, 0x7a, 0x62, 0x64, 0xb6, 0x61, 0x39, 0x53, 0xc3, - 0xc1, 0x16, 0x60, 0xa2, 0xee, 0x0d, 0xf9, 0xaa, 0x1f, 0xc3, 0x51, 0xc1, 0xfa, 0xf7, 0x15, 0x98, - 0x12, 0xbd, 0x5b, 0x83, 0x0a, 0xff, 0xd9, 0xd8, 0x63, 0x5a, 0xe3, 0x38, 0x16, 0xa0, 0x2d, 0xa8, - 0xd4, 0xfb, 0x9d, 0x43, 0x12, 0x9e, 0x79, 0x1d, 0xb6, 0x3d, 0x66, 0x1f, 0x18, 0x5b, 0x91, 0x07, - 0x91, 0x72, 0x1c, 0xab, 0xa0, 0x4f, 0xd5, 0x6d, 0x6a, 0x8e, 0xb1, 0xf5, 0x74, 0x8d, 0x9b, 0x24, - 0xab, 0xb0, 0xba, 0x9f, 0x5f, 0xe5, 0xed, 0x5c, 0x73, 0x9c, 0x41, 0xdc, 0xe0, 0x10, 0x7a, 0x25, - 0x9c, 0xb7, 0xed, 0x0f, 0xe0, 0xda, 0x4e, 0x2f, 0x24, 0xfe, 0x4e, 0xbb, 0x4d, 0xbf, 0x5c, 0x60, - 0x4e, 0x30, 0xcc, 0x55, 0x8e, 0xa9, 0xd1, 0xc0, 0x3a, 0x33, 0xf4, 0x39, 0xcc, 0x3d, 0x77, 0x7a, - 0xbd, 0xba, 0xe7, 0x8a, 0x05, 0x64, 0x4e, 0x32, 0xa4, 0x25, 0x8e, 0x94, 0xaa, 0xc5, 0x69, 0x75, - 0x54, 0x07, 0xa3, 0xe5, 0xdb, 0x6d, 0xd2, 0x1c, 0xd8, 0x12, 0x62, 0x8a, 0x41, 0x2c, 0x73, 0x88, - 0x74, 0x35, 0xce, 0x18, 0xa0, 0x06, 0xa0, 0x27, 0x24, 0x3c, 0xf0, 0xda, 0xe7, 0x89, 0x55, 0x60, - 0x96, 0x19, 0xcc, 0x0a, 0x87, 0xc9, 0x2a, 0x60, 0x8d, 0x11, 0xda, 0x67, 0x7e, 0xa1, 0x75, 0xe1, - 0x26, 0x91, 0x2a, 0x0c, 0xc9, 0x8c, 0x91, 0xd4, 0x7a, 0x9c, 0x35, 0xa1, 0xe3, 0x4c, 0xfd, 0x8b, - 0xdd, 0x3e, 0x4b, 0xae, 0x4c, 0x13, 0x94, 0x71, 0xd6, 0x68, 0x60, 0x9d, 0x19, 0xfa, 0x05, 0x40, - 0xf3, 0xb2, 0xed, 0x46, 0x2e, 0xc6, 0x9c, 0x56, 0xba, 0x93, 0xf1, 0xc7, 0x38, 0xa1, 0x8b, 0x1e, - 0x42, 0x45, 0xfa, 0x39, 0xb3, 0xaa, 0x0c, 0x6c, 0xda, 0x27, 0xe2, 0x58, 0x13, 0x1d, 0xb3, 0x11, - 0x4d, 0x6d, 0x76, 0x73, 0x86, 0xd9, 0x6f, 0xc4, 0xf6, 0x7a, 0x27, 0x82, 0x35, 0xb6, 0x14, 0x31, - 0xeb, 0x3e, 0xcc, 0x59, 0x05, 0xb1, 0x99, 0x8f, 0x98, 0xad, 0x42, 0x7b, 0x30, 0xab, 0xba, 0x4d, - 0x73, 0x8e, 0xa1, 0xad, 0x89, 0xfd, 0xa8, 0x73, 0xc2, 0x38, 0x65, 0x83, 0xb6, 0x61, 0x8a, 0x3b, - 0x1c, 0xd3, 0x60, 0xe6, 0x8b, 0xdc, 0x5c, 0x75, 0x5a, 0x58, 0x68, 0xa1, 0xdf, 0xc0, 0x22, 0x26, - 0x7d, 0xef, 0x1d, 0xa1, 0xff, 0x86, 0x84, 0x2e, 0xa0, 0x96, 0x7d, 0xd2, 0x23, 0xe6, 0x3c, 0x33, - 0xff, 0x40, 0x98, 0xeb, 0x74, 0x04, 0x98, 0x1e, 0x01, 0xed, 0xc0, 0x0c, 0x5d, 0x92, 0xec, 0x64, - 0xdc, 0x75, 0xdc, 0x8e, 0x89, 0x18, 0xe4, 0xf5, 0xc4, 0x12, 0x96, 0x75, 0x02, 0x4a, 0xb5, 0x40, - 0xcf, 0xc0, 0x78, 0xe5, 0x06, 0xc3, 0x93, 0xa0, 0xed, 0x3b, 0x27, 0x24, 0xea, 0xd8, 0x35, 0x86, - 0x52, 0xe3, 0x28, 0xe9, 0x6a, 0xb9, 0xad, 0xd2, 0x15, 0xc9, 0x35, 0xbc, 0x67, 0x87, 0xb6, 0x58, - 0xc3, 0x0b, 0xda, 0x35, 0x9c, 0xd0, 0xc0, 0x3a, 0x33, 0x8e, 0xd6, 0xa4, 0x21, 0x52, 0x72, 0x47, - 0x2c, 0xa6, 0xd1, 0xd2, 0x1a, 0x58, 0x67, 0x46, 0xdd, 0xa3, 0xde, 0xf9, 0x9b, 0x4b, 0x8a, 0x7b, - 0xd4, 0x2b, 0xe1, 0x1c, 0x63, 0x0a, 0x7b, 0xe8, 0x74, 0x7d, 0x3b, 0x24, 0xd4, 0x49, 0xed, 0xfb, - 0x5e, 0x5f, 0xc0, 0x2e, 0x2b, 0xb0, 0x7a, 0x25, 0x9c, 0x63, 0x8c, 0x8e, 0x60, 0x21, 0x51, 0xd3, - 0x92, 0x7d, 0x35, 0x95, 0xf9, 0xd5, 0xa9, 0x60, 0xad, 0x21, 0x3a, 0x01, 0x13, 0x93, 0x9e, 0x67, - 0x77, 0x76, 0x86, 0xa1, 0xd7, 0x70, 0xdb, 0x3e, 0xe9, 0xd3, 0x10, 0x84, 0x8e, 0xb9, 0xb9, 0xc2, - 0x40, 0x6f, 0xcb, 0x75, 0xa8, 0x57, 0x13, 0xf8, 0xb9, 0x38, 0xd4, 0x35, 0xd7, 0xc3, 0x1e, 0x26, - 0x76, 0x87, 0xf8, 0xa2, 0xc3, 0xab, 0x8a, 0x07, 0x49, 0x57, 0xe3, 0x8c, 0x01, 0x3a, 0x84, 0xb9, - 0x27, 0x24, 0xc4, 0x64, 0xd0, 0x73, 0xda, 0x76, 0x74, 0xf2, 0x5e, 0x4f, 0x4f, 0x50, 0xb2, 0x96, - 0xdb, 0x89, 0xd8, 0x2a, 0x55, 0x4b, 0x17, 0x11, 0x26, 0x01, 0x09, 0x9b, 0x24, 0x48, 0xb8, 0x07, - 0x73, 0x4d, 0x59, 0x44, 0x1a, 0x0d, 0xac, 0x33, 0x43, 0x07, 0x30, 0xff, 0xc4, 0x3b, 0xb4, 0x2f, - 0xe8, 0x39, 0x19, 0x08, 0xac, 0x1b, 0xaa, 0xb3, 0x4f, 0xd7, 0xf3, 0x9e, 0x65, 0x0d, 0x39, 0x1a, - 0xe9, 0x1f, 0x38, 0xb1, 0x4f, 0x35, 0x6b, 0x69, 0x34, 0xb5, 0x3e, 0x81, 0xa6, 0x56, 0xa0, 0x2f, - 0x61, 0x79, 0xdf, 0xe9, 0x91, 0x26, 0xf1, 0xdf, 0x39, 0x6d, 0x92, 0x9c, 0x32, 0x73, 0x5d, 0xd9, - 0xcf, 0x39, 0x5a, 0x1c, 0x39, 0x0f, 0x04, 0xf5, 0x61, 0x2d, 0x5d, 0xf5, 0xf8, 0x9d, 0xd3, 0x96, - 0x1d, 0xdf, 0x50, 0xbc, 0x59, 0x91, 0x2a, 0x6f, 0xa9, 0x10, 0x0e, 0xbd, 0x82, 0x85, 0x43, 0x12, - 0xda, 0x1d, 0x3b, 0xb4, 0x95, 0x6f, 0xb9, 0xa9, 0xee, 0x00, 0x8d, 0x0a, 0x87, 0xd7, 0x9a, 0xa3, - 0x23, 0x40, 0x4f, 0xbc, 0x27, 0xf5, 0x63, 0xe2, 0xb7, 0x49, 0x1c, 0xcd, 0x58, 0xea, 0xc9, 0x9f, - 0x51, 0xe0, 0x90, 0x1a, 0x53, 0x1a, 0x4a, 0xec, 0xdb, 0xc3, 0x5e, 0xd8, 0x70, 0x7f, 0x47, 0xe2, - 0xc1, 0xf8, 0x40, 0x01, 0xcc, 0x2a, 0x60, 0x8d, 0x11, 0xfa, 0x2d, 0x2c, 0xd5, 0xc3, 0xde, 0xa1, - 0xc7, 0x9c, 0x29, 0x73, 0x60, 0x02, 0xee, 0x96, 0xb2, 0x03, 0xf4, 0x4a, 0xbc, 0x8f, 0x39, 0x10, - 0xe8, 0x4b, 0x58, 0x79, 0xe3, 0xf9, 0xe7, 0xc1, 0xc0, 0x6e, 0x93, 0xd6, 0x99, 0x4f, 0x82, 0x33, - 0xaf, 0x27, 0xce, 0x04, 0xf3, 0x43, 0xe5, 0x54, 0xcd, 0xd5, 0xc3, 0xf9, 0x10, 0xa8, 0x0f, 0xb5, - 0x7a, 0xd8, 0x3b, 0xf6, 0xc9, 0x29, 0x09, 0xdb, 0x67, 0x47, 0x6e, 0x53, 0x1c, 0x0d, 0xb2, 0x91, - 0xdb, 0xac, 0x91, 0x0f, 0xe3, 0x8f, 0x28, 0x50, 0xc6, 0x23, 0xc0, 0xd0, 0x6f, 0xc1, 0x6c, 0x34, - 0xeb, 0xc7, 0x7b, 0xbe, 0xed, 0xb8, 0x75, 0xcf, 0x0d, 0x86, 0xfd, 0xd8, 0xe7, 0xdc, 0x61, 0x0d, - 0xad, 0xf3, 0x86, 0xf2, 0xd4, 0x70, 0x2e, 0x00, 0x1a, 0xc0, 0x8d, 0x46, 0x9b, 0x9c, 0x10, 0xbf, - 0xcb, 0x03, 0xab, 0x77, 0x76, 0xcf, 0xe9, 0xd8, 0xa1, 0x5c, 0x84, 0x9b, 0xac, 0x85, 0x5b, 0xa2, - 0x85, 0x22, 0x5d, 0x3e, 0x2d, 0xc5, 0x80, 0xa8, 0x0b, 0xab, 0x87, 0x9e, 0xdb, 0xf5, 0xf6, 0x76, - 0xeb, 0x3d, 0x87, 0xad, 0xae, 0xd0, 0xf1, 0x65, 0x73, 0x77, 0x59, 0x73, 0x37, 0xc5, 0x9a, 0xcf, - 0x55, 0xe4, 0x6d, 0x15, 0x40, 0x59, 0xfb, 0xb0, 0x9c, 0x09, 0xf4, 0x39, 0xd3, 0xb9, 0x0f, 0x65, - 0xee, 0xee, 0x02, 0xb3, 0xb4, 0x31, 0xb6, 0x39, 0xfd, 0x60, 0x6e, 0x8b, 0x5f, 0x6b, 0x08, 0x37, - 0x28, 0x15, 0xac, 0x3f, 0xac, 0x42, 0x59, 0x5a, 0xfe, 0xb4, 0x0c, 0x68, 0x01, 0x26, 0x1e, 0xfb, - 0xbe, 0xe7, 0x33, 0xea, 0x53, 0xc5, 0x51, 0x01, 0xbd, 0xcd, 0xed, 0x38, 0xe7, 0x37, 0xb5, 0x3c, - 0x7e, 0x13, 0x69, 0xe1, 0xdc, 0xef, 0x3e, 0x82, 0x05, 0x95, 0xaa, 0x70, 0xd8, 0x09, 0xc5, 0xd3, - 0xe8, 0x54, 0xb0, 0xd6, 0x90, 0x9e, 0x83, 0x31, 0x6b, 0xe1, 0x60, 0x93, 0xca, 0x39, 0x98, 0xae, - 0xc6, 0x19, 0x03, 0xca, 0x2b, 0x12, 0xb4, 0x85, 0xa3, 0x4c, 0x29, 0x87, 0x43, 0xa6, 0x1e, 0x67, - 0x4d, 0x78, 0x14, 0x15, 0xb3, 0x16, 0x8e, 0x54, 0x4e, 0x47, 0x51, 0x69, 0x0d, 0xac, 0x33, 0xe3, - 0xc4, 0x49, 0x52, 0x17, 0x0e, 0x56, 0x49, 0x13, 0xa7, 0x94, 0x02, 0xd6, 0x18, 0xd1, 0x61, 0x57, - 0x99, 0x0b, 0x07, 0x83, 0x74, 0x08, 0x9b, 0x51, 0xc1, 0x5a, 0x43, 0xf4, 0x4b, 0xca, 0x79, 0x04, - 0xb5, 0xe1, 0x9c, 0x67, 0x45, 0xc3, 0x79, 0x38, 0x48, 0x42, 0x19, 0x3d, 0xca, 0x92, 0x1e, 0x33, - 0x4b, 0x7a, 0xb8, 0x61, 0x82, 0xf5, 0xbc, 0x2c, 0x60, 0x3d, 0x37, 0x0b, 0x58, 0x4f, 0x62, 0x58, - 0xd2, 0x24, 0xe5, 0x65, 0x01, 0xed, 0xb9, 0x59, 0x40, 0x7b, 0x04, 0xa4, 0x86, 0xf7, 0x3c, 0xce, - 0xe1, 0x3d, 0x37, 0x72, 0x78, 0x0f, 0x87, 0x4a, 0x13, 0x9f, 0x8f, 0xd3, 0xc4, 0x67, 0x29, 0x4d, - 0x7c, 0xb8, 0xa1, 0x64, 0x3e, 0x5f, 0x14, 0x33, 0x9f, 0x5b, 0xc5, 0xcc, 0x87, 0xa3, 0xe5, 0x50, - 0x9f, 0x5d, 0x3d, 0xf5, 0x59, 0xd3, 0x53, 0x1f, 0x8e, 0x95, 0xe2, 0x3e, 0xcf, 0x73, 0xb9, 0xcf, - 0x7a, 0x2e, 0xf7, 0x11, 0x1b, 0x36, 0x43, 0x7e, 0x12, 0xeb, 0x39, 0x62, 0x31, 0x7c, 0x3d, 0x2f, - 0x68, 0xd7, 0x73, 0x52, 0x05, 0x6b, 0x0d, 0x39, 0x60, 0x82, 0xc8, 0x70, 0xc0, 0xc5, 0x34, 0x60, - 0x46, 0x05, 0x6b, 0x0d, 0xa9, 0x0b, 0xcd, 0xb9, 0xe5, 0xe2, 0x1c, 0xa8, 0x96, 0xc7, 0x81, 0x84, - 0x0b, 0xcd, 0xbb, 0x24, 0x7b, 0x0b, 0xcb, 0x19, 0x22, 0xc3, 0x91, 0x97, 0x15, 0xe4, 0x1c, 0x2d, - 0x9c, 0x67, 0x8e, 0x30, 0x2c, 0xa6, 0xf8, 0x0c, 0xc7, 0x35, 0x95, 0xe9, 0xd6, 0xea, 0x60, 0xbd, - 0x29, 0x6a, 0x8f, 0xe4, 0x42, 0x77, 0x46, 0x72, 0x21, 0xde, 0x42, 0x3e, 0x19, 0xda, 0x87, 0xf9, - 0x04, 0xb7, 0xe1, 0x9d, 0x5e, 0x55, 0x5c, 0x4b, 0xa6, 0x1e, 0x67, 0x4d, 0xd0, 0x8b, 0x3c, 0x3e, - 0x54, 0xcb, 0xe3, 0x43, 0x91, 0x61, 0x1e, 0x21, 0x3a, 0x82, 0x05, 0x95, 0xd9, 0xf0, 0xae, 0xad, - 0x29, 0xab, 0x4a, 0xa7, 0x82, 0xb5, 0x86, 0x51, 0x44, 0x1d, 0x53, 0x1b, 0x0e, 0x77, 0x23, 0x15, - 0x51, 0xa7, 0x15, 0xe2, 0x88, 0x3a, 0x5d, 0xc3, 0x01, 0x25, 0xbb, 0xe1, 0x80, 0xb5, 0x34, 0x60, - 0x4a, 0x21, 0x01, 0x98, 0xaa, 0x41, 0x36, 0x98, 0x59, 0x52, 0xc3, 0x61, 0xd7, 0x95, 0xed, 0x9e, - 0xa7, 0xc6, 0xc1, 0x73, 0x61, 0x68, 0xc4, 0x98, 0xc3, 0x66, 0x78, 0x3b, 0x1b, 0x8a, 0xc7, 0x2b, - 0xd4, 0x15, 0x11, 0x63, 0xa1, 0x12, 0x7a, 0x0b, 0x8b, 0x29, 0x82, 0xc3, 0x5b, 0xba, 0xa9, 0x6e, - 0x0c, 0x9d, 0x0e, 0x6f, 0x41, 0x0f, 0x80, 0x30, 0x5c, 0x53, 0x78, 0x0e, 0xc7, 0xb5, 0xd4, 0x88, - 0x21, 0xab, 0xc1, 0x51, 0x75, 0xc6, 0x34, 0x0a, 0x51, 0x08, 0x0f, 0xc7, 0xfc, 0x40, 0xc1, 0xd4, - 0x68, 0x60, 0x9d, 0x19, 0xa5, 0xba, 0x19, 0x96, 0xc3, 0x11, 0x6f, 0x29, 0x7b, 0x23, 0x47, 0x4b, - 0x50, 0xdd, 0x9c, 0x6a, 0x64, 0xc3, 0xaa, 0x8e, 0xe8, 0xf0, 0x26, 0x3e, 0x54, 0xce, 0xe2, 0x7c, - 0x45, 0x5c, 0x00, 0x12, 0x5d, 0xf0, 0xb8, 0x32, 0x35, 0x24, 0xc1, 0x6f, 0xa7, 0x2e, 0x78, 0xb2, - 0x2a, 0x58, 0x6b, 0x88, 0x06, 0xb0, 0x9e, 0x4b, 0x99, 0x38, 0xf6, 0x1d, 0xe5, 0x9e, 0x67, 0x84, - 0x36, 0x1e, 0x05, 0x47, 0x19, 0xa5, 0x86, 0x41, 0xf1, 0xb6, 0x36, 0x15, 0x46, 0x99, 0xab, 0x87, - 0xf3, 0x21, 0x50, 0x00, 0xb5, 0x3c, 0xd2, 0xc4, 0x1b, 0xb9, 0xab, 0x30, 0xca, 0x62, 0x65, 0x3e, - 0xe7, 0x23, 0x20, 0xd1, 0xef, 0xe0, 0xba, 0x96, 0x3d, 0xf1, 0x16, 0xef, 0xb1, 0x16, 0xad, 0x22, - 0x26, 0xa6, 0x34, 0x57, 0x04, 0x66, 0x35, 0xb4, 0xa9, 0x15, 0x96, 0xed, 0x62, 0xc9, 0xd3, 0x46, - 0x87, 0x27, 0x9d, 0x64, 0x19, 0x2d, 0xc1, 0x64, 0x93, 0x51, 0x32, 0x46, 0x8e, 0x2a, 0x98, 0x97, - 0xac, 0x5f, 0xe9, 0x39, 0x0c, 0xb2, 0xa0, 0x6a, 0x53, 0x79, 0x73, 0xd8, 0xa6, 0xbc, 0x87, 0xe1, - 0x95, 0xb1, 0x22, 0xb3, 0x1a, 0x99, 0x9c, 0x0c, 0x25, 0x74, 0x1c, 0x89, 0x13, 0xba, 0x31, 0x1c, - 0x0b, 0x92, 0xa9, 0xbb, 0xab, 0x8c, 0xec, 0x25, 0x52, 0x77, 0x59, 0x22, 0x63, 0xc2, 0x94, 0xda, - 0xba, 0x28, 0x5a, 0x07, 0xd9, 0xfb, 0x42, 0x64, 0xc0, 0x58, 0xbd, 0xdf, 0xe1, 0x89, 0x3b, 0xfa, - 0x93, 0x49, 0x4e, 0xbb, 0xac, 0x25, 0x2a, 0x39, 0xed, 0x32, 0x82, 0x78, 0x11, 0xfa, 0xb6, 0x24, - 0x88, 0xb4, 0x60, 0xdd, 0xd1, 0x1c, 0xb8, 0x08, 0xc1, 0x38, 0xfd, 0xcd, 0xf1, 0xd8, 0x6f, 0xeb, - 0xb3, 0x51, 0x37, 0x15, 0x74, 0x06, 0x8e, 0xed, 0x30, 0x24, 0x3e, 0x67, 0xc2, 0x15, 0x2c, 0xcb, - 0xd6, 0xc3, 0x91, 0xfb, 0x4c, 0xdb, 0xe8, 0x7f, 0x96, 0xf2, 0x2f, 0x2c, 0xb2, 0xc3, 0x3d, 0x93, - 0x1a, 0x6e, 0xe6, 0xa3, 0x1a, 0x7b, 0x62, 0xb8, 0x79, 0x91, 0xd6, 0x3c, 0xf3, 0x4e, 0x5e, 0xd8, - 0x7d, 0xc2, 0x97, 0x83, 0x28, 0xd2, 0x21, 0x7a, 0xe6, 0x9d, 0x34, 0xf6, 0x18, 0x37, 0x1e, 0xc7, - 0x51, 0x01, 0x6d, 0xc2, 0x5c, 0x14, 0x4c, 0xef, 0x13, 0xb7, 0x4d, 0x8e, 0xdc, 0xde, 0x25, 0x23, - 0xb9, 0x65, 0x9c, 0x16, 0xa3, 0xdb, 0x30, 0x8b, 0x89, 0x4b, 0xbe, 0x8e, 0x15, 0x27, 0x99, 0x62, - 0x4a, 0x6a, 0x3d, 0x2c, 0xf0, 0x01, 0x05, 0x33, 0xff, 0x36, 0x9b, 0xc4, 0xd3, 0xcc, 0xfc, 0x02, - 0x4c, 0x50, 0x85, 0x80, 0xcf, 0x7d, 0x54, 0xa0, 0x83, 0x25, 0xdd, 0x29, 0xfb, 0xec, 0x31, 0x1c, - 0x0b, 0xe8, 0x2a, 0xc8, 0x72, 0x60, 0xdd, 0x84, 0x2c, 0xe8, 0x52, 0x80, 0xd6, 0x9f, 0x4b, 0x50, - 0x16, 0xb2, 0x78, 0xe0, 0x3b, 0xfc, 0x52, 0x43, 0x14, 0x29, 0xe0, 0x73, 0x72, 0x49, 0x3b, 0x36, - 0xb6, 0x59, 0xc5, 0xec, 0x37, 0xba, 0x17, 0x59, 0x1e, 0x7a, 0x9d, 0x68, 0x36, 0x66, 0x1f, 0xcc, - 0x6e, 0xb1, 0x77, 0x20, 0x42, 0x8a, 0x65, 0x3d, 0xda, 0x80, 0x69, 0x27, 0xc0, 0xb6, 0xdb, 0x65, - 0x84, 0x86, 0x4d, 0x52, 0x19, 0x27, 0x45, 0xe8, 0x0e, 0x4c, 0x3d, 0xf5, 0x7a, 0x1d, 0xe2, 0x07, - 0xe6, 0x04, 0xbb, 0x8b, 0x99, 0x89, 0xc0, 0xde, 0xd8, 0x0e, 0x65, 0xd2, 0x58, 0xd4, 0x52, 0x45, - 0x2a, 0xa3, 0x8a, 0x93, 0x5a, 0x45, 0x5e, 0x6b, 0x7d, 0xa9, 0xbd, 0x08, 0xa0, 0x9f, 0x52, 0x77, - 0x1b, 0x62, 0xdc, 0xd9, 0x6f, 0xf4, 0x73, 0xa8, 0x0a, 0xbd, 0x03, 0x27, 0x08, 0xd9, 0x67, 0x4e, - 0x3f, 0x98, 0xe3, 0x5e, 0x4f, 0x42, 0x28, 0x4a, 0xd6, 0x35, 0x4d, 0x22, 0xd4, 0x3a, 0x83, 0xe9, - 0xd6, 0x85, 0xfb, 0x7e, 0x23, 0x8a, 0xbd, 0xaf, 0xe5, 0x88, 0xd2, 0xdf, 0xe8, 0x3e, 0x4c, 0x1d, - 0x0d, 0x42, 0x76, 0x1f, 0x15, 0x65, 0xc1, 0xe7, 0xe3, 0x01, 0xe5, 0x15, 0x58, 0x68, 0x58, 0xff, - 0x51, 0x82, 0x29, 0xde, 0x38, 0xfa, 0x1c, 0xca, 0x75, 0x9f, 0xd8, 0x21, 0xd9, 0x09, 0xf9, 0x7b, - 0x8c, 0xd5, 0xad, 0xe8, 0x79, 0xcc, 0x96, 0x78, 0x1e, 0x93, 0x78, 0x95, 0x51, 0xa6, 0x9e, 0xfa, - 0xf7, 0xff, 0xb3, 0x5e, 0xc2, 0xd2, 0x0a, 0x6d, 0xc0, 0x38, 0x0d, 0x8e, 0xd8, 0xca, 0x9b, 0x7e, - 0x50, 0xdd, 0x0a, 0x2f, 0xdc, 0xad, 0xd6, 0x85, 0x4b, 0x65, 0x98, 0xd5, 0xd0, 0x4f, 0x79, 0x15, - 0x10, 0xbf, 0x75, 0xe1, 0xb2, 0xce, 0x95, 0xb1, 0x28, 0xa2, 0x8f, 0xa1, 0x42, 0xc7, 0x9c, 0xf6, - 0x32, 0x30, 0xc7, 0xd9, 0xd0, 0x21, 0x71, 0x63, 0x13, 0x8f, 0x05, 0x8e, 0x95, 0xac, 0x2f, 0x74, - 0xb7, 0x2a, 0xda, 0x99, 0xf9, 0x98, 0x8d, 0x67, 0x6a, 0x62, 0x66, 0x63, 0x74, 0x06, 0x90, 0x54, - 0xb1, 0x16, 0xb5, 0x79, 0x65, 0xeb, 0x9f, 0x4b, 0x50, 0x91, 0x42, 0xea, 0xf0, 0x5e, 0x78, 0x1d, - 0xd2, 0xba, 0x1c, 0x10, 0xde, 0x9c, 0x2c, 0xd3, 0x23, 0x87, 0xfe, 0x6e, 0x74, 0xf8, 0x36, 0xe4, - 0x25, 0xba, 0x0f, 0x19, 0x00, 0x33, 0x8a, 0xdc, 0x4f, 0x2c, 0xa0, 0x9d, 0x7f, 0x15, 0x90, 0x0e, - 0xf7, 0x3f, 0xec, 0x37, 0x95, 0xed, 0xfb, 0x24, 0xba, 0x58, 0x1b, 0xc7, 0xec, 0x37, 0x6d, 0xf9, - 0xa9, 0x13, 0x62, 0x3b, 0x74, 0x3c, 0xe6, 0x62, 0xae, 0x62, 0x59, 0xb6, 0x5e, 0xe8, 0x6f, 0x88, - 0xd0, 0x23, 0x98, 0x91, 0x42, 0x36, 0x0c, 0xd1, 0x6d, 0xa5, 0xbc, 0x54, 0x94, 0x06, 0xaa, 0x9a, - 0xd5, 0x83, 0xb5, 0xa2, 0x24, 0x2b, 0x9d, 0xd2, 0x27, 0xbe, 0x37, 0x1c, 0x48, 0x27, 0x2c, 0x8a, - 0xc5, 0x2e, 0x58, 0x9c, 0x85, 0x63, 0xea, 0x59, 0xf8, 0x10, 0x6e, 0x14, 0x5e, 0x6c, 0xa8, 0x2f, - 0x4b, 0x26, 0xc4, 0xcb, 0x92, 0x67, 0xec, 0xa3, 0x33, 0x69, 0xdb, 0x1f, 0xd2, 0x39, 0xeb, 0x3e, - 0x2c, 0x6a, 0xef, 0x41, 0xe8, 0x4c, 0xb0, 0x3b, 0x13, 0xbe, 0xb4, 0xe8, 0x6f, 0xab, 0x09, 0xcb, - 0x39, 0x99, 0x5e, 0x54, 0x03, 0xd8, 0xb3, 0x43, 0xfb, 0xc4, 0x0e, 0x88, 0xbc, 0xe0, 0x4d, 0x48, - 0x0a, 0x7a, 0xf0, 0x09, 0x98, 0x79, 0x57, 0x28, 0x05, 0xc7, 0xc3, 0x3e, 0x94, 0xd9, 0xcc, 0x3d, - 0x27, 0x97, 0xb4, 0xab, 0xc7, 0x76, 0x78, 0x26, 0xba, 0x4a, 0x7f, 0xd3, 0x25, 0x79, 0x74, 0x7a, - 0x1a, 0x90, 0xe8, 0xbd, 0xd9, 0x18, 0xe6, 0x25, 0x34, 0x0b, 0x57, 0x9b, 0xdf, 0xf0, 0x33, 0xe1, - 0x6a, 0xf3, 0x1b, 0xeb, 0x11, 0x5f, 0xa2, 0xcc, 0x3f, 0xdf, 0x85, 0xf1, 0x73, 0xea, 0xb3, 0x4b, - 0x8a, 0x33, 0x13, 0xf5, 0x3c, 0x5e, 0x63, 0x2a, 0x56, 0x8b, 0x9e, 0x93, 0xec, 0xd3, 0x65, 0x37, - 0x16, 0x60, 0xa2, 0xe1, 0x76, 0xc8, 0x85, 0x98, 0x2c, 0x56, 0x40, 0xf7, 0xe3, 0x8e, 0x72, 0x57, - 0x91, 0xc6, 0xc5, 0x52, 0xc1, 0x7a, 0xa3, 0xcd, 0x8e, 0xa3, 0xcf, 0x33, 0x8d, 0xf1, 0x2e, 0xca, - 0xeb, 0x35, 0xb5, 0x16, 0xa7, 0xd5, 0xad, 0x23, 0x98, 0x17, 0x83, 0x2a, 0xd1, 0x73, 0x3a, 0x6c, - 0xc0, 0xd8, 0x53, 0x47, 0x3c, 0xd3, 0xa3, 0x3f, 0xe9, 0xf8, 0x52, 0x7d, 0x1e, 0x4b, 0xb1, 0xdf, - 0xd6, 0x97, 0xfa, 0xab, 0x2c, 0xb4, 0xaf, 0x69, 0x88, 0x77, 0xd6, 0x8c, 0x2f, 0x0e, 0xd4, 0x7a, - 0x9c, 0x35, 0xb1, 0xb0, 0x36, 0xb3, 0x8f, 0x7e, 0x0d, 0x55, 0x29, 0x8b, 0x86, 0x21, 0xba, 0x33, - 0x8f, 0x5f, 0x49, 0x26, 0xab, 0xb1, 0xa2, 0xcc, 0xf7, 0x4d, 0xf6, 0xd2, 0xeb, 0x01, 0x54, 0xa4, - 0x50, 0x3e, 0xce, 0xd3, 0x20, 0xe2, 0x58, 0xcd, 0x6a, 0xc2, 0xf4, 0xb1, 0x4f, 0x06, 0xb6, 0x4f, - 0x9a, 0x61, 0x9f, 0x0d, 0x11, 0x8b, 0xb1, 0xf8, 0x12, 0x64, 0x01, 0x96, 0x01, 0x63, 0xcd, 0x97, - 0x07, 0x22, 0x2a, 0x6d, 0xbe, 0x3c, 0xa0, 0x9b, 0xe4, 0xd8, 0xf6, 0xed, 0x3e, 0x75, 0x7f, 0x01, - 0x1f, 0xce, 0x84, 0xc4, 0x3a, 0x03, 0x83, 0x5f, 0x47, 0xb5, 0x48, 0x7f, 0x10, 0xdd, 0x19, 0xae, - 0x42, 0x59, 0x6c, 0x23, 0xe1, 0x77, 0x45, 0x99, 0x4e, 0xe0, 0x4e, 0xcf, 0xb1, 0x65, 0xf4, 0xc3, - 0x0a, 0x34, 0xa0, 0x3f, 0x3e, 0xbb, 0x0c, 0x9c, 0xb6, 0xdd, 0x4b, 0xc4, 0x7d, 0x8a, 0xcc, 0xfa, - 0xb7, 0x52, 0xde, 0xa3, 0x04, 0xba, 0x73, 0xa8, 0x48, 0x3a, 0x11, 0x5e, 0x42, 0x9f, 0xc8, 0x97, - 0x86, 0x51, 0x82, 0x66, 0x4d, 0x7f, 0x5f, 0x17, 0xe9, 0x88, 0x77, 0x88, 0xe8, 0x73, 0xb8, 0x2e, - 0xbf, 0x25, 0xd2, 0x74, 0x3c, 0xb7, 0x39, 0x1c, 0x0c, 0x3c, 0x3f, 0xe4, 0xbe, 0xbf, 0x8c, 0x8b, - 0x54, 0x9e, 0x8d, 0x97, 0xc7, 0x8c, 0x71, 0xeb, 0xff, 0xa6, 0x72, 0xef, 0x0f, 0xe9, 0x9e, 0xde, - 0xdb, 0xe5, 0x83, 0x73, 0x75, 0x6f, 0x17, 0x3d, 0x82, 0x6a, 0x62, 0x6e, 0x02, 0x7e, 0x04, 0x8a, - 0x03, 0x36, 0x51, 0x85, 0x15, 0x3d, 0x74, 0x0f, 0x8c, 0x03, 0x3b, 0x08, 0x77, 0x4e, 0x4f, 0x49, - 0x3b, 0x24, 0x1d, 0x16, 0x6c, 0x44, 0x9e, 0x22, 0x23, 0x47, 0x9f, 0xc1, 0x2c, 0x3d, 0xcc, 0x0f, - 0xc8, 0x3b, 0xd2, 0x4b, 0x1e, 0xe3, 0x0b, 0xe2, 0x56, 0x38, 0x59, 0x89, 0x53, 0xba, 0x68, 0x0f, - 0x6e, 0xa8, 0x0a, 0xa4, 0x47, 0xec, 0x80, 0xc4, 0xe3, 0x12, 0xc5, 0xdc, 0xc5, 0x4a, 0x68, 0x1f, - 0xe6, 0xa8, 0xc2, 0x1e, 0x39, 0x75, 0x5c, 0xd2, 0x79, 0x6d, 0xcb, 0xf8, 0x2e, 0x35, 0x35, 0xaa, - 0x12, 0x4e, 0x1b, 0xa1, 0x5f, 0xc0, 0x72, 0x4a, 0xf4, 0xf8, 0x82, 0xf7, 0x63, 0x8a, 0xf5, 0x23, - 0xaf, 0x9a, 0xf6, 0xa0, 0x79, 0x19, 0x84, 0xa4, 0xff, 0xda, 0xf6, 0x1d, 0x3a, 0x7f, 0x81, 0x59, - 0xd6, 0xf5, 0x40, 0x55, 0xc2, 0x69, 0x23, 0xda, 0x83, 0x94, 0x48, 0xf6, 0xa0, 0x12, 0xf5, 0x20, - 0xa7, 0x1a, 0x7d, 0x06, 0x2b, 0xa9, 0xce, 0x61, 0x32, 0xe8, 0xd9, 0x97, 0xec, 0xa2, 0x1e, 0x98, - 0x6d, 0xbe, 0x02, 0xb5, 0x4e, 0x01, 0x27, 0xac, 0xa7, 0x23, 0xeb, 0x5c, 0x05, 0xf4, 0x14, 0xd6, - 0x53, 0x95, 0x4d, 0xd7, 0x1e, 0x04, 0x67, 0x5e, 0xd8, 0xf2, 0xbc, 0x03, 0xdb, 0xef, 0x12, 0x96, - 0x28, 0x2a, 0xe3, 0x51, 0x6a, 0x14, 0x29, 0xd5, 0xc9, 0x0c, 0xd2, 0x4c, 0x84, 0x34, 0x42, 0x8d, - 0xee, 0x37, 0xbe, 0xa6, 0x3b, 0x74, 0x51, 0x1f, 0x78, 0x6e, 0x97, 0xba, 0x8b, 0xfa, 0x19, 0x69, - 0x9f, 0x93, 0x0e, 0x4b, 0x12, 0x95, 0x71, 0x91, 0x0a, 0x0d, 0xda, 0xf6, 0xbd, 0xa1, 0x1b, 0x2d, - 0xff, 0xb9, 0x28, 0x53, 0x2b, 0x05, 0xe8, 0x53, 0x00, 0xb9, 0x59, 0x03, 0xd3, 0x60, 0x93, 0xbd, - 0xac, 0x4e, 0xb6, 0xac, 0xc7, 0x09, 0x55, 0xf4, 0x08, 0x96, 0x64, 0x89, 0xba, 0x51, 0x22, 0x67, - 0x78, 0x9e, 0xf5, 0x29, 0xa7, 0xd6, 0xfa, 0xaf, 0x09, 0xfd, 0x3b, 0xa7, 0x5c, 0x3f, 0x15, 0x79, - 0x83, 0xab, 0xd2, 0x1b, 0x6c, 0xc0, 0x74, 0x93, 0x84, 0xaf, 0x6d, 0x3f, 0x72, 0x06, 0x63, 0x8c, - 0xac, 0x27, 0x45, 0x19, 0x7f, 0x31, 0xfe, 0x23, 0xfc, 0xc5, 0xc4, 0x7b, 0xfb, 0x8b, 0xc9, 0xef, - 0xe1, 0x2f, 0x34, 0x3b, 0x7d, 0xea, 0x27, 0xde, 0xe9, 0xe5, 0xef, 0xbd, 0xd3, 0x2b, 0x3f, 0xf1, - 0x4e, 0x87, 0x1f, 0xb1, 0xd3, 0xa7, 0x7f, 0xd4, 0x4e, 0xaf, 0x8e, 0xda, 0xe9, 0xca, 0x9e, 0x98, - 0x29, 0xde, 0x13, 0xb3, 0xef, 0xbd, 0x27, 0xac, 0x3f, 0x94, 0x64, 0xea, 0x4a, 0xed, 0xb9, 0x36, - 0x9e, 0xd8, 0x80, 0x89, 0xd7, 0x76, 0x6f, 0x48, 0x78, 0x18, 0x09, 0x5b, 0xec, 0x2f, 0x41, 0x1e, - 0x5f, 0x0c, 0x7c, 0x1c, 0x55, 0xb0, 0x88, 0xe3, 0xab, 0x1e, 0x3f, 0xf0, 0xe9, 0x4f, 0x16, 0xe2, - 0x05, 0xbb, 0x8e, 0xcb, 0x0f, 0xda, 0xa8, 0x40, 0x17, 0x2e, 0x5f, 0xc8, 0x2c, 0xf8, 0x78, 0x4e, - 0xe3, 0xfc, 0x09, 0xb6, 0x89, 0x32, 0x72, 0x54, 0x83, 0x71, 0x46, 0xdf, 0x26, 0x93, 0x8d, 0x52, - 0x09, 0x66, 0x72, 0xeb, 0x25, 0xcc, 0x28, 0x8b, 0x55, 0xdb, 0x75, 0xc9, 0x63, 0xa2, 0xd8, 0x3f, - 0x2a, 0xb0, 0x3f, 0x17, 0xb9, 0x70, 0x1b, 0x7b, 0xd1, 0xa6, 0xac, 0x62, 0x5e, 0xb2, 0xfe, 0x26, - 0x27, 0xa1, 0x57, 0x40, 0x07, 0x7e, 0x0d, 0xeb, 0x23, 0x1e, 0x25, 0x26, 0x19, 0x48, 0x49, 0x65, - 0x20, 0x16, 0x6c, 0x8c, 0xca, 0xe2, 0x59, 0x9b, 0xec, 0x6d, 0xa8, 0xe6, 0x55, 0x21, 0xf5, 0x37, - 0xf5, 0x17, 0x22, 0xfa, 0xa8, 0xbf, 0xe0, 0x7f, 0x28, 0xa0, 0xcb, 0xb7, 0xe5, 0xfc, 0xa1, 0xc0, - 0x47, 0xda, 0xf7, 0x87, 0x79, 0xfe, 0xcd, 0x3a, 0xd6, 0x67, 0xe7, 0xf2, 0x07, 0x87, 0x86, 0x9d, - 0x3b, 0xc3, 0xf0, 0xac, 0x19, 0xfa, 0x8e, 0x1b, 0xdd, 0x92, 0x56, 0x71, 0x42, 0x62, 0x6d, 0x6b, - 0x9e, 0x2c, 0xd2, 0xb8, 0x53, 0x88, 0xc4, 0x1f, 0x54, 0x88, 0xb2, 0xf5, 0xb1, 0x2e, 0x9f, 0x57, - 0x68, 0xf1, 0x4b, 0xcd, 0x3b, 0x46, 0x74, 0x0b, 0x66, 0x84, 0x68, 0xf7, 0x32, 0x24, 0x01, 0x1f, - 0x16, 0x55, 0x68, 0xfd, 0x4a, 0x97, 0xeb, 0x7b, 0x4f, 0xdb, 0xb3, 0xdc, 0x07, 0x8f, 0x68, 0x9b, - 0xaf, 0xeb, 0x12, 0x0b, 0x66, 0xaf, 0xe7, 0x64, 0xdd, 0xe2, 0x85, 0x2e, 0x2f, 0x33, 0x9a, 0xce, - 0x37, 0x84, 0x93, 0xca, 0x58, 0x60, 0x5d, 0xe4, 0x27, 0x10, 0x55, 0xcb, 0x52, 0xca, 0x92, 0x7e, - 0x09, 0x2b, 0xd4, 0xed, 0x81, 0xdd, 0x76, 0xc2, 0x4b, 0x8e, 0xad, 0x0a, 0xe9, 0xec, 0x1e, 0x92, - 0x20, 0xb0, 0xbb, 0xf2, 0x1e, 0x97, 0x17, 0xad, 0xa3, 0xe2, 0x47, 0x97, 0xdf, 0xfb, 0x43, 0xad, - 0xbf, 0x1f, 0x91, 0xa8, 0xfc, 0x2b, 0x7f, 0xcf, 0x27, 0xfa, 0x57, 0x9d, 0xc5, 0xad, 0x5a, 0x7f, - 0x9b, 0x93, 0xeb, 0xcc, 0x76, 0xa7, 0xa4, 0xe9, 0x8e, 0xf5, 0xff, 0xa5, 0x11, 0xef, 0xf9, 0x46, - 0x5c, 0xc0, 0xb3, 0xce, 0x85, 0x76, 0xcf, 0xeb, 0xca, 0x0b, 0x8e, 0x58, 0x40, 0x6b, 0xa9, 0x1b, - 0x64, 0x79, 0x3e, 0x71, 0x0f, 0x26, 0x05, 0xd4, 0x2b, 0x44, 0xef, 0x4a, 0xc6, 0x23, 0x16, 0x17, - 0xf1, 0xbe, 0x1a, 0x80, 0x08, 0xee, 0x1a, 0x7b, 0x3c, 0xac, 0x48, 0x48, 0xd0, 0x83, 0x78, 0x98, - 0x0e, 0xbc, 0x36, 0xe3, 0x4c, 0x4f, 0xed, 0xe0, 0x8c, 0xf9, 0xe9, 0x0a, 0xd6, 0xd6, 0xd1, 0x1d, - 0x1a, 0xbd, 0x4a, 0x6a, 0xec, 0xb1, 0xc8, 0xbe, 0x82, 0x65, 0xd9, 0x7a, 0x3a, 0x2a, 0x95, 0x16, - 0x5d, 0xf8, 0xf7, 0xbd, 0x77, 0xa4, 0xf3, 0xd8, 0x0d, 0x7d, 0x47, 0xee, 0xb9, 0x94, 0xd4, 0xda, - 0xd2, 0xbd, 0x9f, 0xa5, 0x13, 0xce, 0x25, 0xdc, 0x39, 0x88, 0xa2, 0xb5, 0xad, 0x4d, 0x26, 0x17, - 0x18, 0x1c, 0xe8, 0xde, 0xd3, 0x52, 0x7f, 0xc9, 0x1f, 0x10, 0xf2, 0x3f, 0xe9, 0xe2, 0x6f, 0x05, - 0x05, 0xe9, 0x26, 0xec, 0x02, 0x3c, 0x8a, 0x0b, 0x13, 0x12, 0xeb, 0xae, 0x36, 0xef, 0xac, 0x4d, - 0x08, 0xdc, 0xcb, 0x7b, 0x7d, 0x9b, 0xcd, 0x4c, 0x58, 0x1f, 0xe5, 0x26, 0xa0, 0xb5, 0xd0, 0xfd, - 0x82, 0xb7, 0xb7, 0x68, 0x13, 0xe6, 0xf8, 0x5f, 0x02, 0xca, 0xac, 0x46, 0x74, 0x84, 0xa5, 0xc5, - 0x74, 0x8e, 0xde, 0xf8, 0x4e, 0x18, 0x43, 0xf0, 0xc5, 0x98, 0x92, 0x5a, 0x6e, 0x51, 0xfa, 0xfa, - 0xaf, 0xd0, 0xde, 0x6b, 0x7d, 0x2e, 0x1b, 0xfd, 0x1d, 0x54, 0x93, 0xf2, 0xf7, 0xf8, 0xd3, 0x48, - 0x45, 0xdf, 0xfa, 0x87, 0x52, 0xd1, 0xab, 0xd8, 0x11, 0x9b, 0xd6, 0x82, 0x2a, 0x3d, 0x53, 0x09, - 0xbb, 0xa9, 0x90, 0xfb, 0x56, 0x91, 0xd1, 0x50, 0x89, 0xdf, 0xd6, 0x3e, 0xbe, 0x68, 0xf7, 0x86, - 0x81, 0xf3, 0x8e, 0xf0, 0x5b, 0xdc, 0x8c, 0xdc, 0xfa, 0xb4, 0x30, 0x31, 0x5c, 0x10, 0xbd, 0xfc, - 0x6b, 0x1c, 0x07, 0xaa, 0x31, 0xe8, 0x0f, 0x8c, 0x03, 0x37, 0x61, 0xee, 0x05, 0xb9, 0x08, 0x5b, - 0xbe, 0xed, 0x06, 0x76, 0x74, 0x67, 0x13, 0x25, 0x20, 0xd2, 0x62, 0xb4, 0x05, 0x55, 0x3c, 0x74, - 0xe9, 0x68, 0x47, 0x90, 0xe3, 0x19, 0x48, 0xa5, 0xfe, 0xde, 0xbf, 0x4c, 0x26, 0x5e, 0xea, 0xa2, - 0x0a, 0xff, 0xfb, 0x60, 0xe3, 0x0a, 0xba, 0x06, 0x73, 0xa9, 0xc7, 0xb3, 0x46, 0x09, 0x19, 0x50, - 0x4d, 0xa6, 0x9c, 0x8d, 0xab, 0xa8, 0x0a, 0x65, 0x91, 0xfd, 0x35, 0xc6, 0xd0, 0x0c, 0x54, 0x64, - 0x26, 0xce, 0x18, 0x47, 0x73, 0x30, 0x9d, 0x48, 0x3f, 0x19, 0x13, 0x68, 0x16, 0x20, 0x4e, 0x7a, - 0x18, 0x93, 0x14, 0x2f, 0x79, 0xdb, 0x6f, 0x4c, 0x51, 0x8d, 0xf8, 0x8d, 0xa6, 0x51, 0xa6, 0x88, - 0xf2, 0xe9, 0xa5, 0x51, 0x41, 0x4b, 0xba, 0xc7, 0x97, 0x06, 0x50, 0x79, 0xf6, 0x11, 0xa4, 0x31, - 0x8d, 0x50, 0xfa, 0x19, 0xa4, 0x51, 0x45, 0xd3, 0xf2, 0x4d, 0xa3, 0x31, 0x83, 0x56, 0x72, 0x9e, - 0x2b, 0x1a, 0xb3, 0x68, 0x3e, 0xf5, 0xda, 0xd0, 0x98, 0x43, 0x0b, 0xd9, 0xc7, 0x83, 0x86, 0x91, - 0xfc, 0x0a, 0xca, 0xbb, 0x8d, 0x79, 0x2e, 0x91, 0x97, 0x8b, 0x06, 0xa2, 0xc3, 0x99, 0x7a, 0x48, - 0x67, 0x5c, 0xa3, 0xc2, 0xd4, 0x15, 0x98, 0xb1, 0x40, 0x9b, 0x55, 0x82, 0x65, 0x63, 0x11, 0xad, - 0xe5, 0x3f, 0x5e, 0x33, 0x96, 0xe8, 0x10, 0xc9, 0x24, 0xb8, 0xb1, 0xcc, 0x5b, 0x4a, 0x86, 0xab, - 0x86, 0x49, 0x3b, 0x94, 0x8c, 0x31, 0x8d, 0x15, 0x36, 0x15, 0x47, 0x87, 0x3b, 0x6f, 0x8f, 0xf1, - 0x51, 0xbd, 0x69, 0xac, 0xf2, 0xf2, 0xe3, 0xc3, 0x83, 0xc6, 0x61, 0xa3, 0x65, 0x5c, 0xa7, 0x9f, - 0x9a, 0x0e, 0x1a, 0x8c, 0x35, 0x3a, 0x5c, 0xda, 0x50, 0xc2, 0xb8, 0xc1, 0xfa, 0x9d, 0x3c, 0xb0, - 0x8d, 0x1a, 0x9b, 0xff, 0x23, 0x79, 0x10, 0x18, 0xeb, 0x54, 0x90, 0x70, 0xcd, 0xc6, 0x06, 0xed, - 0x6c, 0xca, 0xa9, 0x1a, 0x37, 0xe9, 0x64, 0x66, 0x7d, 0x99, 0x61, 0xd1, 0x8f, 0x48, 0xfa, 0x0a, - 0xe3, 0x03, 0x74, 0x9d, 0xf9, 0x64, 0x5d, 0x62, 0xde, 0xb8, 0x85, 0x16, 0x61, 0x3e, 0x93, 0xa7, - 0x36, 0x3e, 0x44, 0xab, 0xb0, 0xa4, 0x3f, 0x17, 0x8d, 0xdb, 0x68, 0x19, 0xae, 0x69, 0x36, 0xbc, - 0x71, 0xe7, 0xde, 0x3f, 0x96, 0x14, 0x0a, 0x13, 0xdf, 0x8b, 0xd2, 0x11, 0x49, 0x55, 0x44, 0x04, - 0xd7, 0xb8, 0x82, 0xee, 0xc3, 0x9d, 0x54, 0x55, 0xf3, 0xdc, 0x19, 0xe8, 0xae, 0x00, 0x8d, 0x12, - 0xfa, 0x08, 0xee, 0xa6, 0x71, 0x5c, 0x3a, 0x28, 0x5a, 0xf5, 0xab, 0xf7, 0x7e, 0x06, 0x0b, 0xba, - 0x88, 0x0f, 0x01, 0x3d, 0x34, 0xfb, 0x1e, 0xdb, 0xc2, 0x65, 0x18, 0xdf, 0x73, 0x82, 0x73, 0xa3, - 0xb4, 0x7b, 0xf0, 0xc7, 0x6f, 0x6b, 0xa5, 0x3f, 0x7d, 0x5b, 0x2b, 0xfd, 0xef, 0xb7, 0xb5, 0x2b, - 0xbf, 0xff, 0xae, 0x76, 0xe5, 0x9f, 0xbe, 0xab, 0x95, 0xfe, 0xf4, 0x5d, 0xed, 0xca, 0x9f, 0xbf, - 0xab, 0x5d, 0xf9, 0x62, 0x2b, 0xf1, 0xbf, 0x0e, 0xf4, 0xed, 0xd0, 0x77, 0x2e, 0x3c, 0xdf, 0xe9, - 0x3a, 0xae, 0x28, 0xb8, 0x64, 0x7b, 0x70, 0xde, 0xdd, 0x1e, 0x9c, 0x6c, 0xb3, 0x50, 0xf3, 0x64, - 0x92, 0x25, 0x54, 0x7f, 0xfe, 0x97, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1a, 0x2c, 0x73, 0x81, 0x15, - 0x41, 0x00, 0x00, + 0x76, 0x37, 0xf5, 0x49, 0x3e, 0x51, 0x12, 0xd4, 0xd6, 0x07, 0x24, 0x4b, 0x94, 0x8c, 0xf1, 0xd8, + 0xb2, 0xbd, 0x23, 0x4d, 0xb4, 0x33, 0x9e, 0xfd, 0x98, 0xa4, 0x46, 0x22, 0x2d, 0x99, 0x36, 0x65, + 0xc9, 0x4d, 0xfa, 0x63, 0x67, 0xab, 0xa6, 0x0a, 0x22, 0x5b, 0x14, 0x56, 0x24, 0xc0, 0x01, 0x40, + 0x8f, 0x34, 0x55, 0xf9, 0x07, 0x52, 0x95, 0xd4, 0xfe, 0x09, 0xc9, 0x29, 0xb7, 0x1c, 0xb7, 0x2a, + 0xc7, 0xbd, 0xa4, 0xf6, 0xb8, 0x87, 0x1c, 0x36, 0x97, 0x24, 0x35, 0xf3, 0x3f, 0xe4, 0x9a, 0xad, + 0x6e, 0x74, 0x03, 0x68, 0xa0, 0x01, 0x7a, 0xbe, 0x2e, 0x2e, 0xf6, 0xeb, 0xdf, 0xfb, 0x75, 0xa3, + 0x3f, 0x5e, 0xbf, 0xd7, 0xaf, 0x65, 0x98, 0xf9, 0x72, 0x48, 0xdc, 0xeb, 0x9d, 0x81, 0xeb, 0xf8, + 0x0e, 0x9a, 0x64, 0x85, 0xb5, 0xb2, 0xe7, 0x9b, 0xfe, 0xd0, 0x0b, 0x84, 0x6b, 0xd0, 0x73, 0xda, + 0x97, 0xfc, 0x77, 0xc9, 0xbf, 0xb2, 0xf9, 0xcf, 0x79, 0xdf, 0xea, 0x13, 0xcf, 0x37, 0xfb, 0x03, + 0x21, 0xa0, 0x5a, 0x9e, 0x65, 0x9f, 0x3b, 0x42, 0x71, 0xd0, 0x33, 0x05, 0xfa, 0x83, 0xae, 0xe5, + 0x5f, 0x0c, 0xcf, 0x76, 0xda, 0x4e, 0x7f, 0xb7, 0xeb, 0x74, 0x9d, 0x5d, 0x26, 0x3e, 0x1b, 0x9e, + 0xb3, 0x12, 0x2b, 0xb0, 0x5f, 0x1c, 0xbe, 0xd9, 0x75, 0x9c, 0x6e, 0x8f, 0x44, 0xa8, 0x44, 0x63, + 0xc6, 0x1d, 0x28, 0xbf, 0xa0, 0x7d, 0xc5, 0xe4, 0xcb, 0x21, 0xf1, 0x7c, 0xb4, 0x08, 0x93, 0xac, + 0xac, 0x17, 0xb6, 0x0a, 0xdb, 0x25, 0x1c, 0x14, 0x8c, 0xe7, 0xb0, 0xdc, 0xbc, 0x70, 0xbe, 0x3a, + 0x75, 0x9d, 0x36, 0xf1, 0xbc, 0x86, 0xe5, 0xf9, 0x02, 0xbf, 0x0c, 0x53, 0x2d, 0x62, 0x9b, 0xb6, + 0xcf, 0x15, 0x78, 0x09, 0xad, 0x43, 0xa9, 0x79, 0xed, 0xf1, 0xaa, 0xb1, 0xad, 0xc2, 0x76, 0x11, + 0x47, 0x02, 0xe3, 0x35, 0x2c, 0x34, 0xaf, 0xed, 0x76, 0xd5, 0xe9, 0xf7, 0xad, 0x90, 0xea, 0x00, + 0xe6, 0x1a, 0xa6, 0x4f, 0x3c, 0x3f, 0x10, 0xb7, 0x9a, 0x8c, 0x72, 0x66, 0x6f, 0x71, 0x27, 0xea, + 0x74, 0x4b, 0xfc, 0x3a, 0x98, 0xf8, 0xd3, 0x7f, 0x6f, 0xde, 0xc0, 0x09, 0x0d, 0xe3, 0x73, 0x40, + 0x71, 0x62, 0x6f, 0xe0, 0xd8, 0x1e, 0x41, 0x35, 0x98, 0xaf, 0x0e, 0x5d, 0x97, 0xd8, 0xdf, 0x85, + 0x3a, 0xa9, 0x62, 0x20, 0xd0, 0x8e, 0x88, 0x2f, 0xf5, 0xd9, 0xf8, 0x0d, 0x2c, 0xc4, 0x64, 0x3f, + 0x6a, 0x73, 0xbb, 0xb0, 0x54, 0x75, 0x5c, 0x52, 0x1b, 0xf6, 0x07, 0x55, 0xc7, 0x3e, 0xb7, 0xba, + 0xb1, 0x21, 0xdf, 0x6f, 0xfb, 0x96, 0x63, 0x8b, 0x21, 0x0f, 0x4a, 0x86, 0x0e, 0xcb, 0x49, 0x85, + 0xa0, 0x43, 0xc6, 0x2d, 0x58, 0x3d, 0x22, 0xfe, 0x29, 0x9d, 0xf0, 0xb6, 0xd3, 0x7b, 0x45, 0x5c, + 0xcf, 0x72, 0x6c, 0xf1, 0x09, 0xff, 0x51, 0x80, 0x35, 0x55, 0x2d, 0xff, 0x18, 0x1d, 0xa6, 0xb9, + 0x88, 0x35, 0x37, 0x8e, 0x45, 0x11, 0x3d, 0x81, 0xcd, 0x5a, 0xad, 0xf1, 0xca, 0xf2, 0xac, 0x33, + 0xab, 0x67, 0xf9, 0xd7, 0xb4, 0x1b, 0x6f, 0x4d, 0xda, 0x95, 0x53, 0x97, 0x0c, 0x4c, 0x97, 0x74, + 0xf8, 0xc4, 0x8f, 0x82, 0xa1, 0x1a, 0x6c, 0x64, 0x40, 0x0e, 0x89, 0xdd, 0x26, 0x1d, 0x7d, 0x9c, + 0xf1, 0xe4, 0x83, 0x8c, 0x7f, 0x1c, 0x83, 0xd5, 0x66, 0xd6, 0x67, 0xe6, 0x7c, 0xc7, 0x21, 0x54, + 0x32, 0x88, 0x5b, 0xa6, 0xdb, 0x25, 0xbe, 0xa7, 0x8f, 0x6d, 0x8d, 0x6f, 0x97, 0xf0, 0x08, 0x54, + 0xea, 0x2b, 0x02, 0xf9, 0x11, 0xb1, 0x89, 0xcb, 0x70, 0xec, 0x2b, 0x26, 0x70, 0x3e, 0x28, 0x35, + 0xaa, 0x01, 0x80, 0x6d, 0xc3, 0xfd, 0x4e, 0xc7, 0x25, 0x9e, 0xa7, 0x4f, 0xb0, 0x69, 0x1f, 0x05, + 0x33, 0x1e, 0xc1, 0x5a, 0xf3, 0x7b, 0xcc, 0xab, 0xf1, 0x33, 0x98, 0xc3, 0x43, 0xbb, 0x65, 0x7a, + 0x97, 0x62, 0xec, 0xd6, 0xa0, 0x48, 0x8b, 0x55, 0xa7, 0x43, 0x18, 0x78, 0x12, 0x87, 0x65, 0xe3, + 0x3e, 0xcc, 0x87, 0x68, 0x4e, 0xbd, 0x0c, 0x53, 0x98, 0x78, 0xc3, 0x5e, 0x68, 0x13, 0x82, 0x12, + 0x5d, 0xa0, 0x74, 0xa1, 0x59, 0x03, 0xd2, 0xb3, 0x6c, 0x52, 0xb7, 0xcf, 0x1d, 0xb1, 0x06, 0x77, + 0x61, 0x25, 0x55, 0xc3, 0xc9, 0x16, 0x61, 0xb2, 0xea, 0x0c, 0xb9, 0x7d, 0x19, 0xc7, 0x41, 0xc1, + 0xf8, 0xc3, 0x2a, 0x4c, 0x8b, 0xde, 0xad, 0x43, 0x89, 0xff, 0xac, 0xd7, 0x18, 0x6a, 0x02, 0x47, + 0x02, 0xb4, 0x03, 0xa5, 0x6a, 0xbf, 0x73, 0x4c, 0xfc, 0x0b, 0x27, 0x58, 0x8f, 0x73, 0x7b, 0xda, + 0x4e, 0x60, 0xab, 0x43, 0x39, 0x8e, 0x20, 0xe8, 0x13, 0xd9, 0x20, 0xb2, 0x49, 0x9b, 0xd9, 0xbb, + 0xc9, 0x55, 0xe2, 0x55, 0x58, 0xb6, 0x9c, 0x2f, 0xb3, 0x6c, 0x24, 0x9b, 0xaf, 0x99, 0xbd, 0x0d, + 0x4e, 0xa1, 0x06, 0xe1, 0x2c, 0x03, 0xdb, 0x80, 0x9b, 0xfb, 0x3d, 0x9f, 0xb8, 0xfb, 0xed, 0x36, + 0xfd, 0x72, 0xc1, 0x39, 0xc9, 0x38, 0xd7, 0x38, 0xa7, 0x02, 0x81, 0x55, 0x6a, 0xe8, 0x33, 0x98, + 0x7f, 0x66, 0xf5, 0x7a, 0x55, 0xc7, 0x16, 0x1b, 0x43, 0x9f, 0x62, 0x4c, 0xcb, 0x9c, 0x29, 0x51, + 0x8b, 0x93, 0x70, 0x54, 0x05, 0xad, 0xe5, 0x9a, 0x6d, 0xd2, 0x1c, 0x98, 0x21, 0xc5, 0x34, 0xa3, + 0x58, 0xe1, 0x14, 0xc9, 0x6a, 0x9c, 0x52, 0x40, 0x75, 0x40, 0x47, 0xc4, 0x6f, 0x38, 0xed, 0xcb, + 0xd8, 0x2a, 0xd0, 0x8b, 0x8c, 0x66, 0x95, 0xd3, 0xa4, 0x01, 0x58, 0xa1, 0x84, 0x0e, 0x99, 0x05, + 0x6e, 0x5d, 0xd9, 0x71, 0xa6, 0x12, 0x63, 0xd2, 0x23, 0x26, 0xb9, 0x1e, 0xa7, 0x55, 0xe8, 0x38, + 0x53, 0x4b, 0x6e, 0xb6, 0x2f, 0xe2, 0x2b, 0x53, 0x07, 0x69, 0x9c, 0x15, 0x08, 0xac, 0x52, 0x43, + 0xbf, 0x00, 0x68, 0x5e, 0xb7, 0xed, 0xc0, 0x98, 0xeb, 0x33, 0x52, 0x77, 0x52, 0x27, 0x1f, 0x8e, + 0x61, 0xd1, 0xc7, 0x50, 0x0a, 0x4f, 0x14, 0xbd, 0x2c, 0x0d, 0x6c, 0xf2, 0xf4, 0xc1, 0x11, 0x12, + 0x9d, 0xb2, 0x11, 0x4d, 0x6c, 0x76, 0x7d, 0x96, 0xe9, 0x6f, 0x45, 0xfa, 0x6a, 0xe3, 0x88, 0x15, + 0xba, 0x94, 0x31, 0x6d, 0x3e, 0xf4, 0x39, 0x89, 0xb1, 0x99, 0xcd, 0x98, 0xae, 0x42, 0x35, 0x98, + 0x93, 0x0f, 0x28, 0x7d, 0x9e, 0xb1, 0xad, 0x8b, 0xfd, 0xa8, 0x3a, 0xee, 0x70, 0x42, 0x07, 0xed, + 0xc2, 0x34, 0x37, 0x38, 0xba, 0xc6, 0xd4, 0x97, 0xb8, 0xba, 0x6c, 0xb4, 0xb0, 0x40, 0xa1, 0xdf, + 0xc0, 0x12, 0x26, 0x7d, 0xe7, 0x2d, 0xa1, 0xff, 0xfa, 0x84, 0x2e, 0xa0, 0x96, 0x79, 0xd6, 0x23, + 0xfa, 0x02, 0x53, 0x7f, 0x4f, 0xa8, 0xab, 0x30, 0x82, 0x4c, 0xcd, 0x80, 0xf6, 0x61, 0x96, 0x2e, + 0x49, 0xe6, 0x83, 0x1c, 0x58, 0x76, 0x47, 0x47, 0x8c, 0xf2, 0x56, 0x6c, 0x09, 0x87, 0x75, 0x82, + 0x4a, 0xd6, 0x40, 0x4f, 0x41, 0x7b, 0x69, 0x7b, 0xc3, 0x33, 0xaf, 0xed, 0x5a, 0x67, 0x24, 0xe8, + 0xd8, 0x4d, 0xc6, 0x52, 0xe1, 0x2c, 0xc9, 0xea, 0x70, 0x5b, 0x25, 0x2b, 0xe2, 0x6b, 0xb8, 0x66, + 0xfa, 0xa6, 0x58, 0xc3, 0x8b, 0xca, 0x35, 0x1c, 0x43, 0x60, 0x95, 0x1a, 0x67, 0x6b, 0x52, 0x67, + 0x34, 0xbe, 0x23, 0x96, 0x92, 0x6c, 0x49, 0x04, 0x56, 0xa9, 0x51, 0xf3, 0xa8, 0x36, 0xfe, 0xfa, + 0xb2, 0x64, 0x1e, 0xd5, 0x20, 0x9c, 0xa1, 0x4c, 0x69, 0x8f, 0xad, 0xae, 0x6b, 0xfa, 0x84, 0x1a, + 0xa9, 0x43, 0xd7, 0xe9, 0x0b, 0xda, 0x15, 0x89, 0x56, 0x0d, 0xc2, 0x19, 0xca, 0xe8, 0x04, 0x16, + 0x63, 0x35, 0xad, 0xb0, 0xaf, 0xba, 0x34, 0xbf, 0x2a, 0x08, 0x56, 0x2a, 0xa2, 0x33, 0xd0, 0x31, + 0xe9, 0x39, 0x66, 0x67, 0x7f, 0xe8, 0x3b, 0x75, 0xbb, 0xed, 0x92, 0x3e, 0x75, 0xf6, 0xe8, 0x98, + 0xeb, 0xab, 0x8c, 0xf4, 0x6e, 0xb8, 0x0e, 0xd5, 0x30, 0xc1, 0x9f, 0xc9, 0x43, 0x4d, 0x73, 0xd5, + 0xef, 0x61, 0x62, 0x76, 0x88, 0x2b, 0x3a, 0xbc, 0x26, 0x59, 0x90, 0x64, 0x35, 0x4e, 0x29, 0xa0, + 0x63, 0x98, 0x3f, 0x22, 0x3e, 0x26, 0x83, 0x9e, 0xd5, 0x36, 0x83, 0x93, 0xf7, 0x56, 0x72, 0x82, + 0xe2, 0xb5, 0x5c, 0x4f, 0x78, 0xb1, 0x89, 0x5a, 0xba, 0x88, 0x30, 0xf1, 0x88, 0xdf, 0x24, 0x5e, + 0xcc, 0x3c, 0xe8, 0xeb, 0xd2, 0x22, 0x52, 0x20, 0xb0, 0x4a, 0x0d, 0x35, 0x60, 0xe1, 0xc8, 0x39, + 0x36, 0xaf, 0xe8, 0x39, 0xe9, 0x09, 0xae, 0x0d, 0xd9, 0xd8, 0x27, 0xeb, 0x79, 0xcf, 0xd2, 0x8a, + 0x9c, 0x8d, 0xf4, 0x1b, 0x56, 0x64, 0x53, 0xf5, 0x4a, 0x92, 0x4d, 0xae, 0x8f, 0xb1, 0xc9, 0x15, + 0xe8, 0x0b, 0x58, 0x39, 0xb4, 0x7a, 0xa4, 0x49, 0xdc, 0xb7, 0x56, 0x9b, 0xc4, 0xa7, 0x4c, 0xdf, + 0x94, 0xf6, 0x73, 0x06, 0x8a, 0x33, 0x67, 0x91, 0xa0, 0x3e, 0xac, 0x27, 0xab, 0x1e, 0xbf, 0xb5, + 0xda, 0x61, 0xc7, 0xb7, 0x24, 0x6b, 0x96, 0x07, 0xe5, 0x2d, 0xe5, 0xd2, 0xa1, 0x97, 0xb0, 0x78, + 0x4c, 0x7c, 0xb3, 0x63, 0xfa, 0xa6, 0xf4, 0x2d, 0xb7, 0xe5, 0x1d, 0xa0, 0x80, 0x70, 0x7a, 0xa5, + 0x3a, 0x3a, 0x01, 0x74, 0xe4, 0x1c, 0x55, 0x4f, 0x89, 0xdb, 0x26, 0x91, 0x37, 0x63, 0xc8, 0x27, + 0x7f, 0x0a, 0xc0, 0x29, 0x15, 0xaa, 0xd4, 0x95, 0x38, 0x34, 0x87, 0x3d, 0xbf, 0x6e, 0xff, 0x8e, + 0x44, 0x83, 0xf1, 0x9e, 0x44, 0x98, 0x06, 0x60, 0x85, 0x12, 0xfa, 0x2d, 0x2c, 0x57, 0xfd, 0xde, + 0xb1, 0xc3, 0x8c, 0x29, 0x33, 0x60, 0x82, 0xee, 0x8e, 0xb4, 0x03, 0xd4, 0x20, 0xde, 0xc7, 0x0c, + 0x0a, 0xf4, 0x05, 0xac, 0xbe, 0x76, 0xdc, 0x4b, 0x6f, 0x60, 0xb6, 0x49, 0xeb, 0xc2, 0x25, 0xde, + 0x85, 0xd3, 0x13, 0x67, 0x82, 0xfe, 0xbe, 0x74, 0xaa, 0x66, 0xe2, 0x70, 0x36, 0x05, 0xea, 0x43, + 0xa5, 0xea, 0xf7, 0x4e, 0x5d, 0x72, 0x4e, 0xfc, 0xf6, 0xc5, 0x89, 0xdd, 0x14, 0x47, 0x43, 0xd8, + 0xc8, 0x5d, 0xd6, 0xc8, 0xfb, 0xd1, 0x47, 0xe4, 0x80, 0xf1, 0x08, 0x32, 0xf4, 0x5b, 0xd0, 0xeb, + 0xcd, 0xea, 0x69, 0xcd, 0x35, 0x2d, 0xbb, 0xea, 0xd8, 0xde, 0xb0, 0x1f, 0xd9, 0x9c, 0x7b, 0xac, + 0xa1, 0x4d, 0xde, 0x50, 0x16, 0x0c, 0x67, 0x12, 0xa0, 0x01, 0x6c, 0xd4, 0xdb, 0xe4, 0x8c, 0xb8, + 0x5d, 0xee, 0x58, 0xbd, 0x35, 0x7b, 0x56, 0xc7, 0xf4, 0xc3, 0x45, 0xb8, 0xcd, 0x5a, 0xb8, 0x23, + 0x5a, 0xc8, 0xc3, 0xf2, 0x69, 0xc9, 0x27, 0x44, 0x5d, 0x58, 0x3b, 0x76, 0xec, 0xae, 0x53, 0x3b, + 0xa8, 0xf6, 0x2c, 0xb6, 0xba, 0x7c, 0xcb, 0x0d, 0x9b, 0xbb, 0xcf, 0x9a, 0xbb, 0x2d, 0xd6, 0x7c, + 0x26, 0x90, 0xb7, 0x95, 0x43, 0x65, 0x1c, 0xc2, 0x4a, 0xca, 0xd1, 0xe7, 0x91, 0xce, 0x43, 0x28, + 0x72, 0x73, 0xe7, 0xe9, 0x85, 0xad, 0xf1, 0xed, 0x99, 0xbd, 0xf9, 0x1d, 0x7e, 0x81, 0x24, 0xcc, + 0x60, 0x08, 0x30, 0xfe, 0xb8, 0x06, 0xc5, 0x50, 0xf3, 0xc7, 0x8d, 0x80, 0x16, 0x61, 0xf2, 0xb1, + 0xeb, 0x3a, 0x2e, 0x0b, 0x7d, 0xca, 0x38, 0x28, 0xa0, 0x37, 0x99, 0x1d, 0xe7, 0xf1, 0x4d, 0x25, + 0x2b, 0xbe, 0x09, 0x50, 0x38, 0xf3, 0xbb, 0x4f, 0x60, 0x51, 0x0e, 0x55, 0x38, 0xed, 0xa4, 0x64, + 0x69, 0x54, 0x10, 0xac, 0x54, 0xa4, 0xe7, 0x60, 0x14, 0xb5, 0x70, 0xb2, 0x29, 0xe9, 0x1c, 0x4c, + 0x56, 0xe3, 0x94, 0x02, 0x8d, 0x2b, 0x62, 0x61, 0x0b, 0x67, 0x99, 0x96, 0x0e, 0x87, 0x54, 0x3d, + 0x4e, 0xab, 0x70, 0x2f, 0x2a, 0x8a, 0x5a, 0x38, 0x53, 0x31, 0xe9, 0x45, 0x25, 0x11, 0x58, 0xa5, + 0xc6, 0x03, 0xa7, 0x30, 0x74, 0xe1, 0x64, 0xa5, 0x64, 0xe0, 0x94, 0x00, 0x60, 0x85, 0x12, 0x1d, + 0x76, 0x39, 0x72, 0xe1, 0x64, 0x90, 0x74, 0x61, 0x53, 0x10, 0xac, 0x54, 0x44, 0xbf, 0xa4, 0x31, + 0x8f, 0x08, 0x6d, 0x78, 0xcc, 0xb3, 0xaa, 0x88, 0x79, 0x38, 0x49, 0x0c, 0x8c, 0x1e, 0xa5, 0x83, + 0x1e, 0x3d, 0x1d, 0xf4, 0x70, 0xc5, 0x58, 0xd4, 0xf3, 0x22, 0x27, 0xea, 0xb9, 0x9d, 0x13, 0xf5, + 0xc4, 0x86, 0x25, 0x19, 0xa4, 0xbc, 0xc8, 0x09, 0x7b, 0x6e, 0xe7, 0x84, 0x3d, 0x82, 0x52, 0x11, + 0xf7, 0x3c, 0xce, 0x88, 0x7b, 0x36, 0x32, 0xe2, 0x1e, 0x4e, 0x95, 0x0c, 0x7c, 0x3e, 0x4c, 0x06, + 0x3e, 0xcb, 0xc9, 0xc0, 0x87, 0x2b, 0x86, 0x91, 0xcf, 0xe7, 0xf9, 0x91, 0xcf, 0x9d, 0xfc, 0xc8, + 0x87, 0xb3, 0x65, 0x84, 0x3e, 0x07, 0xea, 0xd0, 0x67, 0x5d, 0x1d, 0xfa, 0x70, 0xae, 0x44, 0xec, + 0xf3, 0x2c, 0x33, 0xf6, 0xd9, 0xcc, 0x8c, 0x7d, 0xc4, 0x86, 0x4d, 0x05, 0x3f, 0xb1, 0xf5, 0x1c, + 0x44, 0x31, 0x7c, 0x3d, 0x2f, 0x2a, 0xd7, 0x73, 0x1c, 0x82, 0x95, 0x8a, 0x9c, 0x30, 0x16, 0xc8, + 0x70, 0xc2, 0xa5, 0x24, 0x61, 0x0a, 0x82, 0x95, 0x8a, 0xd4, 0x84, 0x66, 0xdc, 0x72, 0xf1, 0x18, + 0xa8, 0x92, 0x15, 0x03, 0x09, 0x13, 0x9a, 0x75, 0x49, 0xf6, 0x06, 0x56, 0x52, 0x81, 0x0c, 0x67, + 0x5e, 0x91, 0x98, 0x33, 0x50, 0x38, 0x4b, 0x1d, 0x61, 0x58, 0x4a, 0xc4, 0x33, 0x9c, 0x57, 0x97, + 0xa6, 0x5b, 0x89, 0xc1, 0x6a, 0x55, 0xd4, 0x1e, 0x19, 0x0b, 0xdd, 0x1b, 0x19, 0x0b, 0xf1, 0x16, + 0xb2, 0x83, 0xa1, 0x43, 0x58, 0x88, 0xc5, 0x36, 0xbc, 0xd3, 0x6b, 0x92, 0x69, 0x49, 0xd5, 0xe3, + 0xb4, 0x0a, 0x7a, 0x9e, 0x15, 0x0f, 0x55, 0xb2, 0xe2, 0xa1, 0x40, 0x31, 0x2b, 0x20, 0x3a, 0x81, + 0x45, 0x39, 0xb2, 0xe1, 0x5d, 0x5b, 0x97, 0x56, 0x95, 0x0a, 0x82, 0x95, 0x8a, 0x81, 0x47, 0x1d, + 0x85, 0x36, 0x9c, 0x6e, 0x23, 0xe1, 0x51, 0x27, 0x01, 0x91, 0x47, 0x9d, 0xac, 0xe1, 0x84, 0x61, + 0x74, 0xc3, 0x09, 0x2b, 0x49, 0xc2, 0x04, 0x20, 0x46, 0x98, 0xa8, 0x41, 0x26, 0xe8, 0xe9, 0xa0, + 0x86, 0xd3, 0x6e, 0x4a, 0xdb, 0x3d, 0x0b, 0xc6, 0xc9, 0x33, 0x69, 0xa8, 0xc7, 0x98, 0x11, 0xcd, + 0xf0, 0x76, 0xb6, 0x24, 0x8b, 0x97, 0x8b, 0x15, 0x1e, 0x63, 0x2e, 0x08, 0xbd, 0x81, 0xa5, 0x44, + 0x80, 0xc3, 0x5b, 0xba, 0x2d, 0x6f, 0x0c, 0x15, 0x86, 0xb7, 0xa0, 0x26, 0x40, 0x18, 0x6e, 0x4a, + 0x71, 0x0e, 0xe7, 0x35, 0x64, 0x8f, 0x21, 0x8d, 0xe0, 0xac, 0x2a, 0x65, 0xea, 0x85, 0x48, 0x01, + 0x0f, 0xe7, 0x7c, 0x4f, 0xe2, 0x54, 0x20, 0xb0, 0x4a, 0x8d, 0x86, 0xba, 0xa9, 0x28, 0x87, 0x33, + 0xde, 0x91, 0xf6, 0x46, 0x06, 0x4a, 0x84, 0xba, 0x19, 0xd5, 0xc8, 0x84, 0x35, 0x55, 0xa0, 0xc3, + 0x9b, 0x78, 0x5f, 0x3a, 0x8b, 0xb3, 0x81, 0x38, 0x87, 0x24, 0xb8, 0xe0, 0xb1, 0xc3, 0x24, 0x5c, + 0x48, 0x7e, 0x37, 0x71, 0xc1, 0x93, 0x86, 0x60, 0xa5, 0x22, 0x1a, 0xc0, 0x66, 0x66, 0xc8, 0xc4, + 0xb9, 0xef, 0x49, 0xf7, 0x3c, 0x23, 0xd0, 0x78, 0x14, 0x1d, 0x8d, 0x28, 0x15, 0x11, 0x14, 0x6f, + 0x6b, 0x5b, 0x8a, 0x28, 0x33, 0x71, 0x38, 0x9b, 0x02, 0x79, 0x50, 0xc9, 0x0a, 0x9a, 0x78, 0x23, + 0xf7, 0xa5, 0x88, 0x32, 0x1f, 0xcc, 0xe7, 0x7c, 0x04, 0x25, 0xfa, 0x1d, 0xdc, 0x52, 0x46, 0x4f, + 0xbc, 0xc5, 0x07, 0xac, 0x45, 0x23, 0x2f, 0x12, 0x93, 0x9a, 0xcb, 0x23, 0x33, 0xea, 0xca, 0xd4, + 0x0a, 0xcb, 0x76, 0xb1, 0x34, 0x75, 0xbd, 0xc3, 0x93, 0x4e, 0x61, 0x19, 0x2d, 0xc3, 0x54, 0x93, + 0x85, 0x64, 0x2c, 0x38, 0x2a, 0x61, 0x5e, 0x32, 0x7e, 0xa5, 0x8e, 0x61, 0x90, 0x01, 0x65, 0x93, + 0xca, 0x9b, 0xc3, 0x36, 0x8d, 0x7b, 0x18, 0x5f, 0x11, 0x4b, 0x32, 0xa3, 0x9e, 0xca, 0xc9, 0xd0, + 0x80, 0x8e, 0x33, 0xf1, 0x80, 0x6e, 0x1c, 0x47, 0x82, 0x78, 0xea, 0x6e, 0x8c, 0x05, 0x7b, 0xb1, + 0xd4, 0x5d, 0x3a, 0x90, 0xd1, 0x61, 0x5a, 0x6e, 0x5d, 0x14, 0x8d, 0x46, 0xfa, 0xbe, 0x10, 0x69, + 0x30, 0x5e, 0xed, 0x77, 0x78, 0xe2, 0x8e, 0xfe, 0x64, 0x92, 0xf3, 0x2e, 0x6b, 0x89, 0x4a, 0xce, + 0xbb, 0x2c, 0x40, 0xbc, 0xf2, 0x5d, 0x33, 0x0c, 0x10, 0x69, 0xc1, 0xb8, 0xa7, 0x38, 0x70, 0x11, + 0x82, 0x09, 0xfa, 0x9b, 0xf3, 0xb1, 0xdf, 0xc6, 0xa7, 0xa3, 0x6e, 0x2a, 0xe8, 0x0c, 0x9c, 0x9a, + 0xbe, 0x4f, 0x5c, 0x1e, 0x09, 0x97, 0x70, 0x58, 0x36, 0x3e, 0x1e, 0xb9, 0xcf, 0x94, 0x8d, 0xfe, + 0x67, 0x21, 0xfb, 0xc2, 0x22, 0x3d, 0xdc, 0xb3, 0x89, 0xe1, 0x66, 0x36, 0xaa, 0x5e, 0x13, 0xc3, + 0xcd, 0x8b, 0xb4, 0xe6, 0xa9, 0x73, 0xf6, 0xdc, 0xec, 0x13, 0xbe, 0x1c, 0x44, 0x91, 0x0e, 0xd1, + 0x53, 0xe7, 0xac, 0x5e, 0x63, 0xb1, 0xf1, 0x04, 0x0e, 0x0a, 0x68, 0x1b, 0xe6, 0x03, 0x67, 0x9a, + 0x65, 0xac, 0x4f, 0xec, 0xde, 0x35, 0x0b, 0x72, 0x8b, 0x38, 0x29, 0x46, 0x77, 0x61, 0x0e, 0x13, + 0x9b, 0x7c, 0x15, 0x01, 0xa7, 0x18, 0x30, 0x21, 0x35, 0x3e, 0xce, 0xb1, 0x01, 0x39, 0x33, 0xff, + 0x26, 0x9d, 0xc4, 0x53, 0xcc, 0xfc, 0x22, 0x4c, 0x52, 0x80, 0xc7, 0xe7, 0x3e, 0x28, 0xd0, 0xc1, + 0x0a, 0xcd, 0x29, 0xfb, 0xec, 0x71, 0x1c, 0x09, 0xe8, 0x2a, 0x48, 0xc7, 0xc0, 0xaa, 0x09, 0x59, + 0x54, 0xa5, 0x00, 0x8d, 0xbf, 0x14, 0xa0, 0x28, 0x64, 0xd1, 0xc0, 0x77, 0xf8, 0xa5, 0x86, 0x28, + 0x52, 0xc2, 0x67, 0xe4, 0x3a, 0x48, 0xcc, 0x97, 0x31, 0xfb, 0x8d, 0x1e, 0x04, 0x9a, 0xc7, 0x4e, + 0x27, 0x98, 0x8d, 0xb9, 0xbd, 0xb9, 0x1d, 0xf6, 0xe2, 0x46, 0x48, 0x71, 0x58, 0x8f, 0xb6, 0x60, + 0xc6, 0xf2, 0xb0, 0x69, 0x77, 0x59, 0x40, 0xc3, 0x26, 0xa9, 0x88, 0xe3, 0x22, 0x74, 0x0f, 0xa6, + 0x9f, 0x38, 0xbd, 0x0e, 0x71, 0x3d, 0x7d, 0x92, 0xdd, 0xc5, 0xcc, 0x06, 0x64, 0xaf, 0x4d, 0x8b, + 0x46, 0xd2, 0x58, 0xd4, 0x52, 0x20, 0x95, 0x51, 0xe0, 0x94, 0x12, 0xc8, 0x6b, 0x8d, 0x2f, 0x94, + 0x17, 0x01, 0xf4, 0x53, 0xaa, 0x76, 0x5d, 0x8c, 0x3b, 0xfb, 0x8d, 0x7e, 0x0e, 0x65, 0x81, 0x6b, + 0x58, 0x9e, 0xcf, 0x3e, 0x73, 0x66, 0x6f, 0x9e, 0x5b, 0xbd, 0x90, 0x42, 0x02, 0x19, 0x37, 0x15, + 0x89, 0x50, 0xe3, 0x02, 0x66, 0x5a, 0x57, 0xf6, 0xbb, 0x8d, 0x28, 0x76, 0xbe, 0x0a, 0x47, 0x94, + 0xfe, 0x46, 0x0f, 0x61, 0xfa, 0x64, 0xe0, 0xb3, 0xfb, 0xa8, 0x20, 0x0b, 0xbe, 0x10, 0x0d, 0x28, + 0xaf, 0xc0, 0x02, 0x61, 0xfc, 0x7b, 0x01, 0xa6, 0x79, 0xe3, 0xe8, 0x33, 0x28, 0x56, 0x5d, 0x62, + 0xfa, 0x64, 0xdf, 0xe7, 0x2f, 0x5f, 0xd6, 0x76, 0x82, 0x87, 0x48, 0x3b, 0xe2, 0x21, 0x52, 0xec, + 0xfd, 0x4b, 0x91, 0x5a, 0xea, 0xdf, 0xff, 0xcf, 0x66, 0x01, 0x87, 0x5a, 0x68, 0x0b, 0x26, 0xa8, + 0x73, 0xc4, 0x56, 0xde, 0xcc, 0x5e, 0x79, 0xc7, 0xbf, 0xb2, 0x77, 0x5a, 0x57, 0x36, 0x95, 0x61, + 0x56, 0x43, 0x3f, 0xe5, 0xa5, 0x47, 0xdc, 0xd6, 0x95, 0xcd, 0x5f, 0x87, 0x88, 0x22, 0xfa, 0x10, + 0x4a, 0x74, 0xcc, 0x69, 0x2f, 0x3d, 0x7d, 0x82, 0x0d, 0x1d, 0x12, 0x37, 0x36, 0xd1, 0x58, 0xe0, + 0x08, 0x64, 0x7c, 0xae, 0xba, 0x55, 0x51, 0xce, 0xcc, 0x87, 0x6c, 0x3c, 0x13, 0x13, 0x33, 0x17, + 0xb1, 0x33, 0x82, 0x38, 0xc4, 0x58, 0x52, 0xe6, 0x95, 0x8d, 0x7f, 0x29, 0x40, 0x29, 0x14, 0x52, + 0x83, 0xf7, 0xdc, 0xe9, 0x90, 0xd6, 0xf5, 0x80, 0xf0, 0xe6, 0xc2, 0x32, 0x3d, 0x72, 0xe8, 0xef, + 0x7a, 0x87, 0x6f, 0x43, 0x5e, 0xa2, 0xfb, 0x90, 0x11, 0x30, 0xa5, 0xc0, 0xfc, 0x44, 0x02, 0xda, + 0xf9, 0x97, 0x1e, 0xe9, 0x70, 0xfb, 0xc3, 0x7e, 0x53, 0xd9, 0xa1, 0x4b, 0x82, 0x8b, 0xb5, 0x09, + 0xcc, 0x7e, 0xd3, 0x96, 0x9f, 0x58, 0x3e, 0x36, 0x7d, 0xcb, 0x61, 0x26, 0x66, 0x0c, 0x87, 0x65, + 0xe3, 0xb9, 0xfa, 0x86, 0x08, 0x3d, 0x82, 0xd9, 0x50, 0xc8, 0x86, 0x21, 0xb8, 0xad, 0x0c, 0x2f, + 0x15, 0x43, 0x05, 0x19, 0x66, 0xf4, 0x60, 0x3d, 0x2f, 0xc9, 0x4a, 0xa7, 0xf4, 0xc8, 0x75, 0x86, + 0x83, 0xd0, 0x08, 0x8b, 0x62, 0xbe, 0x09, 0x16, 0x67, 0xe1, 0xb8, 0x7c, 0x16, 0x7e, 0x0c, 0x1b, + 0xb9, 0x17, 0x1b, 0xf2, 0xcb, 0x92, 0x49, 0xf1, 0xb2, 0xe4, 0x29, 0xfb, 0xe8, 0x54, 0xda, 0xf6, + 0xfb, 0x74, 0xce, 0x78, 0x08, 0x4b, 0xca, 0x7b, 0x10, 0x3a, 0x13, 0xec, 0xce, 0x84, 0x2f, 0x2d, + 0xfa, 0xdb, 0x68, 0xc2, 0x4a, 0x46, 0xa6, 0x17, 0x55, 0x00, 0x6a, 0xa6, 0x6f, 0x9e, 0x99, 0x1e, + 0x09, 0x2f, 0x78, 0x63, 0x92, 0x9c, 0x1e, 0x7c, 0x04, 0x7a, 0xd6, 0x15, 0x4a, 0xce, 0xf1, 0x70, + 0x08, 0x45, 0x36, 0x73, 0xcf, 0xc8, 0x35, 0xed, 0xea, 0xa9, 0xe9, 0x5f, 0x88, 0xae, 0xd2, 0xdf, + 0x74, 0x49, 0x9e, 0x9c, 0x9f, 0x7b, 0x24, 0x78, 0xd9, 0x37, 0x8e, 0x79, 0x09, 0xcd, 0xc1, 0x58, + 0xf3, 0x6b, 0x7e, 0x26, 0x8c, 0x35, 0xbf, 0x36, 0x1e, 0xf1, 0x25, 0xca, 0xec, 0xf3, 0x7d, 0x98, + 0xb8, 0xa4, 0x36, 0xbb, 0x20, 0x19, 0x33, 0x51, 0xcf, 0xfd, 0x35, 0x06, 0x31, 0x5a, 0xf4, 0x9c, + 0x64, 0x9f, 0x1e, 0x76, 0x63, 0x11, 0x26, 0xeb, 0x76, 0x87, 0x5c, 0x89, 0xc9, 0x62, 0x05, 0xf4, + 0x30, 0xea, 0x28, 0x37, 0x15, 0x49, 0x5e, 0x1c, 0x02, 0x8c, 0xd7, 0xca, 0xec, 0x38, 0xfa, 0x2c, + 0xd5, 0x18, 0xef, 0x62, 0x78, 0xbd, 0x26, 0xd7, 0xe2, 0x24, 0xdc, 0x38, 0x81, 0x05, 0x31, 0xa8, + 0x21, 0x7b, 0x46, 0x87, 0x35, 0x18, 0x7f, 0x62, 0x89, 0x07, 0x91, 0xf4, 0x27, 0x1d, 0x5f, 0x8a, + 0xe7, 0xbe, 0x14, 0xfb, 0x6d, 0x7c, 0xa1, 0xbe, 0xca, 0x42, 0x87, 0x8a, 0x86, 0x78, 0x67, 0xf5, + 0xe8, 0xe2, 0x40, 0xae, 0xc7, 0x69, 0x15, 0x03, 0x2b, 0x33, 0xfb, 0xe8, 0xd7, 0x50, 0x0e, 0x65, + 0xc1, 0x30, 0x04, 0x77, 0xe6, 0xd1, 0x7b, 0xd4, 0x78, 0x35, 0x96, 0xc0, 0x7c, 0xdf, 0xa4, 0x2f, + 0xbd, 0xf6, 0xa0, 0x14, 0x0a, 0xc3, 0x67, 0x90, 0x0a, 0x46, 0x1c, 0xc1, 0x8c, 0x26, 0xcc, 0xf0, + 0xb7, 0x81, 0x4d, 0xbf, 0xcf, 0x86, 0x88, 0xf9, 0x58, 0x7c, 0x09, 0x32, 0x07, 0x4b, 0x83, 0xf1, + 0xe6, 0x8b, 0x86, 0xf0, 0x4a, 0x9b, 0x2f, 0x1a, 0x74, 0x93, 0x9c, 0x9a, 0xae, 0xd9, 0xa7, 0xe6, + 0xcf, 0xe3, 0xc3, 0x19, 0x93, 0x18, 0x17, 0xa0, 0xf1, 0xeb, 0xa8, 0x16, 0xe9, 0x0f, 0x82, 0x3b, + 0xc3, 0x35, 0x28, 0x8a, 0x6d, 0x24, 0xec, 0xae, 0x28, 0xd3, 0x09, 0xdc, 0xef, 0x59, 0x66, 0xe8, + 0xfd, 0xb0, 0x02, 0x75, 0xe8, 0x4f, 0x2f, 0xae, 0x3d, 0xab, 0x6d, 0xf6, 0x62, 0x7e, 0x9f, 0x24, + 0x33, 0xfe, 0xad, 0x90, 0xf5, 0x28, 0x81, 0xee, 0x1c, 0x2a, 0x0a, 0x8d, 0x08, 0x2f, 0xa1, 0x8f, + 0xc2, 0x37, 0x9d, 0x41, 0x82, 0x66, 0x5d, 0x7d, 0x5f, 0x17, 0x60, 0xc4, 0x8b, 0x4f, 0xf4, 0x19, + 0xdc, 0x0a, 0xbf, 0x25, 0x40, 0x5a, 0x8e, 0xdd, 0x1c, 0x0e, 0x06, 0x8e, 0xeb, 0x73, 0xdb, 0x5f, + 0xc4, 0x79, 0x90, 0xa7, 0x13, 0xc5, 0x71, 0x6d, 0xc2, 0xf8, 0xbf, 0xe9, 0xcc, 0xfb, 0x43, 0xba, + 0xa7, 0x6b, 0x07, 0x7c, 0x70, 0xc6, 0x6a, 0x07, 0xe8, 0x11, 0x94, 0x63, 0x73, 0xe3, 0xf1, 0x23, + 0x50, 0x1c, 0xb0, 0xb1, 0x2a, 0x2c, 0xe1, 0xd0, 0x03, 0xd0, 0x1a, 0xa6, 0xe7, 0xef, 0x9f, 0x9f, + 0x93, 0xb6, 0x4f, 0x3a, 0xcc, 0xd9, 0x08, 0x2c, 0x45, 0x4a, 0x8e, 0x3e, 0x85, 0x39, 0x7a, 0x98, + 0x37, 0xc8, 0x5b, 0xd2, 0x8b, 0x1f, 0xe3, 0x8b, 0xe2, 0x56, 0x38, 0x5e, 0x89, 0x13, 0x58, 0x54, + 0x83, 0x0d, 0x19, 0x40, 0x7a, 0xc4, 0xf4, 0x48, 0x34, 0x2e, 0x81, 0xcf, 0x9d, 0x0f, 0x42, 0x87, + 0x30, 0x4f, 0x01, 0x35, 0x72, 0x6e, 0xd9, 0xa4, 0xf3, 0xca, 0x0c, 0xfd, 0xbb, 0xc4, 0xd4, 0xc8, + 0x20, 0x9c, 0x54, 0x42, 0xbf, 0x80, 0x95, 0x84, 0xe8, 0xf1, 0x15, 0xef, 0xc7, 0x34, 0xeb, 0x47, + 0x56, 0x35, 0xed, 0x41, 0xf3, 0xda, 0xf3, 0x49, 0xff, 0x95, 0xe9, 0x5a, 0x74, 0xfe, 0x3c, 0xbd, + 0xa8, 0xea, 0x81, 0x0c, 0xc2, 0x49, 0x25, 0xda, 0x83, 0x84, 0x28, 0xec, 0x41, 0x29, 0xe8, 0x41, + 0x46, 0x35, 0xfa, 0x14, 0x56, 0x13, 0x9d, 0xc3, 0x64, 0xd0, 0x33, 0xaf, 0xd9, 0x45, 0x3d, 0x30, + 0xdd, 0x6c, 0x00, 0xd5, 0x4e, 0x10, 0xc7, 0xb4, 0x67, 0x02, 0xed, 0x4c, 0x00, 0x7a, 0x02, 0x9b, + 0x89, 0xca, 0xa6, 0x6d, 0x0e, 0xbc, 0x0b, 0xc7, 0x6f, 0x39, 0x4e, 0xc3, 0x74, 0xbb, 0x84, 0x25, + 0x8a, 0x8a, 0x78, 0x14, 0x8c, 0x32, 0x25, 0x3a, 0x99, 0x62, 0x9a, 0x0d, 0x98, 0x46, 0xc0, 0xe8, + 0x7e, 0x13, 0x6f, 0x96, 0xe9, 0xa2, 0x6e, 0x38, 0x76, 0x97, 0x9a, 0x8b, 0xea, 0x05, 0x69, 0x5f, + 0x92, 0x0e, 0x4b, 0x12, 0x15, 0x71, 0x1e, 0x84, 0x3a, 0x6d, 0x87, 0xce, 0xd0, 0x0e, 0x96, 0xff, + 0x7c, 0x90, 0xa9, 0x0d, 0x05, 0xe8, 0x13, 0x80, 0x70, 0xb3, 0x7a, 0xba, 0xc6, 0x26, 0x7b, 0x45, + 0x9e, 0xec, 0xb0, 0x1e, 0xc7, 0xa0, 0xe8, 0x11, 0x2c, 0x87, 0x25, 0x6a, 0x46, 0x49, 0x38, 0xc3, + 0x0b, 0xac, 0x4f, 0x19, 0xb5, 0xc6, 0x7f, 0x4d, 0xaa, 0xdf, 0x39, 0x65, 0xda, 0xa9, 0xc0, 0x1a, + 0x8c, 0x85, 0xd6, 0x60, 0x0b, 0x66, 0x9a, 0xc4, 0x7f, 0x65, 0xba, 0x81, 0x31, 0x18, 0x67, 0xc1, + 0x7a, 0x5c, 0x94, 0xb2, 0x17, 0x13, 0x3f, 0xc0, 0x5e, 0x4c, 0xbe, 0xb3, 0xbd, 0x98, 0xfa, 0x0e, + 0xf6, 0x42, 0xb1, 0xd3, 0xa7, 0x7f, 0xe4, 0x9d, 0x5e, 0xfc, 0xce, 0x3b, 0xbd, 0xf4, 0x23, 0xef, + 0x74, 0xf8, 0x01, 0x3b, 0x7d, 0xe6, 0x07, 0xed, 0xf4, 0xf2, 0xa8, 0x9d, 0x2e, 0xed, 0x89, 0xd9, + 0xfc, 0x3d, 0x31, 0xf7, 0xce, 0x7b, 0xc2, 0xf8, 0x63, 0x21, 0x4c, 0x5d, 0xc9, 0x3d, 0x57, 0xfa, + 0x13, 0x5b, 0x30, 0xf9, 0xca, 0xec, 0x0d, 0x09, 0x77, 0x23, 0x61, 0x87, 0xfd, 0xcd, 0xcd, 0xe3, + 0xab, 0x81, 0x8b, 0x83, 0x0a, 0xe6, 0x71, 0x7c, 0xd9, 0xe3, 0x07, 0x3e, 0xfd, 0xc9, 0x5c, 0x3c, + 0xef, 0xc0, 0xb2, 0xf9, 0x41, 0x1b, 0x14, 0xe8, 0xc2, 0xe5, 0x0b, 0x99, 0x39, 0x1f, 0xcf, 0xa8, + 0x9f, 0x3f, 0xc9, 0x36, 0x51, 0x4a, 0x8e, 0x2a, 0x30, 0xc1, 0xc2, 0xb7, 0xa9, 0x78, 0xa3, 0x54, + 0x82, 0x99, 0xdc, 0x78, 0x01, 0xb3, 0xd2, 0x62, 0x55, 0x76, 0x3d, 0x8c, 0x63, 0x02, 0xdf, 0x3f, + 0x28, 0xb0, 0x3f, 0xcc, 0xb9, 0xb2, 0xeb, 0xb5, 0x60, 0x53, 0x96, 0x31, 0x2f, 0x19, 0x7f, 0x93, + 0x91, 0xd0, 0xcb, 0x09, 0x07, 0x7e, 0x0d, 0x9b, 0x23, 0x1e, 0x25, 0xc6, 0x23, 0x90, 0x82, 0x1c, + 0x81, 0x18, 0xb0, 0x35, 0x2a, 0x8b, 0x67, 0x6c, 0xb3, 0xb7, 0xa1, 0x8a, 0x57, 0x85, 0xd4, 0xde, + 0x54, 0x9f, 0x0b, 0xef, 0xa3, 0xfa, 0x9c, 0xff, 0xa1, 0x80, 0x2a, 0xdf, 0x96, 0xf1, 0x87, 0x02, + 0x1f, 0x28, 0xdf, 0x1f, 0x66, 0xd9, 0x37, 0xe3, 0x54, 0x9d, 0x9d, 0xcb, 0x1e, 0x1c, 0xea, 0x76, + 0xee, 0x0f, 0xfd, 0x8b, 0xa6, 0xef, 0x5a, 0x76, 0x70, 0x4b, 0x5a, 0xc6, 0x31, 0x89, 0xb1, 0xab, + 0x78, 0xb2, 0x48, 0xfd, 0x4e, 0x21, 0x12, 0x7f, 0x50, 0x21, 0xca, 0xc6, 0x87, 0xaa, 0x7c, 0x5e, + 0xae, 0xc6, 0x2f, 0x15, 0xef, 0x18, 0xd1, 0x1d, 0x98, 0x15, 0xa2, 0x83, 0x6b, 0x9f, 0x78, 0x7c, + 0x58, 0x64, 0xa1, 0xf1, 0x2b, 0x55, 0xae, 0xef, 0x1d, 0x75, 0x2f, 0x32, 0x1f, 0x3c, 0xa2, 0x5d, + 0xbe, 0xae, 0x0b, 0xcc, 0x99, 0xbd, 0x95, 0x91, 0x75, 0x8b, 0x16, 0x7a, 0x78, 0x99, 0xd1, 0xb4, + 0xbe, 0x26, 0x3c, 0xa8, 0x8c, 0x04, 0xc6, 0x55, 0x76, 0x02, 0x51, 0xd6, 0x2c, 0x24, 0x34, 0xe9, + 0x97, 0xb0, 0x42, 0xd5, 0x1c, 0x98, 0x6d, 0xcb, 0xbf, 0xe6, 0xdc, 0xb2, 0x90, 0xce, 0xee, 0x31, + 0xf1, 0x3c, 0xb3, 0x1b, 0xde, 0xe3, 0xf2, 0xa2, 0x71, 0x92, 0xff, 0xe8, 0xf2, 0x3b, 0x7f, 0xa8, + 0xf1, 0xf7, 0x23, 0x12, 0x95, 0x3f, 0xf1, 0xf7, 0x7c, 0xa4, 0x7e, 0xd5, 0x99, 0xdf, 0xaa, 0xf1, + 0xb7, 0x19, 0xb9, 0xce, 0x74, 0x77, 0x0a, 0x8a, 0xee, 0x18, 0xff, 0x5f, 0x18, 0xf1, 0x9e, 0x6f, + 0xc4, 0x05, 0x3c, 0xeb, 0x9c, 0x6f, 0xf6, 0x9c, 0x6e, 0x78, 0xc1, 0x11, 0x09, 0x68, 0x2d, 0x35, + 0x83, 0x2c, 0xcf, 0x27, 0xee, 0xc1, 0x42, 0x01, 0xb5, 0x0a, 0xc1, 0xbb, 0x92, 0xe0, 0x8f, 0xa6, + 0x82, 0x02, 0xdd, 0xb4, 0xc2, 0xb9, 0xab, 0xd7, 0xb8, 0x5b, 0x11, 0x93, 0xa0, 0xbd, 0x68, 0x98, + 0x1a, 0x4e, 0x9b, 0xc5, 0x4c, 0x4f, 0x4c, 0xef, 0x82, 0xd9, 0xe9, 0x12, 0x56, 0xd6, 0xd1, 0x1d, + 0x1a, 0xbc, 0x4a, 0xaa, 0xd7, 0x98, 0x67, 0x5f, 0xc2, 0x61, 0xd9, 0x78, 0x32, 0x2a, 0x95, 0x16, + 0x5c, 0xf8, 0xf7, 0x9d, 0xb7, 0xa4, 0xf3, 0xd8, 0xf6, 0x5d, 0x2b, 0xdc, 0x73, 0x09, 0xa9, 0xb1, + 0xa3, 0x7a, 0x3f, 0x4b, 0x27, 0x9c, 0x4b, 0xb8, 0x71, 0x10, 0x45, 0x63, 0x57, 0x99, 0x4c, 0xce, + 0x51, 0x68, 0xa8, 0xde, 0xd3, 0x52, 0x7b, 0xc9, 0x1f, 0x10, 0xf2, 0x3f, 0xe9, 0xe2, 0x6f, 0x05, + 0x45, 0xd0, 0x4d, 0xd8, 0x05, 0x78, 0xe0, 0x17, 0xc6, 0x24, 0xc6, 0x7d, 0x65, 0xde, 0x59, 0x99, + 0x10, 0x78, 0x90, 0xf5, 0xfa, 0x36, 0x9d, 0x99, 0x30, 0x3e, 0xc8, 0x4c, 0x40, 0x2b, 0xa9, 0xfb, + 0x39, 0x6f, 0x6f, 0xd1, 0x36, 0xcc, 0xf3, 0xbf, 0xb9, 0x0c, 0xb3, 0x1a, 0xc1, 0x11, 0x96, 0x14, + 0xd3, 0x39, 0x7a, 0xed, 0x5a, 0x7e, 0x44, 0xc1, 0x17, 0x63, 0x42, 0x6a, 0xd8, 0x79, 0xe9, 0xeb, + 0x9f, 0xa0, 0xbd, 0x57, 0xea, 0x5c, 0x36, 0xfa, 0x3b, 0x28, 0xc7, 0xe5, 0xef, 0xf0, 0x47, 0xa8, + 0x12, 0xde, 0xf8, 0x87, 0x42, 0xde, 0xab, 0xd8, 0x11, 0x9b, 0xd6, 0x80, 0x32, 0x3d, 0x53, 0x09, + 0xbb, 0xa9, 0x08, 0xf7, 0xad, 0x24, 0xa3, 0xae, 0x12, 0xbf, 0xad, 0x7d, 0x7c, 0xd5, 0xee, 0x0d, + 0x3d, 0xeb, 0x2d, 0xe1, 0xb7, 0xb8, 0x29, 0xb9, 0xf1, 0x49, 0x6e, 0x62, 0x38, 0xc7, 0x7b, 0xf9, + 0xd7, 0xc8, 0x0f, 0x94, 0x7d, 0xd0, 0xef, 0xe9, 0x07, 0x6e, 0xc3, 0xfc, 0x73, 0x72, 0xe5, 0xb7, + 0x5c, 0xd3, 0xf6, 0xcc, 0x76, 0xf8, 0x87, 0x9d, 0x45, 0x9c, 0x14, 0xa3, 0x1d, 0x28, 0xe3, 0xa1, + 0x4d, 0x47, 0x3b, 0xa0, 0x9c, 0x48, 0x51, 0x4a, 0xf5, 0x0f, 0xfe, 0x30, 0x15, 0x7b, 0xa9, 0x8b, + 0x4a, 0xfc, 0x2f, 0xb1, 0xb5, 0x1b, 0xe8, 0x26, 0xcc, 0x27, 0x1e, 0xcf, 0x6a, 0x05, 0xa4, 0x41, + 0x39, 0x9e, 0x72, 0xd6, 0xc6, 0x50, 0x19, 0x8a, 0x22, 0xfb, 0xab, 0x8d, 0xa3, 0x59, 0x28, 0x85, + 0x99, 0x38, 0x6d, 0x02, 0xcd, 0xc3, 0x4c, 0x2c, 0xfd, 0xa4, 0x4d, 0xa2, 0x39, 0x80, 0x28, 0xe9, + 0xa1, 0x4d, 0x51, 0xbe, 0xf8, 0x6d, 0xbf, 0x36, 0x4d, 0x11, 0xd1, 0x1b, 0x4d, 0xad, 0x48, 0x19, + 0xc3, 0xa7, 0x97, 0x5a, 0x09, 0x2d, 0xab, 0x1e, 0x5f, 0x6a, 0x40, 0xe5, 0xe9, 0x47, 0x90, 0xda, + 0x0c, 0x42, 0xc9, 0x67, 0x90, 0x5a, 0x19, 0xcd, 0x84, 0x6f, 0x1a, 0xb5, 0x59, 0xb4, 0x9a, 0xf1, + 0x5c, 0x51, 0x9b, 0x43, 0x0b, 0x89, 0xd7, 0x86, 0xda, 0x3c, 0x5a, 0x4c, 0x3f, 0x1e, 0xd4, 0xb4, + 0xf8, 0x57, 0xd0, 0xb8, 0x5b, 0x5b, 0xe0, 0x92, 0xf0, 0x72, 0x51, 0x43, 0x74, 0x38, 0x13, 0x0f, + 0xe9, 0xb4, 0x9b, 0x54, 0x98, 0xb8, 0x02, 0xd3, 0x16, 0x69, 0xb3, 0x92, 0xb3, 0xac, 0x2d, 0xa1, + 0xf5, 0xec, 0xc7, 0x6b, 0xda, 0x32, 0x1d, 0xa2, 0x30, 0x09, 0xae, 0xad, 0xf0, 0x96, 0xe2, 0xee, + 0xaa, 0xa6, 0xd3, 0x0e, 0xc5, 0x7d, 0x4c, 0x6d, 0x95, 0x4d, 0xc5, 0xc9, 0xf1, 0xfe, 0x9b, 0x53, + 0x7c, 0x52, 0x6d, 0x6a, 0x6b, 0xbc, 0xfc, 0xf8, 0xb8, 0x51, 0x3f, 0xae, 0xb7, 0xb4, 0x5b, 0xf4, + 0x53, 0x93, 0x4e, 0x83, 0xb6, 0x4e, 0x87, 0x4b, 0xe9, 0x4a, 0x68, 0x1b, 0xac, 0xdf, 0xf1, 0x03, + 0x5b, 0xab, 0xb0, 0xf9, 0x3f, 0x09, 0x0f, 0x02, 0x6d, 0x93, 0x0a, 0x62, 0xa6, 0x59, 0xdb, 0xa2, + 0x9d, 0x4d, 0x18, 0x55, 0xed, 0x36, 0x9d, 0xcc, 0xb4, 0x2d, 0xd3, 0x0c, 0xfa, 0x11, 0x71, 0x5b, + 0xa1, 0xbd, 0x87, 0x6e, 0x31, 0x9b, 0xac, 0x4a, 0xcc, 0x6b, 0x77, 0xd0, 0x12, 0x2c, 0xa4, 0xf2, + 0xd4, 0xda, 0xfb, 0x68, 0x0d, 0x96, 0xd5, 0xe7, 0xa2, 0x76, 0x17, 0xad, 0xc0, 0x4d, 0xc5, 0x86, + 0xd7, 0xee, 0xd1, 0xa6, 0xa3, 0x65, 0xf9, 0x6a, 0x4f, 0xdb, 0x7e, 0xf0, 0x4f, 0x05, 0x29, 0xa8, + 0x89, 0x6e, 0x4a, 0xe9, 0x18, 0x25, 0x2a, 0x82, 0x90, 0x57, 0xbb, 0x81, 0x1e, 0xc2, 0xbd, 0x44, + 0x55, 0xf3, 0xd2, 0x1a, 0xa8, 0x2e, 0x05, 0xb5, 0x02, 0xfa, 0x00, 0xee, 0x27, 0x79, 0x6c, 0x3a, + 0x4c, 0x4a, 0xf8, 0xd8, 0x83, 0x9f, 0xc1, 0xa2, 0xca, 0x07, 0x44, 0x40, 0x8f, 0xd1, 0xbe, 0xc3, + 0x36, 0x75, 0x11, 0x26, 0x6a, 0x96, 0x77, 0xa9, 0x15, 0x0e, 0x1a, 0x7f, 0xfa, 0xa6, 0x52, 0xf8, + 0xf3, 0x37, 0x95, 0xc2, 0xff, 0x7e, 0x53, 0xb9, 0xf1, 0xfb, 0x6f, 0x2b, 0x37, 0xfe, 0xf9, 0xdb, + 0x4a, 0xe1, 0xcf, 0xdf, 0x56, 0x6e, 0xfc, 0xe5, 0xdb, 0xca, 0x8d, 0xcf, 0x77, 0x62, 0xff, 0xe3, + 0x43, 0xdf, 0xf4, 0x5d, 0xeb, 0xca, 0x71, 0xad, 0xae, 0x65, 0x8b, 0x82, 0x4d, 0x76, 0x07, 0x97, + 0xdd, 0xdd, 0xc1, 0xd9, 0x2e, 0x73, 0x3e, 0xcf, 0xa6, 0x58, 0x8a, 0xf5, 0xe7, 0x7f, 0x0d, 0x00, + 0x00, 0xff, 0xff, 0x77, 0x6f, 0x05, 0xb2, 0x91, 0x42, 0x00, 0x00, } func (m *QueryRequest) Marshal() (dAtA []byte, err error) { @@ -6534,6 +6592,26 @@ func (m *GetProtocolVersionResponse) MarshalToSizedBuffer(dAtA []byte) (int, err _ = i var l int _ = l + if m.DDLVisibilityActivationFenced { + i-- + if m.DDLVisibilityActivationFenced { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if m.DDLVisibilityActivationPrepared { + i-- + if m.DDLVisibilityActivationPrepared { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } if m.Version != 0 { i = encodeVarintQuery(dAtA, i, uint64(m.Version)) i-- @@ -6562,6 +6640,27 @@ func (m *SetProtocolVersionRequest) MarshalToSizedBuffer(dAtA []byte) (int, erro _ = i var l int _ = l + if len(m.DDLVisibilityTargetQueryAddress) > 0 { + i -= len(m.DDLVisibilityTargetQueryAddress) + copy(dAtA[i:], m.DDLVisibilityTargetQueryAddress) + i = encodeVarintQuery(dAtA, i, uint64(len(m.DDLVisibilityTargetQueryAddress))) + i-- + dAtA[i] = 0x22 + } + if m.DDLVisibilityTargetGeneration != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.DDLVisibilityTargetGeneration)) + i-- + dAtA[i] = 0x18 + } + if len(m.DDLVisibilityActivationTargets) > 0 { + for iNdEx := len(m.DDLVisibilityActivationTargets) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.DDLVisibilityActivationTargets[iNdEx]) + copy(dAtA[i:], m.DDLVisibilityActivationTargets[iNdEx]) + i = encodeVarintQuery(dAtA, i, uint64(len(m.DDLVisibilityActivationTargets[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } if m.Version != 0 { i = encodeVarintQuery(dAtA, i, uint64(m.Version)) i-- @@ -10981,6 +11080,12 @@ func (m *GetProtocolVersionResponse) ProtoSize() (n int) { if m.Version != 0 { n += 1 + sovQuery(uint64(m.Version)) } + if m.DDLVisibilityActivationPrepared { + n += 2 + } + if m.DDLVisibilityActivationFenced { + n += 2 + } return n } @@ -10993,6 +11098,19 @@ func (m *SetProtocolVersionRequest) ProtoSize() (n int) { if m.Version != 0 { n += 1 + sovQuery(uint64(m.Version)) } + if len(m.DDLVisibilityActivationTargets) > 0 { + for _, s := range m.DDLVisibilityActivationTargets { + l = len(s) + n += 1 + l + sovQuery(uint64(l)) + } + } + if m.DDLVisibilityTargetGeneration != 0 { + n += 1 + sovQuery(uint64(m.DDLVisibilityTargetGeneration)) + } + l = len(m.DDLVisibilityTargetQueryAddress) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } return n } @@ -13389,6 +13507,46 @@ func (m *GetProtocolVersionResponse) Unmarshal(dAtA []byte) error { break } } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DDLVisibilityActivationPrepared", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.DDLVisibilityActivationPrepared = bool(v != 0) + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DDLVisibilityActivationFenced", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.DDLVisibilityActivationFenced = bool(v != 0) default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) @@ -13458,6 +13616,89 @@ func (m *SetProtocolVersionRequest) Unmarshal(dAtA []byte) error { break } } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DDLVisibilityActivationTargets", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DDLVisibilityActivationTargets = append(m.DDLVisibilityActivationTargets, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DDLVisibilityTargetGeneration", wireType) + } + m.DDLVisibilityTargetGeneration = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DDLVisibilityTargetGeneration |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DDLVisibilityTargetQueryAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DDLVisibilityTargetQueryAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) diff --git a/pkg/queryservice/client/query_client.go b/pkg/queryservice/client/query_client.go index 9196d8816f8f1..dcc0b404a4786 100644 --- a/pkg/queryservice/client/query_client.go +++ b/pkg/queryservice/client/query_client.go @@ -63,6 +63,7 @@ var methodVersions = map[pb.CmdMethod]int64{ pb.CmdMethod_ISCPDrainConsumer: defines.MORPCVersion4, pb.CmdMethod_IcebergCacheInvalidate: defines.MORPCVersion4, pb.CmdMethod_MongoDBClientRetire: defines.MORPCVersion5, + pb.CmdMethod_SyncCommitV2: defines.MORPCVersion41, } type queryClient struct { diff --git a/pkg/queryservice/client/query_client_test.go b/pkg/queryservice/client/query_client_test.go index b119e07d7ec9c..6950ab7f8efa4 100644 --- a/pkg/queryservice/client/query_client_test.go +++ b/pkg/queryservice/client/query_client_test.go @@ -15,10 +15,12 @@ package client import ( + "context" "testing" "github.com/matrixorigin/matrixone/pkg/common/moerr" "github.com/matrixorigin/matrixone/pkg/common/morpc" + moruntime "github.com/matrixorigin/matrixone/pkg/common/runtime" "github.com/matrixorigin/matrixone/pkg/defines" "github.com/matrixorigin/matrixone/pkg/pb/query" "github.com/stretchr/testify/assert" @@ -34,6 +36,19 @@ func TestMongoDBClientRetireRequiresProtocolVersion5(t *testing.T) { assert.Equal(t, defines.MORPCVersion5, methodVersions[query.CmdMethod_MongoDBClientRetire]) } +func TestSyncCommitV2RequiresProtocolVersion38(t *testing.T) { + assert.Equal(t, defines.MORPCVersion41, methodVersions[query.CmdMethod_SyncCommitV2]) + + const serviceID = "sync-commit-v2-version-test" + rt := moruntime.DefaultRuntime() + moruntime.SetupServiceBasedRuntime(serviceID, rt) + req := &query.Request{CmdMethod: query.CmdMethod_SyncCommitV2} + rt.SetGlobalVariables(moruntime.MOProtocolVersion, defines.MORPCVersion35) + assert.Error(t, checkMethodVersion(context.Background(), serviceID, req)) + rt.SetGlobalVariables(moruntime.MOProtocolVersion, defines.MORPCVersion41) + assert.NoError(t, checkMethodVersion(context.Background(), serviceID, req)) +} + func TestNewCacheClient(t *testing.T) { ct := testCreateQueryClient(t) assert.NotNil(t, ct) diff --git a/pkg/sql/plan/function/ctl/cmd_rpc_version.go b/pkg/sql/plan/function/ctl/cmd_rpc_version.go index 8dcff5b268dac..6e0968c22ce91 100644 --- a/pkg/sql/plan/function/ctl/cmd_rpc_version.go +++ b/pkg/sql/plan/function/ctl/cmd_rpc_version.go @@ -16,6 +16,7 @@ package ctl import ( "context" + "errors" "fmt" "strconv" "strings" @@ -23,6 +24,7 @@ import ( "github.com/matrixorigin/matrixone/pkg/clusterservice" "github.com/matrixorigin/matrixone/pkg/common/moerr" + "github.com/matrixorigin/matrixone/pkg/defines" "github.com/matrixorigin/matrixone/pkg/pb/metadata" querypb "github.com/matrixorigin/matrixone/pkg/pb/query" qclient "github.com/matrixorigin/matrixone/pkg/queryservice/client" @@ -111,18 +113,90 @@ func handleSetProtocolVersion(proc *process.Process, } if service == cn && targets != nil { - versions := make([]string, 0, len(targets)) - for _, target := range targets { - resp, err := transferToCN(qt, target, version) - if err != nil { + if version >= defines.MORPCVersion41 { + const maxDDLVisibilityActivationTargets = 1024 + if len(targets) == 0 || len(targets) > maxDDLVisibilityActivationTargets { + return Result{}, moerr.NewInternalErrorNoCtxf( + "DDL visibility activation target count must be between 1 and %d", + maxDDLVisibilityActivationTargets) + } + seen := make(map[string]struct{}, len(targets)) + for _, target := range targets { + if target == "" { + return Result{}, moerr.NewInternalErrorNoCtx("DDL visibility activation target is empty") + } + if _, ok := seen[target]; ok { + return Result{}, moerr.NewInternalErrorNoCtxf( + "DDL visibility activation target %s is duplicated", target) + } + seen[target] = struct{}{} + } + } + if version >= defines.MORPCVersion41 { + if err := validateDDLVisibilityActivationMembership(qt, targets); err != nil { return Result{}, err } - if resp == nil { - return Result{}, moerr.NewInternalErrorNoCtx("no such cn service") + } + if version < defines.MORPCVersion41 { + versions := make([]string, 0, len(targets)) + for _, target := range targets { + resp, sendErr := transferToCN(qt, target, version, nil) + if sendErr != nil { + return Result{}, sendErr + } + if resp == nil || resp.SetProtocolVersion == nil { + if resp != nil { + qt.Release(resp) + } + return Result{}, moerr.NewInternalErrorNoCtx("no such cn service") + } + versions = append(versions, fmt.Sprintf("%s:%d", target, resp.SetProtocolVersion.Version)) + qt.Release(resp) } - versions = append(versions, fmt.Sprintf("%s:%d", target, resp.SetProtocolVersion.Version)) + return Result{Method: SetProtocolVersionMethod, Data: strings.Join(versions, ", ")}, nil } + // Live v41 activation is a distributed barrier. Dispatch every target + // concurrently so each CN can block local DDL producers before any CN + // waits for the complete Prepared set. + type targetResult struct { + index int + resp *querypb.Response + err error + } + results := make(chan targetResult, len(targets)) + for i, target := range targets { + go func(index int, serviceID string) { + resp, sendErr := transferToCN(qt, serviceID, version, targets) + results <- targetResult{index: index, resp: resp, err: sendErr} + }(i, target) + } + + versions := make([]string, len(targets)) + var resultErr error + for range targets { + result := <-results + if result.err != nil { + if result.resp != nil { + qt.Release(result.resp) + } + resultErr = errors.Join(resultErr, result.err) + continue + } + if result.resp == nil || result.resp.SetProtocolVersion == nil { + if result.resp != nil { + qt.Release(result.resp) + } + resultErr = errors.Join(resultErr, moerr.NewInternalErrorNoCtx("no such cn service")) + continue + } + versions[result.index] = fmt.Sprintf( + "%s:%d", targets[result.index], result.resp.SetProtocolVersion.Version) + qt.Release(result.resp) + } + if resultErr != nil { + return Result{}, resultErr + } return Result{ Method: SetProtocolVersionMethod, Data: strings.Join(versions, ", "), @@ -153,6 +227,50 @@ func checkProtocolParameter(param string) ([]string, int64, error) { return nil, version, nil } +func validateDDLVisibilityActivationMembership(qt qclient.QueryClient, targets []string) error { + cluster := clusterservice.GetMOCluster(qt.ServiceID()) + refresher, ok := cluster.(clusterservice.AuthoritativeRefresher) + if !ok { + return moerr.NewInternalErrorNoCtx( + "CN cluster service does not support authoritative DDL activation inventory") + } + ctx, cancel := context.WithTimeoutCause(context.Background(), time.Minute, moerr.CauseTransferToCN) + defer cancel() + if err := refresher.Refresh(ctx); err != nil { + return moerr.AttachCause(ctx, err) + } + authoritative := make(map[string]metadata.CNService) + if err := clusterservice.GetCNServiceRawWithContext( + ctx, cluster, clusterservice.NewSelector(), func(cn metadata.CNService) bool { + if cn.QueryAddress != "" && cn.ViewMetadataAdmissionGeneration != 0 { + authoritative[cn.ServiceID] = cn + } + return true + }); err != nil { + return moerr.AttachCause(ctx, err) + } + requested := make(map[string]struct{}, len(targets)) + for _, target := range targets { + requested[target] = struct{}{} + } + if len(requested) != len(authoritative) { + return moerr.NewInvalidStateNoCtx(fmt.Sprintf( + "DDL visibility activation target set does not match authoritative CN membership: requested=%d authoritative=%d", + len(requested), len(authoritative))) + } + for serviceID, cn := range authoritative { + if _, ok := requested[serviceID]; !ok { + return moerr.NewInvalidStateNoCtx(fmt.Sprintf( + "DDL visibility activation omits authoritative CN %s", serviceID)) + } + if !cn.DDLVisibilityBarrierReady { + return moerr.NewInvalidStateNoCtx(fmt.Sprintf( + "authoritative CN %s does not support the DDL visibility activation receiver", serviceID)) + } + } + return nil +} + func transferToTN(qt qclient.QueryClient, version int64) (Result, error) { var addr string var resp *querypb.Response @@ -187,23 +305,59 @@ func transferToTN(qt qclient.QueryClient, version int64) (Result, error) { }, nil } -func transferToCN(qt qclient.QueryClient, target string, version int64) (resp *querypb.Response, err error) { - clusterservice.GetMOCluster(qt.ServiceID()).GetCNService( - clusterservice.NewServiceIDSelector(target), - func(cn metadata.CNService) bool { - req := qt.NewRequest(querypb.CmdMethod_SetProtocolVersion) - req.SetProtocolVersion = &querypb.SetProtocolVersionRequest{ - Version: version, - } - ctx, cancel := context.WithTimeoutCause(context.Background(), time.Second, moerr.CauseTransferToCN) - defer cancel() - - resp, err = qt.SendMessage(ctx, cn.QueryAddress, req) - err = moerr.AttachCause(ctx, err) - return true +func transferToCN( + qt qclient.QueryClient, + target string, + version int64, + activationTargets []string, +) (resp *querypb.Response, err error) { + cluster := clusterservice.GetMOCluster(qt.ServiceID()) + var selected metadata.CNService + if version >= defines.MORPCVersion41 { + refresher, refreshOK := cluster.(clusterservice.AuthoritativeRefresher) + if !refreshOK { + return nil, moerr.NewInternalErrorNoCtx( + "CN cluster service does not support authoritative activation recovery") + } + ctx, cancel := context.WithTimeoutCause( + context.Background(), time.Minute, moerr.CauseTransferToCN) + defer cancel() + if err := refresher.Refresh(ctx); err != nil { + return nil, moerr.AttachCause(ctx, err) + } + if err := clusterservice.GetCNServiceRawWithContext( + ctx, cluster, clusterservice.NewServiceIDSelector(target), func(cn metadata.CNService) bool { + selected = cn + return false + }); err != nil { + return nil, moerr.AttachCause(ctx, err) + } + if selected.ServiceID != target || selected.ViewMetadataAdmissionGeneration == 0 || + selected.QueryAddress == "" { + return nil, moerr.NewInternalErrorNoCtxf( + "no authoritative CN activation target %s with valid generation and query address", target) + } + } else { + cluster.GetCNService(clusterservice.NewServiceIDSelector(target), func(cn metadata.CNService) bool { + selected = cn + return false }) - if err != nil { - return nil, err } - return resp, nil + if selected.QueryAddress == "" { + return nil, nil + } + + req := qt.NewRequest(querypb.CmdMethod_SetProtocolVersion) + req.SetProtocolVersion = &querypb.SetProtocolVersionRequest{ + Version: version, + DDLVisibilityActivationTargets: append([]string(nil), activationTargets...), + DDLVisibilityTargetGeneration: selected.ViewMetadataAdmissionGeneration, + DDLVisibilityTargetQueryAddress: selected.QueryAddress, + } + // Live protocol activation may withdraw CN ingress and wait for a bounded + // logtail frontier fence before acknowledging the transition. + ctx, cancel := context.WithTimeoutCause(context.Background(), time.Minute, moerr.CauseTransferToCN) + defer cancel() + resp, err = qt.SendMessage(ctx, selected.QueryAddress, req) + return resp, moerr.AttachCause(ctx, err) } diff --git a/pkg/sql/plan/function/ctl/cmd_rpc_version_test.go b/pkg/sql/plan/function/ctl/cmd_rpc_version_test.go index 1fd53a045b270..6e069dbb02dab 100644 --- a/pkg/sql/plan/function/ctl/cmd_rpc_version_test.go +++ b/pkg/sql/plan/function/ctl/cmd_rpc_version_test.go @@ -17,6 +17,9 @@ package ctl import ( "context" "fmt" + "strings" + "sync" + "sync/atomic" "testing" "time" @@ -142,16 +145,147 @@ func requireVersionValue(t *testing.T, version int64) { type addressRecordingQueryClient struct { testQClient - address string + address string + deadline time.Time + request *query.Request } func (c *addressRecordingQueryClient) SendMessage( - _ context.Context, address string, _ *query.Request, + ctx context.Context, address string, req *query.Request, ) (*query.Response, error) { c.address = address + c.deadline, _ = ctx.Deadline() + c.request = req return nil, moerr.NewInternalErrorNoCtx("send error") } +type concurrentProtocolQueryClient struct { + testQClient + started atomic.Int32 + both chan struct{} + mu sync.Mutex + requests [][]string + releases atomic.Int32 +} + +func (c *concurrentProtocolQueryClient) SendMessage( + _ context.Context, + _ string, + req *query.Request, +) (*query.Response, error) { + c.mu.Lock() + c.requests = append(c.requests, + append([]string(nil), req.SetProtocolVersion.DDLVisibilityActivationTargets...)) + c.mu.Unlock() + if c.started.Add(1) == 2 { + close(c.both) + } + <-c.both + return &query.Response{SetProtocolVersion: &query.SetProtocolVersionResponse{ + Version: req.SetProtocolVersion.Version, + }}, nil +} + +func (c *concurrentProtocolQueryClient) NewRequest(method query.CmdMethod) *query.Request { + return &query.Request{CmdMethod: method} +} + +func (c *concurrentProtocolQueryClient) Release(*query.Response) { c.releases.Add(1) } + +func TestHandleSetProtocolVersionDispatchesCompleteTargetSetConcurrently(t *testing.T) { + targets := []string{"activation-cn-1", "activation-cn-2"} + rt := runtime.DefaultRuntime() + runtime.SetupServiceBasedRuntime("", rt) + mc := clusterservice.NewMOCluster( + "", + nil, + 3*time.Second, + clusterservice.WithDisableRefresh(), + clusterservice.WithServices( + []metadata.CNService{ + {ServiceID: targets[0], QueryAddress: "cn-1:6001", WorkState: metadata.WorkState_Working, + ViewMetadataAdmissionGeneration: 1, DDLVisibilityBarrierReady: true}, + {ServiceID: targets[1], QueryAddress: "cn-2:6001", WorkState: metadata.WorkState_Working, + ViewMetadataAdmissionGeneration: 2, DDLVisibilityBarrierReady: true}, + }, + nil, + ), + ) + defer mc.Close() + rt.SetGlobalVariables(runtime.ClusterService, mc) + qcli := &concurrentProtocolQueryClient{both: make(chan struct{})} + proc := &process.Process{Base: &process.BaseProcess{QueryClient: qcli}} + + result, err := handleSetProtocolVersion( + proc, cn, strings.Join(targets, ",")+":41", nil) + require.NoError(t, err) + require.Equal(t, "activation-cn-1:41, activation-cn-2:41", result.Data) + require.Equal(t, int32(2), qcli.started.Load()) + require.Equal(t, int32(2), qcli.releases.Load()) + qcli.mu.Lock() + defer qcli.mu.Unlock() + require.Len(t, qcli.requests, 2) + for _, requestTargets := range qcli.requests { + require.Equal(t, targets, requestTargets) + } + + _, err = handleSetProtocolVersion(proc, cn, "activation-cn-1,activation-cn-1:41", nil) + require.ErrorContains(t, err, "duplicated") +} + +func TestHandleSetProtocolVersionRejectsOmittedAuthoritativeCN(t *testing.T) { + rt := runtime.DefaultRuntime() + runtime.SetupServiceBasedRuntime("", rt) + mc := clusterservice.NewMOCluster( + "", nil, 3*time.Second, clusterservice.WithDisableRefresh(), + clusterservice.WithServices([]metadata.CNService{ + {ServiceID: "new-cn", QueryAddress: "new:6001", ViewMetadataAdmissionGeneration: 1, + DDLVisibilityBarrierReady: true}, + {ServiceID: "legacy-public-cn", QueryAddress: "legacy:6001", ViewMetadataAdmissionGeneration: 2, + ViewMetadataIngressReady: true}, + }, nil), + ) + defer mc.Close() + rt.SetGlobalVariables(runtime.ClusterService, mc) + proc := &process.Process{Base: &process.BaseProcess{QueryClient: &concurrentProtocolQueryClient{both: make(chan struct{})}}} + + _, err := handleSetProtocolVersion(proc, cn, "new-cn:41", nil) + require.ErrorContains(t, err, "does not match authoritative CN membership") +} + +func TestTransferToCNAllowsActivationFence(t *testing.T) { + const serviceID = "activation-cn" + rt := runtime.DefaultRuntime() + runtime.SetupServiceBasedRuntime("", rt) + mc := clusterservice.NewMOCluster( + "", + nil, + 3*time.Second, + clusterservice.WithDisableRefresh(), + clusterservice.WithServices( + []metadata.CNService{{ + ServiceID: serviceID, QueryAddress: "activation-cn:6001", + WorkState: metadata.WorkState_Draining, ViewMetadataAdmissionGeneration: 1, + }}, + nil, + ), + ) + defer mc.Close() + rt.SetGlobalVariables(runtime.ClusterService, mc) + + qcli := &addressRecordingQueryClient{} + started := time.Now() + _, err := transferToCN(qcli, serviceID, defines.MORPCVersion41, []string{serviceID}) + require.Error(t, err) + require.Equal(t, "activation-cn:6001", qcli.address) + require.Equal(t, []string{serviceID}, + qcli.request.SetProtocolVersion.DDLVisibilityActivationTargets) + require.Equal(t, uint64(1), qcli.request.SetProtocolVersion.DDLVisibilityTargetGeneration) + require.Equal(t, "activation-cn:6001", + qcli.request.SetProtocolVersion.DDLVisibilityTargetQueryAddress) + require.WithinDuration(t, started.Add(time.Minute), qcli.deadline, time.Second) +} + func Test_transferToTN(t *testing.T) { rt := runtime.DefaultRuntime() diff --git a/pkg/tests/issues/issue_277xx_ddl_consistency_test.go b/pkg/tests/issues/issue_277xx_ddl_consistency_test.go index 4cbd9eca72679..7bce33bd18460 100644 --- a/pkg/tests/issues/issue_277xx_ddl_consistency_test.go +++ b/pkg/tests/issues/issue_277xx_ddl_consistency_test.go @@ -24,7 +24,9 @@ import ( "time" mysql "github.com/go-sql-driver/mysql" + "github.com/matrixorigin/matrixone/pkg/cnservice" "github.com/matrixorigin/matrixone/pkg/embed" + "github.com/matrixorigin/matrixone/pkg/util/fault" "github.com/stretchr/testify/require" ) @@ -42,6 +44,47 @@ func TestIssue277xxDDLConsistency(t *testing.T) { db1 := openIssue277xxDB(t, cn1.GetServiceConfig().CN.Frontend.Port) defer db1.Close() + t.Run("27743 automatic cross-CN DDL visibility", func(t *testing.T) { + const database = "issue_27743_automatic_ddl_visibility" + targets := cn0.ServiceID() + "," + cn1.ServiceID() + var activation string + require.NoError(t, db0.QueryRowContext(ctx, + "select mo_ctl('cn', 'SetProtocolVersion', ?)", targets+":41").Scan(&activation)) + require.Contains(t, activation, cn0.ServiceID()+":41") + require.Contains(t, activation, cn1.ServiceID()+":41") + // SetProtocolVersion returns success only after both target RPCs report + // v41 and HAKeeper acknowledges the atomically committed v41 epoch. + + resetIssue277xxDatabase(t, ctx, db0, database) + defer execSQLMaybe(t, ctx, db0, "drop database if exists `"+database+"`") + + // Prove this public path actually executes SyncCommitV2: an injected + // receiver failure must make CREATE fail after v41 activation. + require.True(t, fault.Enable()) + defer fault.Disable() + faultPointRemoved := false + defer func() { + if !faultPointRemoved { + _, _ = fault.RemoveFaultPoint(context.Background(), cnservice.DDLVisibilitySyncCommitFaultPoint) + } + }() + require.NoError(t, fault.AddFaultPoint(ctx, + cnservice.DDLVisibilitySyncCommitFaultPoint, "1:1::", "return", 0, "expected", false)) + _, err := db0.ExecContext(ctx, + "create table `"+database+"`.`must_fail_sync` (id int primary key)") + require.ErrorContains(t, err, "injected DDL visibility sync commit error") + _, removeErr := fault.RemoveFaultPoint(ctx, cnservice.DDLVisibilitySyncCommitFaultPoint) + require.NoError(t, removeErr) + faultPointRemoved = true + + // Do not issue SYNCCOMMIT or retry the read. The v41 DDL commit contract + // must make the first fresh CN1 snapshot observe CN0's CREATE TABLE. + execSQLRequire(t, ctx, db0, + "create table `"+database+"`.`t` (id int primary key, payload varchar(32))") + require.Equal(t, 0, queryIssue277xxInt(t, ctx, db1, + "select count(*) from `"+database+"`.`t`")) + }) + t.Run("27735 qualified alter drop index without default database", func(t *testing.T) { const database = "issue_27735_qualified_drop" resetIssue277xxDatabase(t, ctx, db0, database) diff --git a/pkg/txn/client/client.go b/pkg/txn/client/client.go index 5431bd3abe3d3..d5b6bde7473bc 100644 --- a/pkg/txn/client/client.go +++ b/pkg/txn/client/client.go @@ -962,7 +962,7 @@ func (client *txnClient) GetLatestCommitTS() timestamp.Timestamp { func (client *txnClient) SyncLatestCommitTS(ts timestamp.Timestamp) { client.updateLastCommitTS(context.TODO(), nil, TxnEvent{Txn: txn.TxnMeta{CommitTS: ts}}, nil) - if client.timestampWaiter != nil { + if client.timestampWaiter != nil && client.timestampWaiter.LatestTS().Less(ts) { ctx, cancel := context.WithTimeoutCause(context.Background(), time.Minute*5, moerr.CauseSyncLatestCommitT) defer cancel() for { diff --git a/pkg/txn/client/client_test.go b/pkg/txn/client/client_test.go index 38f3cbd01e8d1..d5a49cb984262 100644 --- a/pkg/txn/client/client_test.go +++ b/pkg/txn/client/client_test.go @@ -212,6 +212,11 @@ type snapshotTimestampWaiter struct { waitForContext bool } +type appliedTimestampWaiter struct { + latest timestamp.Timestamp + getCalls int +} + func (w *snapshotTimestampWaiter) GetTimestamp( ctx context.Context, ts timestamp.Timestamp, @@ -229,6 +234,18 @@ func (*snapshotTimestampWaiter) NotifyLatestCommitTS(timestamp.Timestamp) {} func (*snapshotTimestampWaiter) Close() {} func (*snapshotTimestampWaiter) LatestTS() timestamp.Timestamp { return timestamp.Timestamp{} } +func (w *appliedTimestampWaiter) GetTimestamp( + context.Context, + timestamp.Timestamp, +) (timestamp.Timestamp, error) { + w.getCalls++ + return w.latest.Next(), nil +} + +func (*appliedTimestampWaiter) NotifyLatestCommitTS(timestamp.Timestamp) {} +func (*appliedTimestampWaiter) Close() {} +func (w *appliedTimestampWaiter) LatestTS() timestamp.Timestamp { return w.latest } + // selectedSuccessTimestampWaiter models the valid notify-vs-cancel ordering // where notification wins the waiter's select, but the successful return is // delayed until after the transaction client has closed. @@ -273,6 +290,42 @@ func TestAdjustClient(t *testing.T) { } } +func TestSyncLatestCommitTS(t *testing.T) { + targetTS := timestamp.Timestamp{PhysicalTime: 100, LogicalTime: 7} + + t.Run("without timestamp waiter", func(t *testing.T) { + client := &txnClient{} + + client.SyncLatestCommitTS(targetTS) + + require.Equal(t, targetTS, client.GetLatestCommitTS()) + require.Equal(t, uint64(1), client.GetSyncLatestCommitTSTimes()) + }) + + t.Run("skips an already applied timestamp waiter", func(t *testing.T) { + waiter := &appliedTimestampWaiter{latest: targetTS} + client := &txnClient{timestampWaiter: waiter} + + client.SyncLatestCommitTS(targetTS) + + require.Zero(t, waiter.getCalls) + require.Equal(t, targetTS, client.GetLatestCommitTS()) + require.Equal(t, uint64(1), client.GetSyncLatestCommitTSTimes()) + }) + + t.Run("waits for an unapplied timestamp", func(t *testing.T) { + waiter := &snapshotTimestampWaiter{} + client := &txnClient{timestampWaiter: waiter} + + client.SyncLatestCommitTS(targetTS) + + require.Equal(t, 1, waiter.called) + require.Equal(t, targetTS, waiter.got) + require.Equal(t, targetTS, client.GetLatestCommitTS()) + require.Equal(t, uint64(1), client.GetSyncLatestCommitTSTimes()) + }) +} + func TestNewWithSnapshotWaitsForLocalLogtail(t *testing.T) { rt := runtime.NewRuntime(metadata.ServiceType_CN, "", logutil.GetPanicLogger(), diff --git a/proto/logservice.proto b/proto/logservice.proto index 9174ee41784a1..173ae0e1d879d 100644 --- a/proto/logservice.proto +++ b/proto/logservice.proto @@ -22,6 +22,7 @@ option go_package = "github.com/matrixorigin/matrixone/pkg/pb/logservice"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; import "google/protobuf/timestamp.proto"; import "metadata.proto"; +import "timestamp.proto"; option (gogoproto.goproto_enum_prefix_all) = false; option (gogoproto.sizer_all) = false; @@ -61,6 +62,15 @@ message CNStore { uint64 ViewMetadataAdmissionGeneration = 18; uint64 ViewMetadataObservedEpoch = 19; bool ViewMetadataAdmissionReady = 20; + // DDLVisibilityBarrierReady means QueryService can accept the versioned DDL + // visibility fence even when public SQL ingress is not admitted yet. + bool DDLVisibilityBarrierReady = 21; + // ViewMetadataIngressReady is retained for raw control-plane inventory. + bool ViewMetadataIngressReady = 22; + int64 DDLVisibilityDeployedProtocol = 23; + bool DDLVisibilityActivationPrepared = 24; + bool DDLVisibilityActivationFenced = 25; + timestamp.Timestamp DDLVisibilityFrontier = 26 [(gogoproto.nullable) = false]; } message TNStore { @@ -92,6 +102,9 @@ message LogStore { repeated LogReplicaInfo Replicas = 5 [(gogoproto.nullable) = false]; ConfigData ConfigData = 6; Locality Locality = 7 [(gogoproto.nullable) = false]; + // DDLVisibilityDeployedProtocolSupported proves the hosted HAKeeper RSM can + // persist and return the cluster-wide DDL deployment epoch. + bool DDLVisibilityDeployedProtocolSupported = 8; } // LogShardInfo contains information a log shard. @@ -133,6 +146,12 @@ message Resource { } // CNStoreHeartbeat is the periodic message sent tp the HAKeeper by CN stores. +message DDLVisibilityActivationTarget { + string ServiceID = 1; + uint64 Generation = 2; + string QueryAddress = 3; +} + message CNStoreHeartbeat { string UUID = 1; string ServiceAddress = 2; @@ -164,6 +183,17 @@ message CNStoreHeartbeat { // ViewMetadataIngressReady is published only after every public CN ingress // listener has started successfully. Admission alone is not routability. bool ViewMetadataIngressReady = 24; + bool DDLVisibilityBarrierReady = 25; + // DDLVisibilityDeployedProtocol reports only a locally committed deployment + // marker. Provisional activation must never advance the cluster-wide epoch. + int64 DDLVisibilityDeployedProtocol = 26; + // Non-empty only for the transition that atomically validates the complete + // CN membership tuple set and commits DDLVisibilityDeployedProtocol. + repeated DDLVisibilityActivationTarget DDLVisibilityEpochCommitTargets = 27 + [(gogoproto.nullable) = false]; + bool DDLVisibilityActivationPrepared = 28; + bool DDLVisibilityActivationFenced = 29; + timestamp.Timestamp DDLVisibilityFrontier = 30 [(gogoproto.nullable) = false]; } // CNAllocateID is the periodic message sent tp the HAKeeper by CN stores. @@ -201,6 +231,9 @@ message LogStoreHeartbeat { // ViewMetadataAdmissionSupported proves that every HAKeeper RSM hosted by // this LogStore can decode the durable admission update and snapshot fields. bool ViewMetadataAdmissionSupported = 10; + // This gates the HAKeeper RSM schema used by the durable DDL deployment + // epoch across both voting and non-voting replicas. + bool DDLVisibilityDeployedProtocolSupported = 11; }; // TNShardInfo contains information of a launched TN shard. @@ -629,6 +662,9 @@ message CommandBatch { // ViewMetadataAdmission is the authoritative snapshot for the heartbeat // sender. Old services ignore this additive field. ViewMetadataAdmission ViewMetadataAdmission = 5; + // DDLVisibilityDeployedProtocol is returned from the same replicated + // heartbeat transition that conditionally publishes CN ingress. + int64 DDLVisibilityDeployedProtocol = 6; } // ViewMetadataAdmission is the durable cluster admission snapshot. Generation @@ -687,12 +723,21 @@ message CNStoreInfo { uint64 ViewMetadataRevalidatedEpoch = 24; bool ViewMetadataAdmissionReady = 25; bool ViewMetadataIngressReady = 26; + bool DDLVisibilityBarrierReady = 27; + int64 DDLVisibilityDeployedProtocol = 28; + bool DDLVisibilityActivationPrepared = 29; + bool DDLVisibilityActivationFenced = 30; + timestamp.Timestamp DDLVisibilityFrontier = 31 [(gogoproto.nullable) = false]; } // CNState contains all CN details known to the HAKeeper. message CNState { - // Stores is keyed by CN store UUID. + // Stores is keyed by CN store UUID. map Stores = 1 [(gogoproto.nullable) = false]; + // DDLVisibilityDeployedProtocol is a monotonic cluster deployment epoch. + // Once a complete-target cut commits, markerless replacement/scale-out CNs + // must join that fence instead of reopening on an older protocol. + int64 DDLVisibilityDeployedProtocol = 2; } @@ -758,6 +803,7 @@ message ClusterDetails { repeated ProxyStore ProxyStores = 4 [(gogoproto.nullable) = false]; repeated DeletedStore DeletedStores = 5 [(gogoproto.nullable) = false]; ViewMetadataAdmission ViewMetadataAdmission = 6; + int64 DDLVisibilityDeployedProtocol = 7; } // ClusterInfo provides a global view of all shards in the cluster. It @@ -797,6 +843,7 @@ message LogStoreInfo { Locality Locality = 8 [(gogoproto.nullable) = false]; bool CommandDeliverySupported = 9; bool ViewMetadataAdmissionSupported = 10; + bool DDLVisibilityDeployedProtocolSupported = 11; } message LogState { diff --git a/proto/metadata.proto b/proto/metadata.proto index 465382f0dda6d..7cf8956ce28e7 100644 --- a/proto/metadata.proto +++ b/proto/metadata.proto @@ -110,6 +110,9 @@ message CNStore { string UUID = 1; // Role CN role CNRole Role = 2; + // DDLVisibilityDeployedProtocol is the durable activation level restored + // before this CN admits SQL after an ordinary restart. + int64 DDLVisibilityDeployedProtocol = 3; } // CNService cn service metadata @@ -139,6 +142,12 @@ message CNService { uint64 ViewMetadataAdmissionGeneration = 13; bool ViewMetadataAdmissionReady = 14; uint64 ViewMetadataObservedEpoch = 15; + // DDLVisibilityBarrierReady is published after QueryService is listening. + // A CN is not publicly routable until its startup DDL frontier fence completes. + bool DDLVisibilityBarrierReady = 16; + // ViewMetadataIngressReady is retained in raw control-plane inventory so + // protocol activation can verify withdrawal/publication without routing it. + bool ViewMetadataIngressReady = 17; } // TNService tn service metadata diff --git a/proto/query.proto b/proto/query.proto index cadbc42c42d59..a87aeb2f7380c 100644 --- a/proto/query.proto +++ b/proto/query.proto @@ -108,6 +108,9 @@ enum CmdMethod { IcebergCacheInvalidate = 38; // MongoDBClientRetire drains obsolete MongoDB driver clients after catalog DDL. MongoDBClientRetire = 39; + // SyncCommitV2 waits for local logtail application with the request context. + // Unlike legacy SyncCommit, cancellation is returned to the sender. + SyncCommitV2 = 40; } // QueryRequest is the common query request. It contains the query @@ -161,11 +164,23 @@ message GetProtocolVersionRequest { message GetProtocolVersionResponse { // ProtocolVersion is the version of the protocol int64 Version = 1; + // DDLVisibilityActivationPrepared means this CN has blocked and drained DDL + // producers before exposing the v36 sender capability. + bool DDLVisibilityActivationPrepared = 2; + // DDLVisibilityActivationFenced means this CN applied the converged peer + // frontier while every participant's DDL producers were blocked. + bool DDLVisibilityActivationFenced = 3; } message SetProtocolVersionRequest { // ProtocolVersion is the version of the protocol int64 Version = 1; + // DDLVisibilityActivationTargets is the complete CN set participating in a + // coordinated live activation. It is empty for ordinary version updates. + repeated string DDLVisibilityActivationTargets = 2; + // The authoritative raw-inventory identity selected by the control plane. + uint64 DDLVisibilityTargetGeneration = 3; + string DDLVisibilityTargetQueryAddress = 4; } message SetProtocolVersionResponse {