- Version:
master (ac0eb23)
- Storage Backend: any
- Mixed Index Backend: any
- Expected Behavior:
mgmt.updateIndex on a mixed index whose keys are all already in the target state either no-ops or reports a clear error, as it does for composite indexes.
- Current Behavior: it fails an internal
Preconditions.checkArgument with a message about RelationTypeVertex and composite indexes, which gives no indication of the real problem.
Details
For a mixed index, updateIndex builds keySubset from the fields whose current status is in the action's applicable set:
|
setStatus(schemaVertex, SchemaStatus.DISCARDED, keySubset); |
|
updatedTypes.add(schemaVertex); |
|
if (!keySubset.isEmpty()) updatedTypes.addAll(dependentTypes); |
|
future = new EmptyScanJobFuture(); |
|
break; |
|
case DISCARD_INDEX: |
|
if (index instanceof JanusGraphIndex && ((JanusGraphIndex) index).isMixedIndex()) { |
|
try { |
|
JanusGraphIndexWrapper indexWrapper = (JanusGraphIndexWrapper) index; |
|
MixedIndexType mixedIndex = (MixedIndexType) indexWrapper.getBaseIndex(); |
|
IndexSerializer.clearStore(mixedIndex, transaction.getTxHandle()); |
keySubset = new HashSet<>();
MixedIndexType mixedIndexType = (MixedIndexType) indexType;
Set<SchemaStatus> applicableStatus = updateAction.getApplicableStatus();
for (ParameterIndexField field : mixedIndexType.getFieldKeys()) {
if (applicableStatus.contains(field.getStatus()))
keySubset.add((PropertyKeyVertex) field.getFieldKey());
}
If no field matches, keySubset is empty. setStatus then routes to the vertex-level path:
|
private void setStatus(JanusGraphSchemaVertex vertex, SchemaStatus status, Set<PropertyKeyVertex> keys) { |
|
if (keys.isEmpty()) setStatusVertex(vertex, status); |
|
else setStatusEdges(vertex, status, keys); |
|
vertex.resetCache(); |
|
updateSchemaVertex(vertex); |
|
} |
|
|
|
private void setStatusVertex(JanusGraphSchemaVertex vertex, SchemaStatus status) { |
|
Preconditions.checkArgument(vertex instanceof RelationTypeVertex || vertex.asIndexType().isCompositeIndex()); |
private void setStatus(JanusGraphSchemaVertex vertex, SchemaStatus status, Set<PropertyKeyVertex> keys) {
if (keys.isEmpty()) setStatusVertex(vertex, status);
else setStatusEdges(vertex, status, keys);
...
}
private void setStatusVertex(JanusGraphSchemaVertex vertex, SchemaStatus status) {
Preconditions.checkArgument(vertex instanceof RelationTypeVertex || vertex.asIndexType().isCompositeIndex());
which is guaranteed to fail for a mixed index.
Note the asymmetry: the composite-index branch calls updateAction.isApplicableStatus(schemaVertex.getStatus()) first, which either returns early or throws the helpful "Update action [%s] cannot be invoked for index with status [%s]". The mixed-index branch performs no such validation, so it falls through into an unrelated precondition.
Realistic triggers, all of them idempotent schema scripts being re-run:
REGISTER_INDEX after all keys are already ENABLED — applicable status is {INSTALLED}, so nothing matches.
DISABLE_INDEX when no key is ENABLED/DISABLED/REGISTERED.
ENABLE_INDEX on a freshly created index whose keys are all INSTALLED.
Separately, and possibly worth its own issue: because the mixed-index branch never calls isApplicableStatus, DROP_INDEX on a live mixed index is not gated on DISCARDED either. schemaVertex.remove() runs regardless, dropping the schema vertex while leaving every document in the index backend orphaned with no JanusGraph-side handle to remove them.
Steps to Reproduce
mgmt = graph.openManagement();
mgmt.buildIndex("byName", Vertex.class).addKey(name).buildMixedIndex("search");
mgmt.commit();
ManagementSystem.awaitGraphIndexStatus(graph, "byName").call();
mgmt = graph.openManagement();
mgmt.updateIndex(mgmt.getGraphIndex("byName"), SchemaAction.REGISTER_INDEX); // keys already ENABLED
// IllegalArgumentException from setStatusVertex
Suggested Fix
Give the mixed-index branch the same guard the composite branch has: when keySubset is empty, either return null as the composite path does for an inapplicable status, or throw the descriptive isApplicableStatus error naming the action and the current field statuses.
master(ac0eb23)mgmt.updateIndexon a mixed index whose keys are all already in the target state either no-ops or reports a clear error, as it does for composite indexes.Preconditions.checkArgumentwith a message aboutRelationTypeVertexand composite indexes, which gives no indication of the real problem.Details
For a mixed index,
updateIndexbuildskeySubsetfrom the fields whose current status is in the action's applicable set:janusgraph/janusgraph-core/src/main/java/org/janusgraph/graphdb/database/management/ManagementSystem.java
Lines 1070 to 1080 in ac0eb23
If no field matches,
keySubsetis empty.setStatusthen routes to the vertex-level path:janusgraph/janusgraph-core/src/main/java/org/janusgraph/graphdb/database/management/ManagementSystem.java
Lines 1283 to 1291 in ac0eb23
which is guaranteed to fail for a mixed index.
Note the asymmetry: the composite-index branch calls
updateAction.isApplicableStatus(schemaVertex.getStatus())first, which either returns early or throws the helpful"Update action [%s] cannot be invoked for index with status [%s]". The mixed-index branch performs no such validation, so it falls through into an unrelated precondition.Realistic triggers, all of them idempotent schema scripts being re-run:
REGISTER_INDEXafter all keys are alreadyENABLED— applicable status is{INSTALLED}, so nothing matches.DISABLE_INDEXwhen no key isENABLED/DISABLED/REGISTERED.ENABLE_INDEXon a freshly created index whose keys are allINSTALLED.Separately, and possibly worth its own issue: because the mixed-index branch never calls
isApplicableStatus,DROP_INDEXon a live mixed index is not gated onDISCARDEDeither.schemaVertex.remove()runs regardless, dropping the schema vertex while leaving every document in the index backend orphaned with no JanusGraph-side handle to remove them.Steps to Reproduce
Suggested Fix
Give the mixed-index branch the same guard the composite branch has: when
keySubsetis empty, either returnnullas the composite path does for an inapplicable status, or throw the descriptiveisApplicableStatuserror naming the action and the current field statuses.