perf(core): optimize queryVerticesByIds for only-one-id query#2859
perf(core): optimize queryVerticesByIds for only-one-id query#2859
Conversation
Change-Id: Ic6e01eb8e2b4c97c5263cfa0f78c58b6033435da
| tryQueryBackend = false; | ||
| if (vertex.expired()) { | ||
| vertex = null; | ||
| } else { |
There was a problem hiding this comment.
Consider extracting the local transaction lookup logic to reduce code duplication between single-ID and multi-ID paths. This pattern repeats in both vertex and edge queries:
private HugeVertex findVertexInLocalTx(Id id) {
if (this.removedVertices.containsKey(id)) {
return null; // Removed
}
HugeVertex vertex = this.addedVertices.get(id);
if (vertex == null) {
vertex = this.updatedVertices.get(id);
}
return (vertex != null && !vertex.expired()) ? vertex : null;
}This would simplify the control flow and make the code more maintainable.
| continue; | ||
| if (vertexIds.length == 1) { | ||
| Id id = HugeVertex.getIdValue(vertexIds[0]); | ||
|
|
There was a problem hiding this comment.
The multiple boolean flags (tryQueryBackend, foundLocal) make the control flow complex. Consider refactoring into a more readable structure:
private QueryResult<HugeVertex> querySingleVertex(Id id, HugeType type) {
if (id == null) {
return QueryResult.empty();
}
HugeVertex localVertex = findVertexInLocalTx(id);
if (localVertex != null) {
return QueryResult.fromLocal(localVertex);
}
if (isRemovedFromTx(id)) {
return QueryResult.empty();
}
return QueryResult.fromBackend(queryBackendVertex(id, type));
}This would eliminate the complex flag management and make the logic clearer.
| return this.queryVerticesByIds(vertexIds, adjacentVertex, checkMustExist, HugeType.VERTEX); | ||
| } | ||
|
|
||
| @Watched(prefix = "graph") |
There was a problem hiding this comment.
Missing test coverage for the single-ID optimization. Consider adding tests for:
- Performance verification: Ensure single-ID queries are faster than multi-ID queries
- Correctness tests:
- Single ID found in local transaction (added/updated)
- Single ID removed from local transaction
- Single ID not found anywhere
- Single null ID handling
- Edge cases: Single ID with expired vertex
Example test structure:
@Test
public void testSingleVertexQueryOptimization() {
// Test single vertex query performance and correctness
Id vertexId = IdGenerator.of("test-vertex");
// Test local transaction lookup
HugeVertex localVertex = constructVertex(vertexId);
tx.addVertex(localVertex);
Iterator<Vertex> result = tx.queryVertices(vertexId);
assertThat(result).hasNext();
assertThat(result.next()).isEqualTo(localVertex);
}| Map<Id, HugeVertex> vertices = new HashMap<>(vertexIds.length); | ||
| List<Id> ids; | ||
| Map<Id, HugeVertex> vertices; | ||
| boolean verticesUpdated = this.verticesInTxSize() > 0; |
There was a problem hiding this comment.
Consider optimizing the verticesUpdated check. Currently it calls this.verticesInTxSize() > 0 which may iterate through all transaction maps. For single-ID queries, you could check more efficiently:
// More efficient check for single ID
boolean hasLocalChanges = this.removedVertices.containsKey(id) ||
this.addedVertices.containsKey(id) ||
this.updatedVertices.containsKey(id);This would be O(1) instead of potentially O(n) for the transaction size calculation.
| (edge = this.updatedEdges.get(id)) != null) { | ||
| // Found from local tx | ||
| tryQueryBackend = false; | ||
| if (edge.expired()) { |
There was a problem hiding this comment.
The same optimization pattern is duplicated for edges and vertices. Consider creating a generic helper method to reduce code duplication:
private <T extends HugeElement> Map<Id, T> querySingleElement(
Id id,
HugeType type,
Map<Id, T> added,
Map<Id, T> updated,
Map<Id, T> removed,
Function<IdQuery, Iterator<T>> backendQuery) {
if (id == null) {
return ImmutableMap.of();
}
// Check local transaction state
if (removed.containsKey(id)) {
return ImmutableMap.of();
}
T element = added.get(id);
if (element == null) {
element = updated.get(id);
}
if (element != null && !element.expired()) {
return ImmutableMap.of(id, element);
}
// Query backend
IdQuery query = new IdQuery.OneIdQuery(type, id);
T backendElement = QueryResults.one(backendQuery.apply(query));
return backendElement == null ? ImmutableMap.of() : ImmutableMap.of(id, backendElement);
}This would eliminate the duplication between vertex and edge query methods.
There was a problem hiding this comment.
Pull Request Overview
This PR optimizes the queryVerticesByIds and queryEdgesByIds methods to handle single-ID queries more efficiently by introducing special handling for cases where only one ID is being queried.
- Optimizes single-ID queries by avoiding unnecessary data structures and using
ImmutableMapfor single results - Refactors the existing multi-ID query logic into conditional branches
- Updates monitoring annotations from "tx" to "graph" prefix and adds new
@Watchedannotations
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| if (vertex.expired()) { | ||
| vertex = null; | ||
| } else { | ||
| assert vertex != null; |
There was a problem hiding this comment.
This assertion is redundant since the condition vertex.expired() being false already guarantees that vertex is not null at this point.
| assert vertex != null; |
| } else { | ||
| assert edge != null; |
There was a problem hiding this comment.
This assertion is redundant since the condition edge.expired() being false already guarantees that edge is not null at this point.
| } else { | |
| assert edge != null; |
|
Due to the lack of activity, the current pr is marked as stale and will be closed after 180 days, any update will remove the stale label |
Change-Id: Ic6e01eb8e2b4c97c5263cfa0f78c58b6033435da