Skip to content

[pixels-retina] retina buffer rows duplicated in trino #1357

Description

@gengdy1545

Description

NodeServiceImpl.getRetinaList() returns one NodeInfo per hash-ring virtual-node slot instead of per physical Retina node. Since Trino builds Retina buffer splits as getRetinaList().size() × node.virtual.num, each memtable shard gets read repeatedly and rows are duplicated in query results.

In a single-Retina deployment with node.virtual.num=16, one inserted row is returned 15 times.

Root Cause

addNodeInternal puts node.virtual.num entries on the ring, each with a distinct virtualNodeId. getRetinaList() then does:

hashRing.values().stream().distinct().forEach(responseBuilder::addNodes);

NodeInfo includes virtualNodeId (proto/node.proto), so protobuf equals() never collapses entries of the same host. Each (address, i) also occupies a unique hash slot, so the values are already unique — .distinct() is a no-op.

The init log makes the intent explicit, and is likewise wrong:

logger.info("Initial hash ring loaded with {} physical nodes and {} virtual nodes.",
        hashRing.values().stream().distinct().count(), bucketNum);

Evidence

Probed against a live single-Retina cluster:

node.virtual.num = 16
getRetinaList().size() = 15
distinct addresses = [<host>]            // only 1 physical node
distinct virtualNodeIds = [0..9, 11..15] // vNodeId=10 lost to a hash collision
  • Trino split count for a table whose only data is in the Retina buffer: 240 = 15 × 16
  • The CDC sink called updateRecord exactly once and got one ack; insertRecord(..., vNodeId) stores the row once
  • Query for that key returns 15 identical rows

Because the factor comes from ring hash collisions, it is host-dependent (14/15/16), not a stable constant.

Impact

Affects unflushed Retina active-memtable data read via Trino buffer splits (retina.enable=true). Flushed object-storage files are unaffected.

Suggested Fix

Dedupe by physical address in getRetinaList(), and clear virtualNodeId in the response:

hashRing.values().stream()
        .collect(Collectors.toMap(
                NodeProto.NodeInfo::getAddress,
                n -> n.toBuilder().clearVirtualNodeId().build(),
                (a, b) -> a,
                LinkedHashMap::new))
        .values()
        .forEach(responseBuilder::addNodes);

Fix the init log to count distinct addresses as well.

getRetinaByBucket correctly returns a single ring entry with its virtualNodeId and should not change. pixels-trino's getBufferSplits() assumes physical hosts and is correct once this API is fixed — no change needed there.

Environment

1 physical Retina node; node.virtual.num=16, node.bucket.num=128, retina.enable=true.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions