laskoviymishka commented on code in PR #3259:
URL: https://github.com/apache/iceberg-rust/pull/3259#discussion_r4075140593


##########
crates/iceberg/src/io/object_cache.rs:
##########
@@ -36,7 +36,10 @@ pub(crate) enum CachedItem {
 
 #[derive(Clone, Debug, Hash, Eq, PartialEq)]
 pub(crate) enum CachedObjectKey {
-    ManifestList((String, FormatVersion)),
+    // Location alone: the file is immutable and uniquely identified by its
+    // path, and a cache is bound to one table, so the format version can't
+    // disambiguate two lookups at the same location.
+    ManifestList(String),

Review Comment:
   I'd reword this comment — the "format version can't disambiguate" framing 
isn't quite right, and it's load-bearing here.
   
   `parse_with_version` genuinely branches on the version: V1 uses the explicit 
V1 Avro reader-schema, V2/V3 read the file's embedded schema plus the extra 
fields. So the parse product depends on the version, not just the bytes. And 
the cache can outlive a format-version change — `ObjectCache` is built once in 
`TableBuilder::build()` and survives `Table::with_metadata`, so 
`table_metadata.format_version` can shift under a live cache.
   
   For V1→V2 the two parses converge on the same values by spec, so this isn't 
a live bug today. But the real invariant is "the first access to a location 
parses with the matching version, and a location maps to one set of bytes" — 
not "version can never matter." I'd state that instead, so whoever next touches 
this (V3 `first_row_id` is the trap) doesn't lean on a claim that isn't true. 
wdyt?



##########
crates/iceberg/src/io/object_cache.rs:
##########
@@ -490,6 +490,46 @@ mod tests {
         );
     }
 
+    #[tokio::test]
+    async fn test_get_manifest_list_ignores_format_version() {
+        let mut fixture = TableTestFixture::new();
+        fixture.setup_manifest_files().await;
+
+        let current_snapshot = 
fixture.table.metadata().current_snapshot().unwrap();
+
+        // A single ObjectCache is bound to one table whose format version is 
fixed,
+        // so the format version cannot disambiguate two lookups at the same
+        // manifest-list location. Two metadata refs differing only in format 
version
+        // must therefore share one cache entry.
+        let mut metadata_v1 = fixture.table.metadata().clone();
+        metadata_v1.format_version = FormatVersion::V1;
+        let metadata_v1: TableMetadataRef = Arc::new(metadata_v1);
+        assert_ne!(

Review Comment:
   Tiny thing while we're here — this precondition has no failure message, 
unlike the two asserts below it. If someone swaps the fixture to V1 metadata 
later, it fires as a bare `left != right` and reads like a logic bug rather 
than a broken setup assumption. A short message noting it's a setup invariant 
would save that confusion.



##########
crates/iceberg/src/io/object_cache.rs:
##########
@@ -490,6 +490,46 @@ mod tests {
         );
     }
 
+    #[tokio::test]
+    async fn test_get_manifest_list_ignores_format_version() {
+        let mut fixture = TableTestFixture::new();
+        fixture.setup_manifest_files().await;
+
+        let current_snapshot = 
fixture.table.metadata().current_snapshot().unwrap();
+
+        // A single ObjectCache is bound to one table whose format version is 
fixed,
+        // so the format version cannot disambiguate two lookups at the same
+        // manifest-list location. Two metadata refs differing only in format 
version
+        // must therefore share one cache entry.
+        let mut metadata_v1 = fixture.table.metadata().clone();
+        metadata_v1.format_version = FormatVersion::V1;
+        let metadata_v1: TableMetadataRef = Arc::new(metadata_v1);
+        assert_ne!(
+            fixture.table.metadata().format_version,
+            metadata_v1.format_version
+        );
+
+        let object_cache = ObjectCache::new(fixture.table.file_io().clone(), 
None);
+
+        // Cold miss under the table's real format version populates the cache.
+        let inserted = object_cache
+            .get_manifest_list(current_snapshot, &fixture.table.metadata_ref())
+            .await
+            .unwrap();
+        assert_eq!(inserted.entries().len(), 1);
+
+        // Warm hit under a different format version at the same location 
returns the
+        // same cached entry rather than reparsing.
+        let cached = object_cache
+            .get_manifest_list(current_snapshot, &metadata_v1)
+            .await
+            .unwrap();
+        assert!(
+            Arc::ptr_eq(&inserted, &cached),

Review Comment:
   This proves the safe direction but not the one the PR is really about.
   
   Calling with the real V2 metadata first parses V2 bytes as V2 (always 
correct), then the V1 hit just returns that same `Arc` — so `Arc::ptr_eq` only 
tells us the cache didn't re-parse. The motivating case is the reverse: a V1 
manifest list cached under V1, then read again after `with_metadata` upgrades 
the table to V2. That's the path where dropping `format_version` actually 
changes behavior.
   
   I'd add a companion test that writes a real V1 manifest list, populates the 
cache under V1, then reads at the same location under V2 metadata, asserting on 
content (e.g. `sequence_number == 0`, `content == Data`) alongside the `ptr_eq` 
— so we're proving the cached parse is correct for both callers, not just that 
it's the same pointer. wdyt?



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