Skip to content

Commit

Permalink
fix(interactive): Unified Query Cache for Gremlin and Cypher Queries (#…
Browse files Browse the repository at this point in the history
…3890)

1. Use a unified query cache for both Gremlin and Cypher queries.
2. Use the pre-optimization `LogicalPlan` as the query cache key to
avoid applying optimizations when computing the cache key.
3. Directly use the string from the plan explanation as the hash code,
instead of computing it from all attributes within each operator of the
plan. This is mainly due to the following reasons:
1. Avoid modifying the hash code each time when a new attribute is added
to an operator.
2. For some static attributes, Java does not allow overloading, and it
does not guarantee these attributes will remain unchanged after a
service restart, such as
[enum](https://stackoverflow.com/questions/19496751/enum-hash-code-calculation-are-not-the-same).
  • Loading branch information
shirly121 authored Sep 5, 2024
1 parent 609c970 commit 72faf82
Show file tree
Hide file tree
Showing 13 changed files with 101 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,16 @@ public GraphServer(
public void start() throws Exception {
ExecutionClient executionClient = ExecutionClient.Factory.create(configs, channelFetcher);
QueryIdGenerator idGenerator = new QueryIdGenerator(configs);
QueryCache queryCache = new QueryCache(configs);
if (!FrontendConfig.GREMLIN_SERVER_DISABLED.get(configs)) {
GraphPlanner graphPlanner =
new GraphPlanner(configs, new LogicalPlanFactory.Gremlin(), optimizer);
QueryCache queryCache = new QueryCache(configs, graphPlanner);
this.gremlinServer =
new IrGremlinServer(
configs,
idGenerator,
queryCache,
graphPlanner,
executionClient,
channelFetcher,
metaQueryCallback,
Expand All @@ -100,10 +101,14 @@ public void start() throws Exception {
if (!FrontendConfig.NEO4J_BOLT_SERVER_DISABLED.get(configs)) {
GraphPlanner graphPlanner =
new GraphPlanner(configs, new LogicalPlanFactory.Cypher(), optimizer);
QueryCache queryCache = new QueryCache(configs, graphPlanner);
this.cypherBootstrapper =
new CypherBootstrapper(
configs, idGenerator, metaQueryCallback, executionClient, queryCache);
configs,
idGenerator,
metaQueryCallback,
executionClient,
queryCache,
graphPlanner);
Path neo4jHomePath = getNeo4jHomePath();
this.cypherBootstrapper.start(
neo4jHomePath,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,20 +93,34 @@ public PlannerInstance instance(String query, IrMeta irMeta) {
if (mq != null) {
optCluster.setMetadataQuerySupplier(() -> mq);
}
return new PlannerInstance(query, optCluster, irMeta);
// build logical plan from parsed query
IrGraphSchema schema = irMeta.getSchema();
GraphBuilder graphBuilder =
GraphBuilder.create(
graphConfig, optCluster, new GraphOptSchema(optCluster, schema));

LogicalPlan logicalPlan = logicalPlanFactory.create(graphBuilder, irMeta, query);
return new PlannerInstance(query, logicalPlan, graphBuilder, irMeta);
}

public class PlannerInstance {
private final String query;
private final GraphOptCluster optCluster;
private final LogicalPlan parsedPlan;
private final GraphBuilder graphBuilder;
private final IrMeta irMeta;

public PlannerInstance(String query, GraphOptCluster optCluster, IrMeta irMeta) {
public PlannerInstance(
String query, LogicalPlan parsedPlan, GraphBuilder graphBuilder, IrMeta irMeta) {
this.query = query;
this.optCluster = optCluster;
this.parsedPlan = parsedPlan;
this.graphBuilder = graphBuilder;
this.irMeta = irMeta;
}

public LogicalPlan getParsedPlan() {
return parsedPlan;
}

public Summary plan() {
LogicalPlan logicalPlan =
ClassUtils.callException(() -> planLogical(), Code.LOGICAL_PLAN_BUILD_FAILED);
Expand All @@ -117,16 +131,7 @@ public Summary plan() {
}

public LogicalPlan planLogical() {
// build logical plan from parsed query
IrGraphSchema schema = irMeta.getSchema();
GraphBuilder graphBuilder =
GraphBuilder.create(
graphConfig,
this.optCluster,
new GraphOptSchema(this.optCluster, schema));

LogicalPlan logicalPlan = logicalPlanFactory.create(graphBuilder, irMeta, query);

LogicalPlan logicalPlan = parsedPlan;
// apply optimizations
if (logicalPlan.getRegularQuery() != null && !logicalPlan.isReturnEmpty()) {
RelNode before = logicalPlan.getRegularQuery();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;

import org.apache.calcite.plan.RelDigest;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.logical.LogicalValues;
import org.apache.calcite.rel.type.RelDataType;
Expand Down Expand Up @@ -136,7 +135,7 @@ public int hashCode() {
return Objects.hash(getDigest(regularQuery), procedureCall, returnEmpty, dynamicParams);
}

private RelDigest getDigest(RelNode rel) {
return rel == null ? null : rel.getRelDigest();
private String getDigest(RelNode rel) {
return rel == null ? null : rel.explain();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@

import com.alibaba.graphscope.common.config.Configs;
import com.alibaba.graphscope.common.config.FrontendConfig;
import com.alibaba.graphscope.common.ir.meta.IrMeta;
import com.alibaba.graphscope.common.ir.runtime.PhysicalPlan;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
Expand All @@ -32,34 +30,22 @@

public class QueryCache {
private final LoadingCache<Key, Value> cache;
private final GraphPlanner graphPlanner;

public QueryCache(Configs configs, GraphPlanner graphPlanner) {
this.graphPlanner = graphPlanner;
public QueryCache(Configs configs) {
int cacheSize = FrontendConfig.QUERY_CACHE_SIZE.get(configs);
this.cache =
CacheBuilder.newBuilder()
.maximumSize(cacheSize)
.build(
CacheLoader.from(
key -> {
PhysicalPlan physicalPlan =
key.plannerInstance.planPhysical(
key.logicalPlan);
GraphPlanner.Summary summary =
new GraphPlanner.Summary(
key.logicalPlan, physicalPlan);
return new Value(summary, null);
}));
.build(CacheLoader.from(key -> new Value(key.instance.plan(), null)));
}

public class Key {
public final GraphPlanner.PlannerInstance plannerInstance;
public final GraphPlanner.PlannerInstance instance;
public final LogicalPlan logicalPlan;

public Key(String query, IrMeta irMeta) {
this.plannerInstance = Objects.requireNonNull(graphPlanner.instance(query, irMeta));
this.logicalPlan = Objects.requireNonNull(this.plannerInstance.planLogical());
public Key(GraphPlanner.PlannerInstance instance) {
this.instance = instance;
this.logicalPlan = instance.getParsedPlan();
}

@Override
Expand All @@ -76,8 +62,8 @@ public int hashCode() {
}
}

public Key createKey(String query, IrMeta irMeta) {
return new Key(query, irMeta);
public Key createKey(GraphPlanner.PlannerInstance instance) {
return new Key(instance);
}

public static class Value {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public class GraphQueryExecutor extends FabricExecutor {
private final QueryIdGenerator idGenerator;
private final FabricConfig fabricConfig;
private final QueryCache queryCache;
private final GraphPlanner graphPlanner;

public GraphQueryExecutor(
FabricConfig config,
Expand All @@ -77,7 +78,8 @@ public GraphQueryExecutor(
QueryIdGenerator idGenerator,
IrMetaQueryCallback metaQueryCallback,
ExecutionClient client,
QueryCache queryCache) {
QueryCache queryCache,
GraphPlanner graphPlanner) {
super(
config,
planner,
Expand All @@ -92,6 +94,7 @@ public GraphQueryExecutor(
this.metaQueryCallback = metaQueryCallback;
this.client = client;
this.queryCache = queryCache;
this.graphPlanner = graphPlanner;
}

/**
Expand Down Expand Up @@ -124,7 +127,8 @@ public StatementResult run(
return super.run(fabricTransaction, statement, parameters);
}
irMeta = metaQueryCallback.beforeExec();
QueryCache.Key cacheKey = queryCache.createKey(statement, irMeta);
QueryCache.Key cacheKey =
queryCache.createKey(graphPlanner.instance(statement, irMeta));
QueryCache.Value cacheValue = queryCache.get(cacheKey);
Preconditions.checkArgument(
cacheValue != null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import com.alibaba.graphscope.common.client.ExecutionClient;
import com.alibaba.graphscope.common.config.Configs;
import com.alibaba.graphscope.common.ir.tools.GraphPlanner;
import com.alibaba.graphscope.common.ir.tools.QueryCache;
import com.alibaba.graphscope.common.ir.tools.QueryIdGenerator;
import com.alibaba.graphscope.common.manager.IrMetaQueryCallback;
Expand Down Expand Up @@ -55,18 +56,20 @@ public CypherBootstrapper(
QueryIdGenerator idGenerator,
IrMetaQueryCallback queryCallback,
ExecutionClient client,
QueryCache queryCache) {
QueryCache queryCache,
GraphPlanner graphPlanner) {
this.client = client;
this.externalDependencies =
createExternalDependencies(
graphConfig, idGenerator, queryCallback, client, queryCache);
graphConfig, idGenerator, queryCallback, client, queryCache, graphPlanner);
this.externalClassTypes =
Arrays.asList(
Configs.class,
QueryIdGenerator.class,
IrMetaQueryCallback.class,
ExecutionClient.class,
QueryCache.class);
QueryCache.class,
GraphPlanner.class);
}

@Override
Expand Down Expand Up @@ -94,9 +97,11 @@ private Dependencies createExternalDependencies(
QueryIdGenerator idGenerator,
IrMetaQueryCallback queryCallback,
ExecutionClient client,
QueryCache queryCache) {
QueryCache queryCache,
GraphPlanner graphPlanner) {
Dependencies dependencies = new Dependencies();
dependencies.satisfyDependencies(configs, idGenerator, queryCallback, client, queryCache);
dependencies.satisfyDependencies(
configs, idGenerator, queryCallback, client, queryCache, graphPlanner);
return dependencies;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import com.alibaba.graphscope.common.client.ExecutionClient;
import com.alibaba.graphscope.common.config.Configs;
import com.alibaba.graphscope.common.ir.tools.GraphPlanner;
import com.alibaba.graphscope.common.ir.tools.QueryCache;
import com.alibaba.graphscope.common.ir.tools.QueryIdGenerator;
import com.alibaba.graphscope.common.manager.IrMetaQueryCallback;
Expand Down Expand Up @@ -164,6 +165,7 @@ public void bootstrapServices() {
var metaQueryCallback = (IrMetaQueryCallback) resolve(IrMetaQueryCallback.class);
var executionClient = (ExecutionClient) resolve(ExecutionClient.class);
var queryCache = (QueryCache) resolve(QueryCache.class);
var graphPlanner = (GraphPlanner) resolve(GraphPlanner.class);
var fabricExecutor =
new GraphQueryExecutor(
fabricConfig,
Expand All @@ -177,7 +179,8 @@ public void bootstrapServices() {
idGenerator,
metaQueryCallback,
executionClient,
queryCache);
queryCache,
graphPlanner);
register(fabricExecutor, FabricExecutor.class);

register(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public IrTestOpProcessor(
Configs configs,
QueryIdGenerator idGenerator,
QueryCache queryCache,
GraphPlanner graphPlanner,
ExecutionClient executionClient,
ChannelFetcher fetcher,
IrMetaQueryCallback metaQueryCallback,
Expand All @@ -84,6 +85,7 @@ public IrTestOpProcessor(
configs,
idGenerator,
queryCache,
graphPlanner,
executionClient,
fetcher,
metaQueryCallback,
Expand Down Expand Up @@ -157,7 +159,9 @@ public ThrowingConsumer<Context> select(Context ctx) {
break;
case GremlinCalciteScriptEngineFactory.LANGUAGE_NAME:
QueryCache.Value value =
queryCache.get(queryCache.createKey(script, irMeta));
queryCache.get(
queryCache.createKey(
graphPlanner.instance(script, irMeta)));
GraphPlanner.Summary summary = value.summary;
ResultSchema resultSchema =
new ResultSchema(summary.getLogicalPlan());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import com.alibaba.graphscope.common.exception.FrontendException;
import com.alibaba.graphscope.common.intermediate.InterOpCollection;
import com.alibaba.graphscope.common.ir.meta.IrMeta;
import com.alibaba.graphscope.common.ir.tools.GraphPlanner;
import com.alibaba.graphscope.common.ir.tools.QueryCache;
import com.alibaba.graphscope.common.ir.tools.QueryIdGenerator;
import com.alibaba.graphscope.common.manager.IrMetaQueryCallback;
Expand Down Expand Up @@ -109,6 +110,7 @@ public class IrStandardOpProcessor extends StandardOpProcessor {
protected final IrMetaQueryCallback metaQueryCallback;
protected final QueryIdGenerator idGenerator;
protected final QueryCache queryCache;
protected final GraphPlanner graphPlanner;
protected final ExecutionClient executionClient;
protected Tracer tracer;
protected LongHistogram queryHistogram;
Expand All @@ -124,6 +126,7 @@ public IrStandardOpProcessor(
Configs configs,
QueryIdGenerator idGenerator,
QueryCache queryCache,
GraphPlanner graphPlanner,
ExecutionClient executionClient,
ChannelFetcher fetcher,
IrMetaQueryCallback metaQueryCallback,
Expand All @@ -143,6 +146,7 @@ public IrStandardOpProcessor(
this.metaQueryCallback = metaQueryCallback;
this.idGenerator = idGenerator;
this.queryCache = queryCache;
this.graphPlanner = graphPlanner;
this.executionClient = executionClient;
this.printThreshold = FrontendConfig.QUERY_PRINT_THRESHOLD_MS.get(configs);
this.opentelemetryIdGenerator = IdGenerator.random();
Expand Down Expand Up @@ -215,6 +219,7 @@ protected void evalOpInternal(
configs,
ctx,
queryCache,
graphPlanner,
executionClient,
jobId,
jobName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
public class LifeCycleSupplier implements Supplier<GremlinExecutor.LifeCycle> {
private final Configs configs;
private final QueryCache queryCache;
private final GraphPlanner graphPlanner;
private final ExecutionClient client;
private final Context ctx;
private final BigInteger queryId;
Expand All @@ -52,6 +53,7 @@ public LifeCycleSupplier(
Configs configs,
Context ctx,
QueryCache queryCache,
GraphPlanner graphPlanner,
ExecutionClient client,
BigInteger queryId,
String queryName,
Expand All @@ -61,6 +63,7 @@ public LifeCycleSupplier(
this.configs = configs;
this.ctx = ctx;
this.queryCache = queryCache;
this.graphPlanner = graphPlanner;
this.client = client;
this.queryId = queryId;
this.queryName = queryName;
Expand All @@ -76,6 +79,7 @@ public GremlinExecutor.LifeCycle get() {
.beforeEval(
b -> {
b.put("graph.query.cache", queryCache);
b.put("graph.planner", graphPlanner);
b.put("graph.meta", meta);
})
.withResult(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import com.alibaba.graphscope.common.exception.FrontendException;
import com.alibaba.graphscope.common.ir.meta.IrMeta;
import com.alibaba.graphscope.common.ir.tools.GraphPlanner;
import com.alibaba.graphscope.common.ir.tools.QueryCache;

import org.apache.tinkerpop.gremlin.jsr223.AbstractGremlinScriptEngineFactory;
Expand Down Expand Up @@ -69,8 +70,10 @@ public Object eval(String script, ScriptContext ctx) throws ScriptException {
try {
Bindings globalBindings = ctx.getBindings(ScriptContext.ENGINE_SCOPE);
QueryCache queryCache = (QueryCache) globalBindings.get("graph.query.cache");
GraphPlanner graphPlanner = (GraphPlanner) globalBindings.get("graph.planner");
IrMeta irMeta = (IrMeta) globalBindings.get("graph.meta");
QueryCache.Key cacheKey = queryCache.createKey(script, irMeta);
QueryCache.Key cacheKey =
queryCache.createKey(graphPlanner.instance(script, irMeta));
return queryCache.get(cacheKey);
} catch (FrontendException e) {
throw e;
Expand Down
Loading

0 comments on commit 72faf82

Please sign in to comment.