-
Notifications
You must be signed in to change notification settings - Fork 3.9k
[opt](point-query) reduce short circuit lookup cache usage #67228
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -32,18 +32,30 @@ | |
| import com.google.common.annotations.VisibleForTesting; | ||
| import com.google.common.base.Preconditions; | ||
| import com.google.common.collect.Maps; | ||
| import com.google.common.hash.Hasher; | ||
| import com.google.common.hash.Hashing; | ||
| import com.google.protobuf.ByteString; | ||
| import org.apache.thrift.TException; | ||
| import org.apache.thrift.TSerializer; | ||
|
|
||
| import java.nio.ByteBuffer; | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.Objects; | ||
| import java.util.UUID; | ||
| import java.util.concurrent.atomic.AtomicLong; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| public class ShortCircuitQueryContext { | ||
| // Number of buckets used to spread a hot query over multiple Backend | ||
| // LookupConnectionCache shards, avoiding single-shard lock contention. | ||
| private static final int CACHE_ID_BUCKET_NUM = 128; | ||
|
|
||
| // Round-robin bucket allocator, giving an even distribution across buckets | ||
| // regardless of connection id skew. | ||
| private static final AtomicLong CACHE_ID_BUCKET_COUNTER = new AtomicLong(0); | ||
|
|
||
| // Cached for better CPU performance, since serialize DescriptorTable and | ||
| // outputExprs are heavy work | ||
| public final Planner planner; | ||
|
|
@@ -110,14 +122,30 @@ public ShortCircuitQueryContext(Planner planner, Queriable analzyedQuery) throws | |
| TExprList exprList = new TExprList(exprs); | ||
| serializedOutputExpr = ByteString.copyFrom( | ||
| new TSerializer().serialize(exprList)); | ||
| this.cacheID = UUID.randomUUID(); | ||
| this.cacheID = genCacheID(serializedDescTable, serializedOutputExpr, serializedQueryOptions); | ||
| this.scanNode = olapScanNode; | ||
| this.tbl = this.scanNode.getOlapTable(); | ||
| this.tableName = this.scanNode.getTableNameInPlan(); | ||
| this.schemaVersion = this.tbl.getBaseSchemaVersion(); | ||
| this.analzyedQuery = analzyedQuery; | ||
| } | ||
|
|
||
| // Build a 128-bit cache identifier from serialized query structures and a | ||
| // round-robin bucket. Identical hot queries are intentionally spread across | ||
| // multiple Backend LookupConnectionCache shards to reduce lock contention and | ||
| // high sys CPU, while still bounding the number of cache entries. | ||
| private static UUID genCacheID(ByteString serializedDescTable, ByteString serializedOutputExpr, | ||
| ByteString serializedQueryOptions) { | ||
| int bucket = (int) Math.floorMod(CACHE_ID_BUCKET_COUNTER.getAndIncrement(), CACHE_ID_BUCKET_NUM); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P2] Rotate buckets per query identity, not from one JVM-global ordinal. With a fixed set of |
||
| Hasher hasher = Hashing.murmur3_128().newHasher(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P2] Generate this ID only when a request can actually send it. |
||
| hasher.putBytes(serializedDescTable.toByteArray()); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P2] Hash the existing |
||
| hasher.putBytes(serializedOutputExpr.toByteArray()); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P1] Preserve the nondeterministic-plan no-reuse boundary in this ID. |
||
| hasher.putBytes(serializedQueryOptions.toByteArray()); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P1] Include the schema generation in this cache identity. |
||
| hasher.putInt(bucket); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P2] Keep wide-block work out of the newly shared per-key mutex. Once independent connections converge on one of these 128 IDs, every warm hit uses the same |
||
| ByteBuffer buffer = ByteBuffer.wrap(hasher.hash().asBytes()); | ||
| return new UUID(buffer.getLong(), buffer.getLong()); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P2] Coalesce cold initialization for the new shared keys. BE currently does |
||
| } | ||
|
|
||
| @VisibleForTesting | ||
| ShortCircuitQueryContext(OlapTable tbl, String tableName, int schemaVersion, | ||
| long fileCacheQueryLimitBytes) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[P1] Do not share this mutable BE context across connections. The first and 129th identical prepared contexts on one FE (or bucket 0 on two FEs) now use the same UUID, so concurrent BE requests receive the same
Reusable. Only its block pool is locked: each request writesruntime_state()->set_timezone(...)and both execute the same originalVExprContextSPtrs, whose execution mutates context/function state. For example, A can set UTC, B overwrite Asia/Tokyo, and A's supportedfrom_unixtimepoint query formats with B's timezone; concurrent string/timezone access is also a C++ data race. Make the cached value immutable and clone/lease request-local runtime and expression state (or otherwise serialize the whole use) before collapsing IDs; adding timezone to the hash alone does not make same-timezone executions thread-safe.