laserninja commented on PR #12241:
URL: https://github.com/apache/gravitino/pull/12241#issuecomment-5153065714

   @roryqi thank you - the determinism point was not a theoretical one, and 
chasing it found a correctness bug. All three of your asks are addressed; the 
document is updated and #12194 has the fix.
   
   **The total order was not total.** You asked me to either cite why two 
semantically distinct tasks cannot share `(data file location, start, length)`, 
or add a tie-breaker. It cannot be cited, because they can: appending one data 
file path twice leaves two manifest entries that tie on all three fields while 
carrying different sequence numbers, and potentially different delete files. 
Planning such a table shows it directly:
   
   ```
   location=…/dup.parquet start=0 length=10  dataSeq=2  manifest=…-m0.avro pos=0
   location=…/dup.parquet start=0 length=10  dataSeq=1  manifest=…-m0.avro pos=0
   ```
   
   Sorting is stable, so tied tasks keep the order Iceberg planned them in - 
precisely the order that is not reproducible. Two of them either side of a 
batch boundary can swap between the plan and a later re-plan, and the client 
then receives one twice and never sees the other. That is a wrong result, not a 
slow one, so thank you for pushing on it.
   
   Fixed in df9a2c065 by sorting on `(location, start, length, data sequence 
number, file sequence number, manifest location, entry position)`. A manifest 
entry is unique within a snapshot, so the order is now total for any two tasks 
that are not interchangeable; sequence numbers come first because they survive 
a rewrite into new manifests. The regression test builds a table holding the 
same path twice and asserts which entry each batch gets - it fails on the old 
comparator (`expected: <[2]> but was: <[1]>`) and passes on the new one.
   
   **Multi-replica behaviour is now its own section (§5.15).** It separates the 
two halves of your point: correctness does not depend on which replica serves a 
request, and cost does. It states the worst case (`ceil(N/B)` full plans spread 
across replicas, since the cache is node-local and the Iceberg client fetches 
plan tasks concurrently), the production posture as a table (enable a cache, 
route a scan to one replica, raise the batch size, coalesce concurrent 
redemptions on a replica), and explicitly that those shrink the multiplier 
without removing it. Your signing observation is recorded there too: if we ever 
sign a plan task, the key must be shared by every replica, or verification 
succeeds only on the issuing replica and clients see `404`s that depend on 
routing.
   
   **On removing the amplification rather than shrinking it (§8.5).** I agree 
with your reply to @lasdf1234 that an internal cache cannot cure the multi-node 
case. The way to cure it is to change what a plan task *is*: name a data 
manifest, or a manifest plus an entry range, instead of an offset into a global 
task list. Redemption then reads one manifest plus the delete manifests, so its 
cost is independent of plan size, on any replica, with no shared cache - and it 
makes the whole ordering question moot, because there is no global index to 
stabilise. I did not propose it for this PR because Iceberg does not expose 
enough to build it: `ManifestGroup` and `DeleteFileIndex`, which do residual 
evaluation and delete attachment, are package-private in `iceberg-core`, so it 
needs an upstream API or a careful port. It is written up as a follow-up.
   
   **Polaris comparison (§8.4).** I looked, and there is nothing to compare 
against yet: Polaris excludes `.../plan`, `.../plan/{plan-id}` and `.../tasks` 
from the catalog spec it generates its API from, marked "Not implemented in 
Polaris", and omits them from its advertised `/v1/config` endpoints, so clients 
fall back to client-side planning. Their tracking issue, apache/polaris#966, 
has been open since February 2025 with no comments, through spec refreshes up 
to Iceberg 1.11. So they have neither problem you raised, for the reason that 
they do not plan server side.
   
   The implementation that does exist is Iceberg's own `CatalogHandlers` with 
`RESTCatalogAdapter`, which keeps plans in a static `InMemoryPlanningState` and 
resolves a plan task by map lookup. That is the exact inverse trade-off: 
redemption is free, but a plan task is meaningless after a restart or on 
another process. I would rather pay re-planning cost than ship that, but the 
section states both sides so a reader can disagree with the choice.
   


-- 
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]

Reply via email to