PDGGK opened a new pull request, #9405:
URL: https://github.com/apache/paimon/pull/9405

   ### Purpose
   
   `IcebergRef.equals` compares `maxRefAgeMs` with `==`, and that field is a 
boxed
   `Long`:
   
   ```java
   // IcebergRef.java:50
   @Nullable private final Long maxRefAgeMs;
   
   // :85-87
   return snapshotId == that.snapshotId
           && type.equals(that.type)
           && maxRefAgeMs == that.maxRefAgeMs;
   ```
   
   So the comparison is on references, not values. It would still work if the 
two
   boxes happened to be the same object, but the constructor guarantees they are
   not:
   
   ```java
   // :53-60
   public IcebergRef(@JsonProperty(FIELD_SNAPSHOT_ID) long snapshotId) {
       this.snapshotId = snapshotId;
       this.type = "tag";
       this.maxRefAgeMs = Long.MAX_VALUE;
   }
   ```
   
   `Long.MAX_VALUE` is far outside the `Long` cache, so every `new 
IcebergRef(..)`
   autoboxes a fresh object and
   
   ```java
   new IcebergRef(1L).equals(new IcebergRef(1L))   // false
   ```
   
   `hashCode` was already written the other way:
   
   ```java
   // :91-93
   return Objects.hash(snapshotId, type, maxRefAgeMs);
   ```
   
   which is value-based. The two therefore disagree: refs that hash to the same
   bucket compare unequal, so a `HashSet<IcebergRef>` keeps both copies rather 
than
   deduplicating.
   
   It does not stop there. `IcebergMetadata.equals` compares its refs map:
   
   ```java
   // IcebergMetadata.java:371
   && Objects.equals(refs, that.refs);
   ```
   
   `Map.equals` compares values with `equals`, so two `IcebergMetadata` carrying
   identical tags are reported as different.
   
   ### What changes
   
   ```java
   return snapshotId == that.snapshotId
           && Objects.equals(type, that.type)
           && Objects.equals(maxRefAgeMs, that.maxRefAgeMs);
   ```
   
   `Objects` is already imported. `type` is switched over at the same time — it 
is
   non-null in practice, but there is no reason for one field in the expression 
to
   be able to throw while the others cannot.
   
   ### Test
   
   `IcebergRefTest`, three cases, one per layer the defect reaches:
   
   * `testEqualsIsByValue` — two refs built with the same snapshot id are 
equal, and
     refs with different ids are not
   * `testEqualObjectsShareAHashCode` — the hashCodes match, and a `HashSet` 
holding
     both ends up with one element
   * `testMetadataComparesItsRefsByValue` — two `Map<String, IcebergRef>` with 
the
     same content compare equal, which is the shape `IcebergMetadata.equals` 
relies
     on
   
   Reverting only the `equals` body, on a forced clean rebuild of `paimon-core`,
   fails all three — at three different levels rather than all on the same
   assertion.
   
   Wider run: `*Iceberg*` across `paimon-core` — 167 tests, 0 failures.
   
   ### API and Format
   
   No change to any option, on-disk format or public signature.
   


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