初步调试
回顾
首先我们通过 debug 官方的 GraphOfGod 大概进行一个简单的调试,然后我们仔细查看 janusgraph 调用栈,分析了关键类。
这次我们主要看看schema 的建立过程,我们上次分析已经知道,其实 schema也是以Vertex的方式存储在内存和数据库中的。
通过 CacheVertex 的子类 JanusGraphSchemaVertex 实现。JanusGraphSchemaVertex 有两个个子类,
AbstractElement (org.janusgraph.graphdb.internal)
AbstractVertex (org.janusgraph.graphdb.vertices)
StandardVertex (org.janusgraph.graphdb.vertices)
CacheVertex (org.janusgraph.graphdb.vertices)
JanusGraphSchemaVertex (org.janusgraph.graphdb.types.vertices)
RelationTypeVertex (org.janusgraph.graphdb.types.vertices)
PropertyKeyVertex (org.janusgraph.graphdb.types.vertices)
EdgeLabelVertex (org.janusgraph.graphdb.types.vertices)
VertexLabelVertex (org.janusgraph.graphdb.types)
中间省略了一些接口。
所以每次 new 一个 EdgeLabelVertex、VertexLabelVertex、PropertyKeyVertex 的时候,调用栈会非常深。
我们大概查看一下这些类的功能。
AbstractElement
只有一个属性:private long id; 这个id是唯一的。小于0 的是临时id,事务提交时候会分配大于0的id,等于0的是虚拟的并不存在的,大雨0的是物理persist的。
InternalVertex
图上没有展示 InternalVertex ,这是一个接口,继承自 JanusGraphVertex 和 InternalElement。凡是带 Internal 的都是比原来的多一个 janus 专属方法的类。
所以 InternalVertex 也是比 JanusGraphVertex 多一些 janus 专属的方法 例如: removeRelation addRelation tx()。
JanusGraphVertex 中则是 janus 和 gremin 都会有的方法 , 例如 addEdge property label。
InternalVertex 有 query() 等方法,
AbstractVertex
AbstractVertex 继承自 AbstractElement 和 InternalVertex,
AbstractVertex 比 AbstractElement 的 id 基础上多了一个 StandardJanusGraphTx tx 的属性。也就是多了一个事务空值对象。
StandardVertex
StandardVertex 继承自 AbstractVertex,多了一个 lifecycle 属性和 volatile AddedRelationsContainer addedRelations 属性。应该是通过缓存空值。
CacheVertex
CacheVertex 继承自 StandardVertex 。多了一个 queryCache 属性。
JanusGraphSchemaVertex
JanusGraphSchemaVertex 就是保存 Schema 的 Vertex ,分为两类 RelationTypeVertex 和 VertexLabelVertex。其中 RelationTypeVertex 分为 PropertyKeyVertex 和 EdgeLabelVertex。
预览
schema 操作是通过 ManagementSystem <: JanusGraphManagement 完成的。ManagementSystem 内容很复杂,上次已经大概看了他的方法和属性,这次我们着重看一下方法的实现,首先还是再次浏览一下方法。
属性
private static final String CURRENT_INSTANCE_SUFFIX = "(current)";
private final StandardJanusGraph graph;
private final Log sysLog;
private final ManagementLogger mgmtLogger;
private final KCVSConfiguration baseConfig;
private final TransactionalConfiguration transactionalConfig;
private final ModifiableConfiguration modifyConfig;
private final UserModifiableConfiguration userConfig;
private final SchemaCache schemaCache;
private final StandardJanusGraphTx transaction;
private final Set<JanusGraphSchemaVertex> updatedTypes;
private final List<Callable<Boolean>> updatedTypeTriggers;
private final Instant txStartTime;
private boolean graphShutdownRequired;
private boolean isOpen;
构造方法
基本都是直接赋值。StandardJanusGraph 的 openManagement 方法返回一个 ManagementSystem 。
Instances 操作
getOpenInstancesInternal
getOpenInstances
forceCloseInstance
判断正在运行的 instance 。
commit 和 rollback
commit 方法有四步。
- 判断 transactionalConfig 是否变化,如果变化,将变化写出。
- transactionalConfig.commit();
- transaction.commit();
- 判断 updatedTypes 是否有更新,进行 expire 操作。
rollback 方法,则很简单。直接调用两个 transaction 的 callback ,然后 close。
getSchemaElement
这个方法返回一个 JanusGraphSchemaElement ,但是实际上返回的是 RelationTypeIndexWrapper 或者 JanusGraphIndexWrapper ,原因未知,这两个类上一节介绍过,。
buildRelationTypeIndex
包括 buildPropertyIndex 和 buildEdgeIndex。
步骤都是先 生成对应的 RelationTypeMaker,然后 make,然后调用 addSchemaEdge, 最后调用 updateIndex
getRelationIndex
得到 RelationType 的 Index。
调用 QueryUtil.getVertices(transaction, BaseKey.SchemaName, JanusGraphSchemaCategory.getRelationTypeName(composedName))
然后 return new RelationTypeIndexWrapper((InternalRelationType) v);
getRelationIndexes
得到所有的 Indexs。
getGraphIndexDirect
直接调用 transaction.getSchemaVertex(JanusGraphSchemaCategory.GRAPHINDEX.getSchemaName(name));
得到 GraphIndex ,GraphIndex 和 RelationTypeIndex 不一样,一个是基于关系的,前者是基于属性的。
getGraphIndexes
返回所有的 GraphIndex
createMixedIndex
调用 JanusGraphSchemaVertex indexVertex = transaction.makeSchemaVertex(JanusGraphSchemaCategory.GRAPHINDEX, indexName, def); 得到 vertex
调用 addSchemaEdge(indexVertex, (JanusGraphSchemaVertex) constraint, TypeDefinitionCategory.INDEX_SCHEMA_CONSTRAINT, null); 添加关系
然后调用 updateSchemaVertex(indexVertex);
最终 new JanusGraphIndexWrapper(indexVertex.asIndexType());
可以看出,这个方法其实只是添加了一个顶点,然后和另一个 constraint 顶点简历了一条关系。
addIndexKey
给已有的 index 添加一个key,这个应该很复杂,我们先跳过。
createCompositeIndex
创建 CompositeIndex ,GrpahIndex 分为 CompositeIndex 和 mixedIndex
创建过程也是 addSchemaEdge , updateSchemaVertex,updateIndex
InnerClass
很多内部类:
IndexBuilder -- 构建 Index
EmptyIndexJobFuture -- 提交的job
UpdateStatusTrigger -- 更新status的触发器
IndexJobStatus -- job 的 status
IndexIdentifier --标识
addSchemaEdge
上面好几个方法都会调用 addSchemaEdge updateSchemaVertex updateIndex ,我们看看这三个方法。
addSchemaEdge 是私有方法,应该是在内部会调用的。根据名字可以得出这个方法是添加边,而且添加的是 schema 的边,
我们之前已经知道实际上 schema 都是保存为 vertex,而现在就是给这些 Vertex 添加 Edge,这个边的 EdgeLabel 是 BaseLabel.SchemaDefinitionEdge。
例如 某个 PropertyKey 添加一个 Index ,实际上会有两个 SchemaVertex,然后给他们建立一个关系。
我们可以通过查看方法调用时机,基本上是修改 index 或者 schemaVertex ,一般与 updateSchemaVertex 或者 updateIndex 配合执行。
方法大概步骤就是调用 transaction.addEdge(out, in, BaseLabel.SchemaDefinitionEdge) 得到 Edge,
然后调用 edge.property(BaseKey.SchemaDefinitionDesc.name(), desc);最后返回 edge。
updateSchemaVertex
就一句话 transaction.updateSchemaVertex(schemaVertex);
updateIndex
IndexJobFuture updateIndex(Index index, SchemaAction updateAction)
SchemaAction 是一个枚举,包括 REGISTER_INDEX REINDEX ENABLE_INDEX DISABLE_INDEX REMOVE_INDEX 。
IndexJobFuture 代表的是提交了的 job,等待返回结果。
方法步骤:
JanusGraphSchemaVertex schemaVertex = getSchemaVertex(index);
更新 dependentTypes ,实际上就是为了更新 updatedTypes。
根据不同的请求,调用 setStatus setUpdateTrigger setJob 。这个过程很复杂,后面再讲解。
编码调试
ManagementSystem 构造方法
调试整个过程总是很麻烦的,我们只能专注某些部分,首先我们主要看一下 ManagementSystem 的构造过程,和使用细节。
打断点进入构造方法,看这一句代码:this.transaction = (StandardJanusGraphTx) graph.buildTransaction().disableBatchLoading().start();
一步一步进入调用栈
this.transaction = (StandardJanusGraphTx) graph.buildTransaction().disableBatchLoading().start();
return graph.newTransaction(immutable);
tx.setBackendTransaction(openBackendTransaction(tx));
return backend.beginTransaction(tx.getConfiguration(), retriever);
在 Backend 的 beginTransaction 方法停下来,首先看看 StoreTransaction tx = storeManagerLocking.beginTransaction(configuration)
的调用栈:
// 这个 ExpectedValueCheckingStoreManager 继承自 KCVSManagerProxy ,它内部有个 KeyColumnValueStoreManager manager 。显然是代理模式,当然也可以认为是装饰模式。
StoreTransaction tx = storeManagerLocking.beginTransaction(configuration);
StoreTransaction inconsistentTx = manager.beginTransaction(configuration);
return new CassandraTransaction(config);
StoreTransaction strongConsistentTx = manager.beginTransaction(consistentTxCfg);
return new CassandraTransaction(config);
ExpectedValueCheckingTransaction wrappedTx = new ExpectedValueCheckingTransaction(inconsistentTx, strongConsistentTx, maxReadTime);
return wrappedTx;
可以看出 tx 的大概构造,里面有两个 CassandraTransaction 一个是强一致的,一个是非强一致的。
然后是 CacheTransaction cacheTx = new CacheTransaction(tx, storeManagerLocking, bufferSize, maxWriteTime, configuration.hasEnabledBatchLoading());
// CacheTransaction 继承自 StoreTransaction 和 LoggableTransaction ,内部有一个 StoreTransaction 对象,显然也是代理模式或者装饰模式。
CacheTransaction cacheTx = new CacheTransaction(tx, storeManagerLocking, bufferSize, maxWriteTime, configuration.hasEnabledBatchLoading());
就是一堆赋值。
CacheTransaction 内部有一个 StoreTransaction 也就是 上面的 tx, 然后还有一个 StoreManager storeManagerLocking。
然后是 indexTx.put(entry.getKey(), new IndexTransaction(entry.getValue() indexKeyRetriever.get(entry.getKey()), configuration, maxWriteTime));
indexTx.put(entry.getKey(), new IndexTransaction(entry.getValue(), indexKeyRetriever.get(entry.getKey()), configuration, maxWriteTime));
new KeyInformation.IndexRetriever() {
...省略代码}
new IndexTransaction()
index.<