[flink] Support custom lookup shuffle by bucket for Flink 2.x#3675
[flink] Support custom lookup shuffle by bucket for Flink 2.x#3675naivedogger wants to merge 3 commits into
Conversation
Implement SupportsLookupCustomShuffle (FLIP-462) in the Flink 2.2 connector so lookup-join probe rows are repartitioned by Fluss bucket key, co-locating rows of the same bucket on the same lookup subtask (better cache locality, lower RPC fan-out). The ability is added only in fluss-flink-2.2 via a same-FQN LookupShuffleSourceAdapter that shadows the common no-op at package time, keeping fluss-flink-common compilable against Flink 1.x (SupportsLookupCustomShuffle does not exist before Flink 2.0). The partitioner mirrors client-side bucketing (bucket-key encoding + BucketingFunction). Enabled for full primary-key lookups only.
6d33a44 to
bd17dfa
Compare
zuston
left a comment
There was a problem hiding this comment.
thanks for your work! @naivedogger . I have a minor question about this.
which improves lookup cache locality
Could you help describe more about this? I haven't seen the similar lookup cache related code.
|
Thanks @zuston for your comment! For some context: I'm trying to help enhance Fluss's Flink connector, and this feature is similar to Paimon's Large Scale Lookup (Fixed Bucket) — it only kicks in on Flink 2.0+ and when the join keys contain all bucket keys. What shuffle lookup buys us: rows of the same bucket are routed to the same lookup subtask, so each subtask only deals with its own buckets. This way, when partial caching is enabled, each subtask only needs to cache its own slice of the table, giving a potentially better cache hit ratio and less duplicated cached data across subtasks. Here the cache refers to Flink's built-in lookup caching, not a Fluss-specific one — that's probably why it was hard to spot. |
|
Thanks for your detailed explanation, @naivedogger. Much appreciated. 🙏 |
Purpose
Linked issue: close #3670
Implement
SupportsLookupCustomShuffle(FLIP-462) in the Flink 2.2 connector so alookup-join probe stream is repartitioned by the Fluss bucket key. Rows that map to the
same Fluss bucket are co-located on the same lookup subtask, which improves lookup cache
locality and lowers RPC fan-out. The feature is opt-in through Flink's existing
LOOKUP('shuffle'='true')join hint; behaviour is unchanged when the hint is absent.Brief change log
FlinkLookupShuffleTableSource(extendsFlinkTableSource,implements
SupportsLookupCustomShuffle).getPartitioner()returns a bucket-basedpartitioner for full primary-key lookups;
copy()preserves the subtype so the abilitysurvives planner copies.
FlussLookupInputPartitioner, which mirrors client-sidebucketing (bucket-key encoding +
BucketingFunction, selected by the table's data-lakeformat) and routes
bucketId % numPartitions, matching howPrimaryKeyLookuperbuckets.LookupShuffleSourceAdapterin bothfluss-flink-common(no-op) andfluss-flink-2.2(wraps the source). The 2.x classshadows the common one at package time, keeping
fluss-flink-commoncompilable againstFlink 1.20 (
SupportsLookupCustomShuffledoes not exist before Flink 2.0). This followsthe existing
*Adapterpattern in the module.FlinkTableSource: add a copy constructor as the single source oftruth for state cloning (
copy()now delegates to it); stash theLookupNormalizercomputed in
getLookupRuntimeProvider()for later use bygetPartitioner(); expose a fewprotectedaccessors for the 2.x variant.FlinkTableFactory: route the created source throughLookupShuffleSourceAdapter.maybeWithCustomShuffle(...).Tests
FlussLookupInputPartitionerTest(unit, 4 cases):partition()equalsbucket % numPartitions,is deterministic, routes same-bucket keys to the same partition, and handles a composite
bucket key — verified against an independent bucket oracle.
Flink22LookupShuffleITCase(IT, 3 cases):StreamGraphand assert thelookup-join probe edge is partitioned by Flink's
RowDataCustomStreamPartitioner(i.e.our partitioner was actually applied) only when the hint is present; plus result parity
with/without the hint (custom shuffle is a pure distribution optimization).
testSameBucketKeysAreRoutedToSameSubtask: end-to-end viapartitionCustomusing theproduction partitioner — same bucket → same subtask,
subtask == bucketId % parallelism,and keys spread across more than one subtask.
mvn clean verifypasses for the affected modules (fluss-flink-common,fluss-flink-2.2);spotless + checkstyle clean.
API and Format
No changes to public Fluss API or storage/wire format. This only adds a Flink 2.x
DynamicTableSourceability (SupportsLookupCustomShuffle) on the Flink connector side.Documentation
No user-facing docs required; the feature is opt-in via the standard Flink
LOOKUP('shuffle'='true')hint. Current scope/limitations:(partition, bucket)alignment).bucket.numsilently keeps the defaultdistribution.