JingsongLi commented on code in PR #10665:
URL: https://github.com/apache/paimon/pull/10665#discussion_r4345790126


##########
paimon-core/src/main/java/org/apache/paimon/index/DynamicBucketIndexMaintainer.java:
##########
@@ -72,7 +80,11 @@ public void notifyNewRecord(KeyValue record) {
         if (!(key instanceof BinaryRow)) {
             throw new IllegalArgumentException("Unsupported key type: " + 
key.getClass());
         }
-        boolean changed = hashcode.add(key.hashCode());
+        // only DELETE removes a key: -U is the first half of an update, and 
+U / +I write the key
+        boolean changed =
+                removeDeletedKeys && record.valueKind() == RowKind.DELETE
+                        ? hashcode.remove(key.hashCode())

Review Comment:
   [P1] Keep routing for live keys that share a deleted key hash
   
   The persisted set contains 32-bit hashes, not distinct primary keys. 
Removing a hash on DELETE also removes the routing of any other live key with 
that hash. I reproduced this with BIGINT keys 187054415718944 and 
562722320285643 (both hash to -1755412784): write both into bucket 1, delete 
the first plus a filler in bucket 0, commit and reopen the assigner/writer, 
then update the surviving key. With this option on, the survivor moves to 
bucket 0 and the table returns both its stale value 20 and new value 99. The 
option-off control retains bucket 1 and returns only 99. This is primary-key 
data corruption beyond the documented approximate row count. Preserve 
collision-safe routing across deletes/recovery, or use sufficient key/refcount 
information to prove no live key owns the hash before removing it.



##########
paimon-core/src/main/java/org/apache/paimon/table/sink/RowPartitionKeyExtractor.java:
##########
@@ -48,4 +49,10 @@ public BinaryRow partition(InternalRow record) {
     public BinaryRow trimmedPrimaryKey(InternalRow record) {
         return trimmedPrimaryKeyProjection.apply(record);
     }
+
+    /** Only {@link RowKind#DELETE} deletes a key: {@code -U} is the first 
half of an update. */
+    @Override
+    public boolean isDelete(InternalRow record) {
+        return record.getRowKind() == RowKind.DELETE;

Review Comment:
   [P2] Use the writer effective row kind for slot accounting
   
   `rowkind.field` is allowed with this option, but TableWriteImpl derives the 
actual kind using RowKindGenerator after the assigner, while this method only 
reads the incoming row metadata. With default local merge disabled, `+I(id=1, 
op='-D')` is deleted by the real writer yet is not recognized here, so a later 
key opens bucket 2 instead of reusing bucket 0. Conversely a metadata DELETE 
with op='+I' frees a still-live slot. This was reproduced with current branch 
extractor/assigner/writer calls and committed readback. Apply the same 
effective-kind logic as the writer, or reject this configuration until it is 
supported.



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