gortiz opened a new pull request, #20058:
URL: https://github.com/apache/pinot/pull/20058

   ## Summary
   
   **What.** Pinot instances can now advertise *capabilities* in their Helix 
instance config, and other components can read them. A capability is a named 
feature with an integer revision, for example `{"MSE_MV_AGGREGATE": "1"}`.
   
   **Why.** During a rolling upgrade, a new broker can send work to servers of 
an older version. A new feature that old servers cannot run must stay off until 
every server supports it. Pinot has no generic way to know this today. 
Comparing Pinot versions does not work well: different distributions and 
backport branches use their own version numbers.
   
   **How.**
   
   - `InstanceCapability` (in `pinot-common`) is the registry of capabilities. 
Each one has a name, the instance type that advertises it, and a revision. This 
PR adds the mechanism with an empty registry. The first capability comes in a 
follow-up PR (the multi-stage engine MV aggregate).
   - At startup, each instance writes its capabilities to the new map field 
`capabilities` of its own instance config, with 
`HelixHelper.updateCapabilities`. Today only servers call it.
   - `InstanceCapabilityTracker` listens to instance config changes and gives 
the minimum revision of a capability over all the instances of one type. 
`MultiClusterInstanceCapabilityTracker` gives the minimum over several 
clusters, for a broker that federates remote clusters.
   
   ## Contract
   
   - A revision only increases. Revision `n` includes all the function of the 
revisions before it.
   - A revision is not a Pinot version. The mechanism compares no version 
numbers.
   - A missing map, a missing key, or a value that is not a valid integer 
counts as revision 0: not supported. Every failure is fail-safe.
   - The names are persisted in ZooKeeper. A capability is never renamed or 
removed.
   
   ## Rollback guard
   
   An old build does not know the `capabilities` field, so it does not remove 
it after a rollback. The map has a reserved entry `pinotVersion` with the 
version of the build that wrote it. Every build since 1.4.0 writes the 
`pinotVersion` simple field at startup. If the two values are not equal, the 
tracker ignores the capabilities of that instance. This is a string equality 
between two fields of one instance config, not a comparison of versions.
   
   One gap remains. The controller writes the full instance config record, for 
example on `PUT /instances/{name}`. If this write races with an instance 
restart, it can restore the `pinotVersion` field and the `capabilities` map of 
the previous build. The `pinotVersion` field has the same problem today. A 
follow-up can make the controller keep the fields that the instance owns.
   
   ## ZooKeeper cost
   
   Writes:
   
   - Each instance writes only its own instance config znode, 
`/<cluster>/CONFIGS/PARTICIPANT/<instanceId>`. There is no shared znode, so 
instances never write the same node.
   - The write is part of the instance config update that `BaseServerStarter` 
already does at startup. It adds about 60 bytes. On an upgrade, the 
`pinotVersion` field changes anyway, so the capabilities add no extra write. A 
restart with the same version writes nothing.
   - The controller writes the full instance config record (for example on `PUT 
/instances/{name}`). If such a write races with a server restart, the map can 
disappear. The readers then see revision 0 until the next restart of that 
server. This is fail-safe.
   
   Reads (only when a component creates a tracker; this PR creates none):
   
   - One `InstanceConfigChangeListener` for each tracked cluster.
   - On the first notification, and when an instance is added or removed, the 
tracker reads the config of each instance of the tracked type, about 2 
ZooKeeper operations each.
   - When one instance config changes, it reads that config only.
   
   The broker routing manager already reads all the instance configs on every 
instance config change. A tracker on a broker adds traffic of the same order, 
and a second watch on the same path. A possible follow-up removes this cost: 
the routing manager can feed the tracker from the records that it already reads.
   
   ## Thread safety
   
   Helix callback threads update the tracker under a lock. Query threads read 
an immutable snapshot through a volatile field, without a lock.
   
   ## Testing
   
   - `InstanceCapabilityTrackerTest`: revision 0 before the first notification, 
missing and malformed values, the minimum over mixed revisions, config changes, 
removed instances, failed reads, and the rollback guard with matching, 
different and missing version stamps.
   - `MultiClusterInstanceCapabilityTrackerTest`: the minimum over clusters, a 
cluster that does not connect, a failed registration, and `close()`.
   - `HelixHelperTest`: `updateCapabilities` writes the capabilities and the 
version stamp, and reports a change only when the map changes.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to