janusgraph源码分析4-schema过程Debug

本文深入探讨JanusGraph的源码,详细分析了schema的创建过程,涉及AbstractElement、InternalVertex等关键类的调试,揭示了如何通过ManagementSystem构建和管理schema。内容包括构造方法、事务处理、索引操作等,展示了schema如何以Vertex方式存储,并通过CacheVertex的子类JanusGraphSchemaVertex实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

初步调试

回顾

首先我们通过 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 方法有四步。

  1. 判断 transactionalConfig 是否变化,如果变化,将变化写出。
  2. transactionalConfig.commit();
  3. transaction.commit();
  4. 判断 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.<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值