From 544f1bcc6e3cdc381689fbbf4b9ae6d79c756baf Mon Sep 17 00:00:00 2001 From: imbajin Date: Mon, 5 Oct 2026 01:40:32 +0800 Subject: [PATCH 1/4] docs: explain shared foundation migration - document bilingual ownership and Java/SPI changes - describe coordinated upgrades and legacy data limits - align dependency inventory prerequisites and links --- .../contribution-guidelines/contribute.md | 17 +- content/cn/docs/guides/custom-plugin.md | 3 + .../guides/shared-foundation-migration.md | 141 +++++++++++++++++ .../contribution-guidelines/contribute.md | 18 ++- content/en/docs/guides/custom-plugin.md | 4 + .../guides/shared-foundation-migration.md | 149 ++++++++++++++++++ data/version_routes.json | 14 ++ 7 files changed, 340 insertions(+), 6 deletions(-) create mode 100644 content/cn/docs/guides/shared-foundation-migration.md create mode 100644 content/en/docs/guides/shared-foundation-migration.md diff --git a/content/cn/docs/contribution-guidelines/contribute.md b/content/cn/docs/contribution-guidelines/contribute.md index 4254edc3a4..7e0a74c77f 100644 --- a/content/cn/docs/contribution-guidelines/contribute.md +++ b/content/cn/docs/contribution-guidelines/contribute.md @@ -53,6 +53,8 @@ hugegraph-server/hugegraph-core/src/main/java/org/apache/hugegraph/ 上述名称来自根 `pom.xml` 及各子项目 `pom.xml`;代码结构以正在使用的分支为准。 +计划用于 1.8.0 的整合见[共享基础模块职责和 Java 迁移指南](/cn/docs/guides/shared-foundation-migration/)。 + 先运行与改动直接相关的测试。Server 常用测试入口如下: ```bash @@ -74,9 +76,18 @@ GitHub 已不支持通过用户名和密码直接推送代码。需要使用个 提交第三方依赖时,还要同步发行包中的许可证信息: -1. 把依赖的许可证文件放入 `hugegraph-server/hugegraph-dist/release-docs/licenses/`。 -2. 更新 `hugegraph-server/hugegraph-dist/release-docs/LICENSE`;依赖包含 NOTICE 时,同时更新 `NOTICE`。 -3. 运行 `hugegraph-server/hugegraph-dist/scripts/dependency/regenerate_known_dependencies.sh`,更新已知依赖清单。 +1. 把依赖的许可证文件放入 `install-dist/release-docs/licenses/`。 +2. 更新 `install-dist/release-docs/LICENSE`;依赖包含 NOTICE 时,同时更新 `NOTICE`。 +3. 在仓库根目录,先准备当前 revision 的 Server、PD、Store 发行包库,再生成依赖清单: + + ```bash + mvn install -DskipTests -Dmaven.javadoc.skip=true + bash install-dist/scripts/dependency/regenerate_known_dependencies.sh + ``` + + 此构建跳过测试。依赖收集同时覆盖 Maven runtime 依赖和实际发行包中的平铺、嵌套 jar, + 包括 Spring Boot repackaging 引入的 `BOOT-INF/lib` 依赖。缺失发行包必须使收集失败;只检查 POM 不能证明完整发行依赖集。 + 核查 `install-dist/scripts/dependency/known-dependencies.txt` 的严格增删对比及对应 license/NOTICE,并按需覆盖发布平台和 profile 变体。 ## 提交 Pull Request diff --git a/content/cn/docs/guides/custom-plugin.md b/content/cn/docs/guides/custom-plugin.md index fb3703925c..df2f5275fd 100644 --- a/content/cn/docs/guides/custom-plugin.md +++ b/content/cn/docs/guides/custom-plugin.md @@ -4,6 +4,9 @@ linkTitle: "HugeGraph Plugin" weight: 3 --- +插件接入计划用于 1.8.0 的共享基础模块 API 时,请按[Java/SPI 迁移指南](/cn/docs/guides/shared-foundation-migration/)调整并使用配套 artifacts 重新编译。 +下方示例描述 1.7.0 API。 + ### 背景 1. HugeGraph 不仅开源开放,而且要做到简单易用,一般用户无需更改源码也能轻松增加插件扩展功能。 diff --git a/content/cn/docs/guides/shared-foundation-migration.md b/content/cn/docs/guides/shared-foundation-migration.md new file mode 100644 index 0000000000..79dc4df780 --- /dev/null +++ b/content/cn/docs/guides/shared-foundation-migration.md @@ -0,0 +1,141 @@ +--- +title: "共享基础模块:1.8.0 迁移指南" +linkTitle: "1.8.0 Java 与升级迁移" +description: "准备 1.8.0 共享基础模块整合所需的 Java 集成调整,以及 Server、PD、Store 同步升级。" +weight: 8 +--- + +本文说明计划用于 1.8.0 的共享基础模块整合,不代表版本已发布或已满足发布条件。 +实现与网站文档需要配套审查、协调合并;下游发布仍待完成。 +源迁移说明和兼容性样本见[实现 PR](https://github.com/apache/hugegraph/pull/3270)。 + +## 模块职责 + +`hugegraph-core` 继续作为图引擎。`hugegraph-struct` 统一维护共享模型和编解码,`hugegraph-common` 维护通用工具。 + +```text +Server core ──> struct ──> common +Store core ──> struct +PD service ──> common +``` + +图中展示共享基础模块边界,不是完整依赖树。Server 的 HStore 适配器仍需要 PD、Store 客户端;Store 保留网络、PD 客户端和存储依赖。 +PD、Store 不应依赖图引擎,struct 不应依赖 PD 客户端或 core。 + +| 能力 | 维护模块 | +|---|---| +| ID、schema 元数据、类型码、查询、基础元素、属性与字节编解码、索引构建和分词器 | struct | +| 事务、遍历、任务、schema 修改、后端适配器和索引更新编排 | core | +| 基于 PD 的 schema 访问、监听器和缓存生命周期(`SchemaGraph`/`SchemaDriver`) | Store | +| JWT 签名与校验、共享认证常量和 RPC 配置接口 | common | + +Core、Store 实现 `HugeGraphSupplier`,向共享代码提供 schema、配置和时钟访问能力,避免 struct 引入图引擎依赖。 +共享基础元素统一持有状态和邻接关系;`HugeVertex`/`HugeEdge` 保留引擎行为以及基于同一状态的包装对象身份。 +Core 序列化器保留后端适配逻辑并委托共享编解码。共享索引与 OLAP 选择接收调用方提供的 schema 候选和结果接收器; +事务写入及后端能力检查仍由 core 负责。 + +## 重新编译 Java 集成和插件 + +使用配套的 1.8.0 artifacts 重新编译受影响的应用、插件和内部集成。类型迁移同时改变 import 和方法描述符, +只调整源码 import 不能让旧的已编译集成具备二进制兼容性。被移除的 core 类没有通用兼容包。 +[插件示例](/cn/docs/guides/custom-plugin/)仍以 1.7.0 为例;接入迁移后的 API 时,需应用以下调整。 + +| 原入口 | 共享入口或必要调整 | +|---|---| +| `org.apache.hugegraph.backend.id.*` | `org.apache.hugegraph.id.*` | +| `org.apache.hugegraph.schema.*` 元数据 | `org.apache.hugegraph.struct.schema.*` | +| `org.apache.hugegraph.backend.query.*` | `org.apache.hugegraph.query.*` | +| `org.apache.hugegraph.backend.store.Shard` | `org.apache.hugegraph.backend.Shard` | +| `org.apache.hugegraph.backend.store.BackendEntry.BackendColumn` | `org.apache.hugegraph.backend.BackendColumn` | +| `org.apache.hugegraph.structure.HugeIndex` | `org.apache.hugegraph.structure.Index` | +| `HugeGraph.sameAs(HugeGraph)` | `HugeGraph.sameAs(HugeGraphSupplier)` | +| 后端序列化器中的共享字节与编码逻辑 | `org.apache.hugegraph.serializer.*` | +| Core `HugeException` | `org.apache.hugegraph.exception.HugeException` | +| `org.apache.hugegraph.SchemaGraph`/`SchemaDriver` | `org.apache.hugegraph.store.schema.*` | + +表格描述职责迁移,不能直接批量替换整个包。Schema 修改 builder 和后端专用序列化器仍在 core。 +客户端 REST DTO 保持独立,包括 Toolchain 的 `org.apache.hugegraph.structure.graph.Shard`。 + +外部 `HugeGraph` 实现需要将 `sameAs` override 参数改为 `org.apache.hugegraph.HugeGraphSupplier`,没有保留旧描述符的重载。 +`GraphSerializer.writeIndex`/`readIndex` 改为接收或返回共享 `Index`,自定义序列化器和调用方需同步修改签名。 +自定义 `HugeElement` 子类需通过 `element()` 提供共享 `BaseElement` 状态,通过 `wrapProperty` 适配属性; +不要恢复已移除的引擎状态字段或维护第二套邻接集合。 + +`org.apache.hugegraph.rpc.RpcServiceConfig4Client` 和 `RpcServiceConfig4Server` 统一由 common 维护。 +全限定类名和签名不变,这项迁移无需调整 RPC import 或配置。 +`org.apache.hugegraph.auth.TokenGenerator` 也位于 common,调用方提供签名密钥,并将 JWT 失败转换为各服务原有的响应。 +Common 中的 JWT 依赖为 optional,直接使用 JWT 的模块需显式声明所需库。 + +自定义 Gremlin `classImports` 和脚本需将 `org.apache.hugegraph.backend.id.IdGenerator` 改为 `org.apache.hugegraph.id.IdGenerator`。 +除编译 Java 源码外,还需启动发行包中的 Gremlin 服务,验证这些仅存在于配置中的引用。 + +### 下游异常和 classpath + +| 原下游入口 | 必要调整 | +|---|---| +| Hubble `org.apache.hugegraph.exception.HugeException` | `org.apache.hugegraph.exception.HubbleException` | +| Java client `org.apache.hugegraph.exception.NotSupportException` | `org.apache.hugegraph.exception.ClientNotSupportException` | +| Hubble 自带的 `org.apache.hugegraph.license.MachineInfo` | 保留 import,使用 common 的实现 | + +同步修改 import、构造调用和 catch。`HubbleException` 保留 `RuntimeException` 父类和字符串构造器; +`ClientNotSupportException` 保留 `ClientException` 父类及 message/cause、message/arguments 构造器。 +Computer 的 `HgkvDirImpl` 是受影响的 client 异常调用方。Struct 中名称相近的异常具有不同契约,不能替代这些下游异常。 +Loader 的 REST 边界和独立 client DTO 不需要整体改为 struct。 + +这些 Toolchain、Computer 调整仍需配套下游发布、构建和发行包 classpath 检查,才能确认迁移就绪。 +只扫描源码不能证明发行 jar 中没有残留或传递依赖引入的同名类冲突。 + +## 同步升级服务 + +将 Server、PD、Store 一起升级到配套发行版本,本次迁移不支持混合版本滚动升级。 +升级前按照已有[备份恢复流程](/cn/docs/guides/backup-restore/)备份图数据,并保留部署配置。 +先在验证环境中检查历史数据读取和发行包服务启动,再用于生产。本次整合不提供自动数据重写操作。 + +既有类型码、ID 与属性字节、普通 Server 创建的索引 key、TTL 格式和配置默认值保持不变。 +字符串 ID 统一按 Java UTF-16 顺序比较,包括从 UTF-8 字节加载的 ID;持久化 ID 字节不变。 +类型迁移后,查询 JSON 中的历史 Java 类名及受支持的 Kryo 数据仍需要兼容解码。 + +Schema map 解码保留原 wire 字段和 ID 身份,并修复主键、edge sort-key 列表的顺序及重复项丢失问题。 +匹配的冗余端点元数据和旧版仅含端点字段的 map 可以读取;冲突或格式错误的端点元数据会被拒绝。 +基础元素分类也保留生产方上下文,storage 将 `~variables` 视为任务数据;engine 将 `~server`、`~role_data` 视为服务端数据, +将 `~variables` 视为普通顶点数据。两者都将 `~task`、`~taskresult` 视为任务数据。 + +### 检查旧索引行 + +旧 Store `IndexBuilder` 将长文本截短为 20 个字符,而 Server 查询使用完整值及其 hash。共享 builder 现在采用 Server 契约。 +旧的截短索引行仍可解码,但不会自动匹配完整值查询。若部署曾通过 Store 重建索引,应评估受影响索引,必要时从图数据重新构建。 +本次迁移不会自动重建索引,也不会补回缺失的历史索引项。 + +普通 schema 索引保留 Server 的 key/name-TTL 布局。Store 中带正过期时间的 SYSTEM-label 索引保留稳定 key 及旧版 13 字节 value 封装, +core 的 SYSTEM-label writer 保持不变。Label reader 仅识别这个有明确边界的旧封装;这不意味着给 key 增加 TTL 后缀、改变前缀删除行为, +或启用原本不支持的仅凭索引重建元素功能。 + +### HStore OLAP 物理 key + +新 OLAP 行使用 `[property ID][vertex ID]` key,替代仅有 vertex ID 的 key,使同一顶点的多个 OLAP 属性可以共存。 +旧行的 value 仍可读取。Reader 优先读取请求属性的组合 key,仅当旧 vertex-only 行的 value 包含匹配的 property ID 时才回退读取。 +删除某个属性时,移除其组合行和匹配的旧行,保留其他属性。旧 writer 已经覆盖丢失的属性无法通过本次修复恢复。 + +恢复写入前,必须同步升级所有 Server、Store writer。旧 writer 可能更新旧行,而新 reader 仍优先读取已经存在的组合行; +混合版本 OLAP 写入不在支持的升级契约内。 + +### 保持 PD 元数据命名空间一致 + +Store 的 `pd.cluster` 默认值为 `hg`,在 Store `application.yml` 中配置: + +```yaml +pd: + cluster: hg +``` + +对应环境变量为 `PD_CLUSTER`。`usePD=true` 时与 Server 的 `cluster` 一致,`usePD=false` 时与图配置的 `pd.cluster` 一致。 +命名空间用于 schema、图配置、缓存 watch 和 TTL cleaner 元数据。同一个 Store 进程不能在冲突命名空间之间共享 schema driver。 +保留既有 backend `graphspace/store/table` 名称,例如 `DEFAULT/hugegraph/g`;REST 标识 `DEFAULT-hugegraph` 不是元数据 key。 + +## 采用迁移前的验证 + +验证历史 ID、schema、属性和索引样本、分页、TTL、OLAP 行为,以及受影响的 Java/SPI 集成。 +检查 RocksDB、HStore 执行、认证、Store schema 与过滤行为,以及共享元素状态传播。 +核查解析后的依赖与发行包,排除禁止的模块依赖和重复 HugeGraph 类,再启动实际发行包中的服务,不依赖 classpath 顺序规避冲突。 +构建与依赖清单的前置条件见[贡献指南](/cn/docs/contribution-guidelines/contribute/)。 +文档、源码编译或跳过测试的构建不能证明已满足发布条件;实现、网站和下游发布需要协调完成。 diff --git a/content/en/docs/contribution-guidelines/contribute.md b/content/en/docs/contribution-guidelines/contribute.md index 32aa6c5a15..0f09362a41 100644 --- a/content/en/docs/contribution-guidelines/contribute.md +++ b/content/en/docs/contribution-guidelines/contribute.md @@ -53,6 +53,8 @@ Current top-level Maven modules are listed below. Server's `hugegraph-server/hug These names come from the root and subproject `pom.xml` files. Use the structure of the branch you are working on. +For the planned 1.8.0 consolidation, see the [shared-foundation ownership and Java migration guide](/docs/guides/shared-foundation-migration/). + Run the tests directly related to your change first. Common Server test commands include: ```bash @@ -74,9 +76,19 @@ GitHub requires a username and token for Git authentication instead of a usernam When adding a third-party dependency, also update the license information included in the distribution: -1. Add the dependency's license file to `hugegraph-server/hugegraph-dist/release-docs/licenses/`. -2. Update `hugegraph-server/hugegraph-dist/release-docs/LICENSE`. If the dependency includes a NOTICE file, update `NOTICE` as well. -3. Run `hugegraph-server/hugegraph-dist/scripts/dependency/regenerate_known_dependencies.sh` to update the known-dependency list. +1. Add the dependency's license file to `install-dist/release-docs/licenses/`. +2. Update `install-dist/release-docs/LICENSE`. If the dependency includes a NOTICE file, update `NOTICE` as well. +3. From the repository root, prepare the current revision's Server, PD and Store distribution libraries before regenerating the inventory: + + ```bash + mvn install -DskipTests -Dmaven.javadoc.skip=true + bash install-dist/scripts/dependency/regenerate_known_dependencies.sh + ``` + + This build skips tests. Collection combines Maven runtime dependencies with actual flat and nested distribution jars, + including Spring Boot `BOOT-INF/lib` dependencies introduced by repackaging. Missing distributions must fail collection; + POM inspection alone does not establish the complete shipped inventory. Review strict additions/removals in + `install-dist/scripts/dependency/known-dependencies.txt` and their license/NOTICE coverage, including release platform/profile variants. ## Submit a Pull Request diff --git a/content/en/docs/guides/custom-plugin.md b/content/en/docs/guides/custom-plugin.md index b48ff902cb..52605d69ef 100644 --- a/content/en/docs/guides/custom-plugin.md +++ b/content/en/docs/guides/custom-plugin.md @@ -4,6 +4,10 @@ linkTitle: "HugeGraph Plugin" weight: 3 --- +For plugins targeting the planned 1.8.0 shared-foundation API, follow the +[Java/SPI migration guide](/docs/guides/shared-foundation-migration/) and recompile against matching artifacts. +The examples below describe the 1.7.0 API. + ### Background 1. HugeGraph is not only open source and open, but also simple and easy to use. General users can easily add plug-in extension functions without changing the source code. diff --git a/content/en/docs/guides/shared-foundation-migration.md b/content/en/docs/guides/shared-foundation-migration.md new file mode 100644 index 0000000000..fd6d9639b3 --- /dev/null +++ b/content/en/docs/guides/shared-foundation-migration.md @@ -0,0 +1,149 @@ +--- +title: "Shared foundations: 1.8.0 migration" +linkTitle: "1.8.0 Java and upgrade migration" +description: "Prepare Java integrations and coordinated Server, PD and Store upgrades for the 1.8.0 shared-foundation consolidation." +weight: 8 +--- + +This guide describes the shared-foundation consolidation planned for 1.8.0. It does not announce a release or establish release readiness. +The implementation and website documentation must be reviewed and merged together; downstream publication remains pending. +See the [implementation PR](https://github.com/apache/hugegraph/pull/3270) for the source migration document and compatibility fixtures. + +## Module ownership + +`hugegraph-core` remains the graph engine. `hugegraph-struct` owns the shared model and codecs, and `hugegraph-common` owns common utilities. + +```text +Server core ──> struct ──> common +Store core ──> struct +PD service ──> common +``` + +These arrows show the shared-foundation boundary, not the complete dependency tree. The Server HStore adapter still needs PD and Store clients; +Store retains its network, PD client and storage dependencies. PD and Store must not depend on the graph engine, +and struct must not depend on PD clients or core. + +| Capability | Owner | +|---|---| +| IDs, schema metadata, type codes, queries, base elements, property/byte codecs, index construction and analyzers | struct | +| Transactions, traversal, tasks, schema mutation, backend adapters and index update orchestration | core | +| PD-backed schema access, listeners and cache lifecycle (`SchemaGraph`/`SchemaDriver`) | Store | +| JWT signing/verification, shared auth constants and RPC configuration interfaces | common | + +Core and Store implement `HugeGraphSupplier` to supply schema, configuration and clock access without introducing an engine dependency in struct. +Shared base elements own state and adjacency. `HugeVertex`/`HugeEdge` retain engine behavior and wrapper identity over that state; +core serializers delegate shared encoding while retaining backend adapters. Shared index and OLAP selection accept caller-supplied schema candidates +and result sinks; core keeps transaction writes and backend capability checks. + +## Recompile Java integrations and plugins + +Recompile affected applications, plugins and internal integrations against matching 1.8.0 artifacts. +Relocated types change method descriptors as well as imports. Updating source imports does not make previously compiled integrations binary-compatible, +and there is no general compatibility package for removed core classes. +The [plugin example](/docs/guides/custom-plugin/) remains a 1.7.0 example; apply the changes below when targeting the migrated API. + +| Previous entry | Shared entry or required change | +|---|---| +| `org.apache.hugegraph.backend.id.*` | `org.apache.hugegraph.id.*` | +| `org.apache.hugegraph.schema.*` metadata | `org.apache.hugegraph.struct.schema.*` | +| `org.apache.hugegraph.backend.query.*` | `org.apache.hugegraph.query.*` | +| `org.apache.hugegraph.backend.store.Shard` | `org.apache.hugegraph.backend.Shard` | +| `org.apache.hugegraph.backend.store.BackendEntry.BackendColumn` | `org.apache.hugegraph.backend.BackendColumn` | +| `org.apache.hugegraph.structure.HugeIndex` | `org.apache.hugegraph.structure.Index` | +| `HugeGraph.sameAs(HugeGraph)` | `HugeGraph.sameAs(HugeGraphSupplier)` | +| Shared bytes/encoding in backend serializers | `org.apache.hugegraph.serializer.*` | +| Core `HugeException` | `org.apache.hugegraph.exception.HugeException` | +| `org.apache.hugegraph.SchemaGraph`/`SchemaDriver` | `org.apache.hugegraph.store.schema.*` | + +This is an ownership map, not a blanket package replacement. Schema mutation builders and backend-specific serializers remain in core. +Client REST DTOs, including Toolchain's `org.apache.hugegraph.structure.graph.Shard`, remain separate. + +External `HugeGraph` implementations must change their `sameAs` override to accept `org.apache.hugegraph.HugeGraphSupplier`; the old descriptor has no overload. +`GraphSerializer.writeIndex`/`readIndex` now accept/return shared `Index`, so custom serializer implementations and callers must update their signatures. +Custom `HugeElement` subclasses must expose shared `BaseElement` state through `element()` and adapt properties through `wrapProperty`; +do not recreate removed engine state fields or a second adjacency collection. + +`org.apache.hugegraph.rpc.RpcServiceConfig4Client` and `RpcServiceConfig4Server` have one owner in common. +Their fully qualified names and signatures are unchanged, so this move requires no RPC import or configuration change. +`org.apache.hugegraph.auth.TokenGenerator` also resides in common. Callers supply the signing secret and translate JWT failures into local service responses. +JWT dependencies are optional in common; direct JWT consumers must declare their required libraries explicitly. + +Replace `org.apache.hugegraph.backend.id.IdGenerator` with `org.apache.hugegraph.id.IdGenerator` in custom Gremlin `classImports` and scripts. +Check these configuration-only references by starting the packaged Gremlin service as well as compiling Java sources. + +### Downstream exceptions and classpaths + +| Previous downstream entry | Required change | +|---|---| +| Hubble `org.apache.hugegraph.exception.HugeException` | `org.apache.hugegraph.exception.HubbleException` | +| Java client `org.apache.hugegraph.exception.NotSupportException` | `org.apache.hugegraph.exception.ClientNotSupportException` | +| Hubble's copy of `org.apache.hugegraph.license.MachineInfo` | Retain the import and use the common implementation | + +Update imports, constructor calls and catch clauses. `HubbleException` retains its `RuntimeException` parent and string constructor; +`ClientNotSupportException` retains its `ClientException` parent and message/cause and message/arguments constructors. +Computer's `HgkvDirImpl` is an affected client-exception caller. Struct's similarly named exceptions have different contracts and are not substitutes. +Loader's REST boundary and independent client DTOs need no wholesale conversion to struct. + +These Toolchain and Computer adjustments require matching downstream publication, builds and packaged-classpath checks +before migration readiness can be claimed. +A source scan alone cannot establish that shipped jars have no stale or transitive class collisions. + +## Coordinated service upgrade + +Upgrade Server, PD and Store together to matching release artifacts. Mixed-version rolling upgrades are not supported for this migration. +Back up graph data using the existing [backup and restore procedure](/docs/guides/backup-restore/), and preserve deployment configuration before upgrading. +Verify historical data reads and packaged service startup in a validation environment before production use. +The consolidation introduces no automatic data rewrite. + +Existing type codes, ID/property bytes, ordinary Server-created index keys, TTL formats and configuration defaults are preserved. +String IDs now consistently use Java UTF-16 ordering, including IDs loaded from UTF-8 bytes; persisted ID bytes are unchanged. +Historical Java names in query JSON and supported Kryo values still need legacy decoding after relocation. + +Schema map decoding keeps existing wire fields and ID identity. It now preserves primary-key and edge sort-key list order and duplicates, +accepts matching redundant endpoint metadata and legacy endpoint-only maps, and rejects conflicting or malformed endpoints. +Base-element classification also retains the producer context: storage treats `~variables` as task data; +engine elements treat `~server` and `~role_data` as server data and `~variables` as ordinary vertex data. Both retain `~task` and `~taskresult` as task data. + +### Assess legacy index rows + +The previous Store-side `IndexBuilder` shortened long text values to 20 characters, while Server queries used the full value and its hash. +The shared builder now uses the Server contract. Old shortened rows remain decodable but do not automatically match full-value queries. +If an installation used Store-side index rebuilding, assess affected indexes and rebuild them from graph data where necessary. +The migration does not automatically rebuild indexes or recover missing historical entries. + +Ordinary schema indexes retain the Server key/name-TTL layout. Store SYSTEM-label indexes with positive expiry retain stable keys and their legacy +13-byte value envelope; the core SYSTEM-label writer remains unchanged. The label reader recognizes that bounded envelope. +This does not add TTL suffixes to those keys, change prefix deletion, or enable previously unsupported index-only element reconstruction. + +### HStore OLAP physical keys + +New OLAP rows use `[property ID][vertex ID]` keys rather than a vertex ID alone, allowing several OLAP properties on one vertex to coexist. +Existing row values remain readable. Readers first try the requested property's compound key, then use a legacy vertex-only row only if its value contains +the matching property ID. Deleting one property removes its compound row and a matching legacy row while preserving other properties. +Properties already overwritten by the old vertex-only writer cannot be recovered by this repair. + +Upgrade all Server and Store writers together before resuming writes. An old writer can update a legacy row while a new reader still prefers an existing +compound row; mixed-version OLAP writes are outside the supported upgrade contract. + +### Keep the PD metadata namespace consistent + +Store's `pd.cluster` setting defaults to `hg`. In Store `application.yml`, configure: + +```yaml +pd: + cluster: hg +``` + +The environment equivalent is `PD_CLUSTER`. Match Server's `cluster` when `usePD=true`, or the graph's `pd.cluster` when `usePD=false`. +This namespace covers schema, graph configuration, cache watches and TTL-cleaner metadata. +One Store process cannot share a schema driver across conflicting namespaces. +Keep existing backend `graphspace/store/table` names, such as `DEFAULT/hugegraph/g`; the REST identity `DEFAULT-hugegraph` is not a metadata key. + +## Before adopting the migration + +Validate historical ID/schema/property/index samples, pagination, TTL and OLAP behavior, and affected Java/SPI integrations. +Check RocksDB and HStore execution, authentication, Store schema/filter behavior and shared element state propagation. +Inspect resolved dependencies and packaged distributions for forbidden module dependencies and duplicate HugeGraph classes, +then start the actual packaged services without classpath-ordering workarounds. +Use the [contribution guidance](/docs/contribution-guidelines/contribute/) for build and dependency-inventory prerequisites. +Documentation, source compilation and skipped-test builds do not establish release readiness; coordinate implementation, website and downstream publication. diff --git a/data/version_routes.json b/data/version_routes.json index 7f6e062577..a25942fceb 100644 --- a/data/version_routes.json +++ b/data/version_routes.json @@ -477,6 +477,13 @@ "1.3": "cn/docs/guides/security/", "1.0": null }, + "cn:guides/shared-foundation-migration": { + "latest": "cn/docs/guides/shared-foundation-migration/", + "1.7": null, + "1.5": null, + "1.3": null, + "1.0": null + }, "cn:guides/toolchain-local-test": { "latest": "cn/docs/guides/toolchain-local-test/", "1.7": "cn/docs/guides/toolchain-local-test/", @@ -1191,6 +1198,13 @@ "1.3": "docs/guides/security/", "1.0": null }, + "en:guides/shared-foundation-migration": { + "latest": "docs/guides/shared-foundation-migration/", + "1.7": null, + "1.5": null, + "1.3": null, + "1.0": null + }, "en:guides/toolchain-local-test": { "latest": "docs/guides/toolchain-local-test/", "1.7": "docs/guides/toolchain-local-test/", From 611003dc88f91252f1235d3151e9cbe93b3df8a3 Mon Sep 17 00:00:00 2001 From: imbajin Date: Mon, 5 Oct 2026 02:26:02 +0800 Subject: [PATCH 2/4] docs: illustrate the developer migration guide - Lead both languages with affected integrations and Java examples - Explain ownership and upgrade steps with generated illustrations - Keep compatibility limits in readable reference tables --- .../guides/shared-foundation-migration.md | 247 ++++++++++------ .../guides/shared-foundation-migration.md | 263 +++++++++++------- static/images/shared-foundation/migration.png | Bin 0 -> 1291328 bytes static/images/shared-foundation/ownership.png | Bin 0 -> 1281776 bytes 4 files changed, 328 insertions(+), 182 deletions(-) create mode 100644 static/images/shared-foundation/migration.png create mode 100644 static/images/shared-foundation/ownership.png diff --git a/content/cn/docs/guides/shared-foundation-migration.md b/content/cn/docs/guides/shared-foundation-migration.md index 79dc4df780..40b9292911 100644 --- a/content/cn/docs/guides/shared-foundation-migration.md +++ b/content/cn/docs/guides/shared-foundation-migration.md @@ -1,46 +1,80 @@ --- -title: "共享基础模块:1.8.0 迁移指南" +title: "1.8.0 共享基础模块迁移指南" linkTitle: "1.8.0 Java 与升级迁移" -description: "准备 1.8.0 共享基础模块整合所需的 Java 集成调整,以及 Server、PD、Store 同步升级。" +description: "调整 Java import 和插件实现,并准备 1.8.0 共享基础模块迁移所需的 Server、PD、Store 同步升级。" weight: 8 --- -本文说明计划用于 1.8.0 的共享基础模块整合,不代表版本已发布或已满足发布条件。 -实现与网站文档需要配套审查、协调合并;下游发布仍待完成。 +计划中的 1.8.0 迁移会改变 Java import 和扩展接口的签名,Server、PD、Store 也需要一起升级。 +先根据集成方式找出受影响代码,再重新构建应用,并在部署前检查已有数据。 + +## 先确定需要调整的部分 + +| 你的集成方式 | 需要准备什么 | +|---|---| +| Java 代码使用 Server 的 ID、schema 元数据、查询或索引 | 调整 import 和受影响签名,使用配套的 1.8.0 依赖包重新编译。 | +| 自定义序列化器、`HugeGraph` 实现或元素子类 | 按下方说明修改 SPI 契约和共享元素状态的适配。 | +| 自定义 Gremlin import 或脚本 | 替换旧 `IdGenerator` 类名,并验证发行包中的 Gremlin 服务启动。 | +| Hubble、Java client 或 Computer 集成 | 配套依赖包可用后,应用对应的异常类调整。 | +| HStore 部署 | 对齐 PD 命名空间,检查旧的 Store 重建索引,同步升级所有写入端。 | +| Loader 或其他仅通过 REST 调用的集成 | 保留独立 client DTO,按实际使用情况迁移受影响的 Java 调用。 | + +这些调整计划用于 1.8.0,尚未发布。 源迁移说明和兼容性样本见[实现 PR](https://github.com/apache/hugegraph/pull/3270)。 -## 模块职责 +## 共享代码现在由谁维护 -`hugegraph-core` 继续作为图引擎。`hugegraph-struct` 统一维护共享模型和编解码,`hugegraph-common` 维护通用工具。 +此前 Server core 和 struct 分别维护了部分 ID、schema 类型、查询、元素和编解码实现。 +迁移后,这些共享类型统一由一个模块维护,Server 和 Store 使用同一份实现。 +`hugegraph-core` 继续运行图引擎,`hugegraph-struct` 提供共享模型和编解码,`hugegraph-common` 提供通用工具。 -```text -Server core ──> struct ──> common -Store core ──> struct -PD service ──> common -``` +![迁移前 core 和 struct 分别维护共享实现,迁移后 core 与 Store 使用 struct,struct 使用 common,PD 使用 common](/images/shared-foundation/ownership.png) -图中展示共享基础模块边界,不是完整依赖树。Server 的 HStore 适配器仍需要 PD、Store 客户端;Store 保留网络、PD 客户端和存储依赖。 -PD、Store 不应依赖图引擎,struct 不应依赖 PD 客户端或 core。 +*Core 保留图执行和适配器,Store 保留存储和 schema 生命周期,struct、common 统一维护共享实现。 +箭头描述共享职责,不包含完整依赖树。* -| 能力 | 维护模块 | +| 维护模块 | 职责 | |---|---| -| ID、schema 元数据、类型码、查询、基础元素、属性与字节编解码、索引构建和分词器 | struct | -| 事务、遍历、任务、schema 修改、后端适配器和索引更新编排 | core | -| 基于 PD 的 schema 访问、监听器和缓存生命周期(`SchemaGraph`/`SchemaDriver`) | Store | -| JWT 签名与校验、共享认证常量和 RPC 配置接口 | common | +| struct | ID、schema 元数据、类型码、查询、基础元素、属性和字节编解码、索引构建及分词器 | +| core | 事务、遍历、任务、schema 修改、后端适配器及索引更新编排 | +| Store | 通过 `SchemaGraph`/`SchemaDriver` 管理基于 PD 的 schema 访问、监听器和缓存生命周期 | +| common | JWT 签名与校验、共享认证常量和 RPC 配置接口 | + +Server 的 HStore 适配器仍需要 PD、Store 客户端,Store 保留网络、PD 客户端及存储库。 +PD、Store 不应依赖 core,struct 不应依赖 core 或 PD 客户端。 + +Core、Store 实现 `HugeGraphSupplier`,提供 schema、配置和时钟访问能力。 +共享基础元素持有状态和邻接关系,`HugeVertex`、`HugeEdge` 保留引擎行为及基于同一状态的包装对象身份。 +属性修改、克隆、删除、过期和加载状态都应通过这份共享状态传递。 +Core 序列化器保留后端适配逻辑,委托共享代码完成编解码。 +共享索引和 OLAP 选择接收调用方提供的 schema 候选及结果接收器,事务写入和后端能力检查仍由 core 执行。 -Core、Store 实现 `HugeGraphSupplier`,向共享代码提供 schema、配置和时钟访问能力,避免 struct 引入图引擎依赖。 -共享基础元素统一持有状态和邻接关系;`HugeVertex`/`HugeEdge` 保留引擎行为以及基于同一状态的包装对象身份。 -Core 序列化器保留后端适配逻辑并委托共享编解码。共享索引与 OLAP 选择接收调用方提供的 schema 候选和结果接收器; -事务写入及后端能力检查仍由 core 负责。 +## 调整 Java 源码和 SPI 实现 + +先检查代码实际使用的类型。以下 ID 构造调用保留相同的字符串值,只需要迁移 import。 + +**调整前,使用 1.7.0 core API** + +```java +import org.apache.hugegraph.backend.id.Id; +import org.apache.hugegraph.backend.id.IdGenerator; + +Id vertexId = IdGenerator.of("vertex-1"); +``` -## 重新编译 Java 集成和插件 +**调整后,使用共享 API** -使用配套的 1.8.0 artifacts 重新编译受影响的应用、插件和内部集成。类型迁移同时改变 import 和方法描述符, -只调整源码 import 不能让旧的已编译集成具备二进制兼容性。被移除的 core 类没有通用兼容包。 -[插件示例](/cn/docs/guides/custom-plugin/)仍以 1.7.0 为例;接入迁移后的 API 时,需应用以下调整。 +```java +import org.apache.hugegraph.id.Id; +import org.apache.hugegraph.id.IdGenerator; -| 原入口 | 共享入口或必要调整 | +Id vertexId = IdGenerator.of("vertex-1"); +``` + +调整源码后,重新编译应用和插件。迁移后的类型同时改变了方法描述符,仅修改 import 不能让旧的已编译 jar 具备二进制兼容性。 +被移除的 core 类没有通用兼容包。[插件示例](/cn/docs/guides/custom-plugin/)仍描述 1.7.0 API,接入迁移后的 API 时按下表调整。 + +| 原 Java 入口 | 迁移后的入口 | |---|---| | `org.apache.hugegraph.backend.id.*` | `org.apache.hugegraph.id.*` | | `org.apache.hugegraph.schema.*` 元数据 | `org.apache.hugegraph.struct.schema.*` | @@ -48,94 +82,135 @@ Core 序列化器保留后端适配逻辑并委托共享编解码。共享索引 | `org.apache.hugegraph.backend.store.Shard` | `org.apache.hugegraph.backend.Shard` | | `org.apache.hugegraph.backend.store.BackendEntry.BackendColumn` | `org.apache.hugegraph.backend.BackendColumn` | | `org.apache.hugegraph.structure.HugeIndex` | `org.apache.hugegraph.structure.Index` | -| `HugeGraph.sameAs(HugeGraph)` | `HugeGraph.sameAs(HugeGraphSupplier)` | | 后端序列化器中的共享字节与编码逻辑 | `org.apache.hugegraph.serializer.*` | | Core `HugeException` | `org.apache.hugegraph.exception.HugeException` | | `org.apache.hugegraph.SchemaGraph`/`SchemaDriver` | `org.apache.hugegraph.store.schema.*` | -表格描述职责迁移,不能直接批量替换整个包。Schema 修改 builder 和后端专用序列化器仍在 core。 +Schema 修改 builder 和后端专用序列化器仍在 core,需按实际受影响类型应用表中的映射。 客户端 REST DTO 保持独立,包括 Toolchain 的 `org.apache.hugegraph.structure.graph.Shard`。 -外部 `HugeGraph` 实现需要将 `sameAs` override 参数改为 `org.apache.hugegraph.HugeGraphSupplier`,没有保留旧描述符的重载。 -`GraphSerializer.writeIndex`/`readIndex` 改为接收或返回共享 `Index`,自定义序列化器和调用方需同步修改签名。 -自定义 `HugeElement` 子类需通过 `element()` 提供共享 `BaseElement` 状态,通过 `wrapProperty` 适配属性; -不要恢复已移除的引擎状态字段或维护第二套邻接集合。 +| 扩展点 | 实现需要怎样调整 | +|---|---| +| `HugeGraph.sameAs(HugeGraph)` | Override 改为 `sameAs(org.apache.hugegraph.HugeGraphSupplier)`,没有旧签名的重载。 | +| `GraphSerializer.writeIndex`/`readIndex` | 接收或返回共享 `org.apache.hugegraph.structure.Index`,并调整调用方。 | +| 自定义 `HugeElement` 子类 | 通过 `element()` 提供共享 `BaseElement`,通过 `wrapProperty` 适配属性。 | -`org.apache.hugegraph.rpc.RpcServiceConfig4Client` 和 `RpcServiceConfig4Server` 统一由 common 维护。 -全限定类名和签名不变,这项迁移无需调整 RPC import 或配置。 -`org.apache.hugegraph.auth.TokenGenerator` 也位于 common,调用方提供签名密钥,并将 JWT 失败转换为各服务原有的响应。 -Common 中的 JWT 依赖为 optional,直接使用 JWT 的模块需显式声明所需库。 +元素适配器继续使用共享状态,不要恢复已移除的引擎状态字段,也不要新增一套邻接集合。 +Gremlin `classImports` 和脚本需将 `org.apache.hugegraph.backend.id.IdGenerator` 改为 `org.apache.hugegraph.id.IdGenerator`。 +启动发行包中的 Gremlin 服务,验证仅存在于配置中的类名引用;Java 编译不会检查这些引用。 -自定义 Gremlin `classImports` 和脚本需将 `org.apache.hugegraph.backend.id.IdGenerator` 改为 `org.apache.hugegraph.id.IdGenerator`。 -除编译 Java 源码外,还需启动发行包中的 Gremlin 服务,验证这些仅存在于配置中的引用。 +RPC 接口 `org.apache.hugegraph.rpc.RpcServiceConfig4Client` 和 `RpcServiceConfig4Server` 统一由 common 维护。 +类名和签名保持不变,无需因这项迁移调整 RPC import 或配置。 +`org.apache.hugegraph.auth.TokenGenerator` 也由 common 维护,调用方提供签名密钥,并将 JWT 失败转换为各服务的响应。 +Common 的 JWT 依赖为 optional,直接使用 JWT 的模块需声明所需库。 -### 下游异常和 classpath +### 使用配套依赖包时调整下游异常 -| 原下游入口 | 必要调整 | +| 原下游入口 | 替换为 | |---|---| | Hubble `org.apache.hugegraph.exception.HugeException` | `org.apache.hugegraph.exception.HubbleException` | | Java client `org.apache.hugegraph.exception.NotSupportException` | `org.apache.hugegraph.exception.ClientNotSupportException` | -| Hubble 自带的 `org.apache.hugegraph.license.MachineInfo` | 保留 import,使用 common 的实现 | +| Hubble 自带的 `org.apache.hugegraph.license.MachineInfo` | 保留 import,使用 common 的实现。 | -同步修改 import、构造调用和 catch。`HubbleException` 保留 `RuntimeException` 父类和字符串构造器; +与 import 一起修改构造调用和 catch。`HubbleException` 保留 `RuntimeException` 父类和字符串构造器。 `ClientNotSupportException` 保留 `ClientException` 父类及 message/cause、message/arguments 构造器。 -Computer 的 `HgkvDirImpl` 是受影响的 client 异常调用方。Struct 中名称相近的异常具有不同契约,不能替代这些下游异常。 -Loader 的 REST 边界和独立 client DTO 不需要整体改为 struct。 - -这些 Toolchain、Computer 调整仍需配套下游发布、构建和发行包 classpath 检查,才能确认迁移就绪。 -只扫描源码不能证明发行 jar 中没有残留或传递依赖引入的同名类冲突。 +Computer 的 `HgkvDirImpl` 是受影响的 client 异常调用方。Struct 中名称相近的异常具有不同契约。 +Loader 的 REST 边界和独立 client DTO 无需整体改为 struct。 -## 同步升级服务 +Toolchain、Computer 调整仍是未发布的下游交接内容,需要配套发布、构建和发行包类路径检查。 +检查构建后的 jar 以及源码,排除残留或传递依赖引入的同名类副本。 -将 Server、PD、Store 一起升级到配套发行版本,本次迁移不支持混合版本滚动升级。 -升级前按照已有[备份恢复流程](/cn/docs/guides/backup-restore/)备份图数据,并保留部署配置。 -先在验证环境中检查历史数据读取和发行包服务启动,再用于生产。本次整合不提供自动数据重写操作。 +## 准备同步升级服务 -既有类型码、ID 与属性字节、普通 Server 创建的索引 key、TTL 格式和配置默认值保持不变。 -字符串 ID 统一按 Java UTF-16 顺序比较,包括从 UTF-8 字节加载的 ID;持久化 ID 字节不变。 -类型迁移后,查询 JSON 中的历史 Java 类名及受支持的 Kryo 数据仍需要兼容解码。 +将 Server、PD、Store 一起升级到配套发行包,本次迁移不支持混合版本滚动升级。 +以下步骤适用于配套发行包已可用的部署。 -Schema map 解码保留原 wire 字段和 ID 身份,并修复主键、edge sort-key 列表的顺序及重复项丢失问题。 -匹配的冗余端点元数据和旧版仅含端点字段的 map 可以读取;冲突或格式错误的端点元数据会被拒绝。 -基础元素分类也保留生产方上下文,storage 将 `~variables` 视为任务数据;engine 将 `~server`、`~role_data` 视为服务端数据, -将 `~variables` 视为普通顶点数据。两者都将 `~task`、`~taskresult` 视为任务数据。 +![迁移流程包括备份和索引检查、修改 Java 代码、同步升级 Server PD Store、验证服务和数据,OLAP key 改为属性加顶点](/images/shared-foundation/migration.png) -### 检查旧索引行 +*先备份数据和配置、检查受影响索引,再修改 import、SPI 并重新编译,随后同步升级配套服务、对齐 PD 命名空间。 +最后验证发行包服务及图数据读写。OLAP 从每个顶点一行改为每个属性和顶点一行;匹配的旧行仍可读取,覆盖丢失的值不能恢复,不支持混合版本滚动升级。* -旧 Store `IndexBuilder` 将长文本截短为 20 个字符,而 Server 查询使用完整值及其 hash。共享 builder 现在采用 Server 契约。 -旧的截短索引行仍可解码,但不会自动匹配完整值查询。若部署曾通过 Store 重建索引,应评估受影响索引,必要时从图数据重新构建。 -本次迁移不会自动重建索引,也不会补回缺失的历史索引项。 +1. 按已有[备份恢复流程](/cn/docs/guides/backup-restore/)备份图数据,并保留部署配置。 +2. 准备配套的 Server、PD、Store 发行包,以及重新构建的插件和实际需要的下游集成。 +3. 按下方配置对齐元数据命名空间,保留既有底层图名称。 +4. 在验证环境演练,启动发行包中的服务,读取历史数据,执行受影响查询和写入。 +5. 暂停写入后同步升级部署。所有 Server、Store 写入端使用配套发行包且检查通过后,再恢复写入。 -普通 schema 索引保留 Server 的 key/name-TTL 布局。Store 中带正过期时间的 SYSTEM-label 索引保留稳定 key 及旧版 13 字节 value 封装, -core 的 SYSTEM-label writer 保持不变。Label reader 仅识别这个有明确边界的旧封装;这不意味着给 key 增加 TTL 后缀、改变前缀删除行为, -或启用原本不支持的仅凭索引重建元素功能。 +本次整合不会自动重写图数据、重建索引或恢复历史项。演练时需检查下方兼容性场景。 -### HStore OLAP 物理 key +### 对齐 PD 元数据命名空间 -新 OLAP 行使用 `[property ID][vertex ID]` key,替代仅有 vertex ID 的 key,使同一顶点的多个 OLAP 属性可以共存。 -旧行的 value 仍可读取。Reader 优先读取请求属性的组合 key,仅当旧 vertex-only 行的 value 包含匹配的 property ID 时才回退读取。 -删除某个属性时,移除其组合行和匹配的旧行,保留其他属性。旧 writer 已经覆盖丢失的属性无法通过本次修复恢复。 - -恢复写入前,必须同步升级所有 Server、Store writer。旧 writer 可能更新旧行,而新 reader 仍优先读取已经存在的组合行; -混合版本 OLAP 写入不在支持的升级契约内。 - -### 保持 PD 元数据命名空间一致 - -Store 的 `pd.cluster` 默认值为 `hg`,在 Store `application.yml` 中配置: +Store 的 `pd.cluster` 默认值为 `hg`,在 Store `application.yml` 中按以下方式设置。 ```yaml pd: cluster: hg ``` -对应环境变量为 `PD_CLUSTER`。`usePD=true` 时与 Server 的 `cluster` 一致,`usePD=false` 时与图配置的 `pd.cluster` 一致。 -命名空间用于 schema、图配置、缓存 watch 和 TTL cleaner 元数据。同一个 Store 进程不能在冲突命名空间之间共享 schema driver。 -保留既有 backend `graphspace/store/table` 名称,例如 `DEFAULT/hugegraph/g`;REST 标识 `DEFAULT-hugegraph` 不是元数据 key。 +对应环境变量为 `PD_CLUSTER`。 + +| Server 模式 | 必须与 Store `pd.cluster` 一致的配置 | +|---|---| +| `usePD=true` | Server 的 `cluster` | +| `usePD=false` | 图配置的 `pd.cluster` | + +命名空间用于 schema、图配置、缓存监听和 TTL 清理器元数据。 +同一个 Store 进程不能在冲突命名空间之间共享 schema driver。 +保留 backend `graphspace/store/table` 名称,例如 `DEFAULT/hugegraph/g`;REST 标识 `DEFAULT-hugegraph` 不是元数据 key。 + +## 检查数据兼容性 + +| 数据或行为 | 保留或调整的行为 | +|---|---| +| 已存储的 ID 和属性 | 保留既有类型码、ID 与属性字节,以及配置默认值。 | +| 字符串 ID 比较 | 统一采用 Java UTF-16 顺序,包括从 UTF-8 字节加载的 ID;存储字节不变。 | +| 查询 JSON 和受支持的 Kryo 数据 | 类型迁移后仍需兼容解码历史 Java 类名。 | +| Schema map | 保留 wire 字段和 ID 身份;主键及 edge sort-key 列表保留顺序和重复项。 | +| Schema 端点 | 可读取匹配的冗余元数据和旧版仅含端点字段的 map;拒绝冲突或格式错误的端点。 | +| 普通 schema 索引 | Server 创建的 key 保留 key/name-TTL 布局。 | +| 带正过期时间的 Store SYSTEM-label 索引 | 保留稳定 key 和旧版 13 字节 value 封装,label 读取端识别这个有明确边界的封装。 | + +Core SYSTEM-label 写入端保留原有字节。这次迁移不为这些 key 添加 TTL 后缀,也不改变前缀删除行为。 +原本不支持的仅凭索引重建元素功能仍不支持。 + +元素分类通过 `BaseVertex.TypeContext` 保留生产方上下文。 + +| 上下文 | 既有系统顶点分类 | +|---|---| +| `STORAGE` | `~variables` 是任务数据。 | +| `ENGINE` | `~server`、`~role_data` 是服务端数据,`~variables` 是普通顶点数据。 | +| 两者 | `~task`、`~taskresult` 都是任务数据。 | + +引擎 wrapper 选择 `ENGINE`,独立 storage 元素保留 `STORAGE`,使表路由沿用既有行为。 + +### 重建受影响的旧长文本索引 + +旧 Store `IndexBuilder` 将长文本值截短为 20 个字符,Server 查询使用完整值及其 hash。 +共享 builder 现在采用 Server 契约。旧的截短索引行仍可解码,但不会自动匹配完整值查询。 + +若部署曾通过 Store 重建索引,应找出受影响索引,必要时从图数据重新构建。 +本次迁移不自动重建索引,也不能恢复缺失的历史项。普通 Server 创建的索引 key 沿用原有契约。 + +### 验证 HStore OLAP 读取和删除 + +新 OLAP 行使用 `[property ID][vertex ID]` key,让同一顶点的多个 OLAP 属性可以共存。 +旧行只以 vertex ID 作为 key,其 value 无需重写仍可读取。 + +| 操作 | 组合 key 与旧 key 的处理方式 | +|---|---| +| 读取某个属性 | 优先读取其组合 key;旧 vertex-only 行的 value 包含请求的 property ID 时,才回退读取。 | +| 删除某个属性 | 移除其组合行和匹配的旧行,保留其他属性。 | +| 读取旧写入端已经覆盖丢失的属性 | 本次修复无法恢复丢失的值。 | + +恢复写入前,升级全部 Server、Store 写入端。旧写入端可能更新旧行,新读取端仍优先读取已经存在的组合行。 +混合版本 OLAP 写入不在支持的升级契约内。 + +## 验证发行包服务和集成 -## 采用迁移前的验证 +运行受影响的 Java/SPI 测试,再检查 RocksDB、HStore 查询、认证、Store schema 与过滤行为,以及共享元素状态传播。 +覆盖历史 ID、schema、属性和索引样本、分页、TTL、OLAP 场景。 +核查解析后的依赖和发行 jar,排除禁止的模块依赖或重复 HugeGraph 类,并启动实际发行包中的服务,不依赖类路径顺序规避冲突。 +构建和依赖清单的前置条件见[贡献指南](/cn/docs/contribution-guidelines/contribute/)。 -验证历史 ID、schema、属性和索引样本、分页、TTL、OLAP 行为,以及受影响的 Java/SPI 集成。 -检查 RocksDB、HStore 执行、认证、Store schema 与过滤行为,以及共享元素状态传播。 -核查解析后的依赖与发行包,排除禁止的模块依赖和重复 HugeGraph 类,再启动实际发行包中的服务,不依赖 classpath 顺序规避冲突。 -构建与依赖清单的前置条件见[贡献指南](/cn/docs/contribution-guidelines/contribute/)。 -文档、源码编译或跳过测试的构建不能证明已满足发布条件;实现、网站和下游发布需要协调完成。 +实现、网站文档和下游依赖包仍需配套发布。 +根据配套发行包、依赖包和运行结果决定采用迁移的时间;只编译源码或跳过测试的构建不足以确认可用性。 diff --git a/content/en/docs/guides/shared-foundation-migration.md b/content/en/docs/guides/shared-foundation-migration.md index fd6d9639b3..cffe0afa3c 100644 --- a/content/en/docs/guides/shared-foundation-migration.md +++ b/content/en/docs/guides/shared-foundation-migration.md @@ -1,48 +1,81 @@ --- -title: "Shared foundations: 1.8.0 migration" +title: "1.8.0 shared-foundation migration" linkTitle: "1.8.0 Java and upgrade migration" -description: "Prepare Java integrations and coordinated Server, PD and Store upgrades for the 1.8.0 shared-foundation consolidation." +description: "Update Java imports and plugins, then prepare matching Server, PD and Store upgrades for the 1.8.0 shared-foundation changes." weight: 8 --- -This guide describes the shared-foundation consolidation planned for 1.8.0. It does not announce a release or establish release readiness. -The implementation and website documentation must be reviewed and merged together; downstream publication remains pending. -See the [implementation PR](https://github.com/apache/hugegraph/pull/3270) for the source migration document and compatibility fixtures. +The planned 1.8.0 migration changes Java imports and extension signatures, and requires Server, PD and Store to upgrade together. +Use this guide to identify affected code, rebuild your integrations and check existing data before deployment. -## Module ownership +## Find the work that applies to you -`hugegraph-core` remains the graph engine. `hugegraph-struct` owns the shared model and codecs, and `hugegraph-common` owns common utilities. +| Your integration | What to prepare | +|---|---| +| Java code using Server IDs, schema metadata, queries or indexes | Update imports and affected signatures, then recompile against matching 1.8.0 artifacts. | +| Custom serializers, `HugeGraph` implementations or element subclasses | Adapt the SPI contracts and shared element state below. | +| Custom Gremlin imports or scripts | Replace the old `IdGenerator` class name and test packaged Gremlin startup. | +| Hubble, Java client or Computer integration | Apply the downstream exception changes when matching artifacts are available. | +| HStore deployment | Align the PD namespace, inspect legacy rebuilt indexes and upgrade all writers together. | +| Loader or another REST-only caller | Keep the independent client DTOs; migrate any actual affected Java calls. | -```text -Server core ──> struct ──> common -Store core ──> struct -PD service ──> common -``` +These changes are planned for 1.8.0 and have not been released. +The [implementation PR](https://github.com/apache/hugegraph/pull/3270) includes the source migration document and compatibility fixtures. + +## Where shared code now lives + +Server core and struct previously carried separate implementations of several IDs, schema types, queries, elements and codecs. +The migration gives these shared types one owner so Server and Store can use the same implementation. +`hugegraph-core` continues to run the graph engine, `hugegraph-struct` supplies shared models and codecs, and `hugegraph-common` supplies utilities. + +![Core and struct once duplicated shared code; core and Store now use struct, while struct and PD use common](/images/shared-foundation/ownership.png) -These arrows show the shared-foundation boundary, not the complete dependency tree. The Server HStore adapter still needs PD and Store clients; -Store retains its network, PD client and storage dependencies. PD and Store must not depend on the graph engine, -and struct must not depend on PD clients or core. +*Core retains graph execution and adapters, Store retains storage and schema lifecycle, and struct/common own the shared implementations. +The arrows show shared ownership, not the full dependency tree.* -| Capability | Owner | +| Owner | Responsibilities | |---|---| -| IDs, schema metadata, type codes, queries, base elements, property/byte codecs, index construction and analyzers | struct | -| Transactions, traversal, tasks, schema mutation, backend adapters and index update orchestration | core | -| PD-backed schema access, listeners and cache lifecycle (`SchemaGraph`/`SchemaDriver`) | Store | -| JWT signing/verification, shared auth constants and RPC configuration interfaces | common | +| struct | IDs, schema metadata, type codes, queries, base elements, property/byte codecs, index construction and analyzers | +| core | Transactions, traversal, tasks, schema mutation, backend adapters and index update orchestration | +| Store | PD-backed schema access, listeners and cache lifecycle through `SchemaGraph`/`SchemaDriver` | +| common | JWT signing/verification, shared auth constants and RPC configuration interfaces | + +The Server HStore adapter still needs PD and Store clients. Store retains its network, PD client and storage libraries. +PD and Store must not depend on core, and struct must not depend on core or PD clients. + +Core and Store implement `HugeGraphSupplier` to provide schema, configuration and clock access. +Shared base elements hold state and adjacency. `HugeVertex` and `HugeEdge` retain engine behavior and wrapper identity over that same state; +property mutation, cloning, removal, expiration and loading state must propagate through it. +Core serializers retain backend adapters and delegate shared encoding. +Shared index and OLAP selection receive schema candidates and result sinks from their callers; core keeps transaction writes and backend capability checks. + +## Update Java source and SPI implementations + +Start with the types your code imports. This ID construction call keeps the same string value while its imports move. + +**Before, using the 1.7.0 core API** -Core and Store implement `HugeGraphSupplier` to supply schema, configuration and clock access without introducing an engine dependency in struct. -Shared base elements own state and adjacency. `HugeVertex`/`HugeEdge` retain engine behavior and wrapper identity over that state; -core serializers delegate shared encoding while retaining backend adapters. Shared index and OLAP selection accept caller-supplied schema candidates -and result sinks; core keeps transaction writes and backend capability checks. +```java +import org.apache.hugegraph.backend.id.Id; +import org.apache.hugegraph.backend.id.IdGenerator; -## Recompile Java integrations and plugins +Id vertexId = IdGenerator.of("vertex-1"); +``` + +**After, using the shared API** + +```java +import org.apache.hugegraph.id.Id; +import org.apache.hugegraph.id.IdGenerator; + +Id vertexId = IdGenerator.of("vertex-1"); +``` -Recompile affected applications, plugins and internal integrations against matching 1.8.0 artifacts. -Relocated types change method descriptors as well as imports. Updating source imports does not make previously compiled integrations binary-compatible, -and there is no general compatibility package for removed core classes. -The [plugin example](/docs/guides/custom-plugin/) remains a 1.7.0 example; apply the changes below when targeting the migrated API. +Recompile the application and its plugins after adapting source. Relocated types also change method descriptors, +so editing imports cannot make an old compiled jar binary-compatible. Removed core classes have no general compatibility package. +The [plugin examples](/docs/guides/custom-plugin/) still describe 1.7.0; apply the following mappings when targeting the migrated API. -| Previous entry | Shared entry or required change | +| Previous Java entry | Use in the migrated API | |---|---| | `org.apache.hugegraph.backend.id.*` | `org.apache.hugegraph.id.*` | | `org.apache.hugegraph.schema.*` metadata | `org.apache.hugegraph.struct.schema.*` | @@ -50,100 +83,138 @@ The [plugin example](/docs/guides/custom-plugin/) remains a 1.7.0 example; apply | `org.apache.hugegraph.backend.store.Shard` | `org.apache.hugegraph.backend.Shard` | | `org.apache.hugegraph.backend.store.BackendEntry.BackendColumn` | `org.apache.hugegraph.backend.BackendColumn` | | `org.apache.hugegraph.structure.HugeIndex` | `org.apache.hugegraph.structure.Index` | -| `HugeGraph.sameAs(HugeGraph)` | `HugeGraph.sameAs(HugeGraphSupplier)` | -| Shared bytes/encoding in backend serializers | `org.apache.hugegraph.serializer.*` | +| Shared byte/encoding code in backend serializers | `org.apache.hugegraph.serializer.*` | | Core `HugeException` | `org.apache.hugegraph.exception.HugeException` | | `org.apache.hugegraph.SchemaGraph`/`SchemaDriver` | `org.apache.hugegraph.store.schema.*` | -This is an ownership map, not a blanket package replacement. Schema mutation builders and backend-specific serializers remain in core. -Client REST DTOs, including Toolchain's `org.apache.hugegraph.structure.graph.Shard`, remain separate. +Schema mutation builders and backend-specific serializers remain in core, so apply these mappings only to affected types. +Client REST DTOs remain independent, including Toolchain's `org.apache.hugegraph.structure.graph.Shard`. -External `HugeGraph` implementations must change their `sameAs` override to accept `org.apache.hugegraph.HugeGraphSupplier`; the old descriptor has no overload. -`GraphSerializer.writeIndex`/`readIndex` now accept/return shared `Index`, so custom serializer implementations and callers must update their signatures. -Custom `HugeElement` subclasses must expose shared `BaseElement` state through `element()` and adapt properties through `wrapProperty`; -do not recreate removed engine state fields or a second adjacency collection. +| Extension point | Change in your implementation | +|---|---| +| `HugeGraph.sameAs(HugeGraph)` | Override `sameAs(org.apache.hugegraph.HugeGraphSupplier)`. There is no old-signature overload. | +| `GraphSerializer.writeIndex`/`readIndex` | Accept/return shared `org.apache.hugegraph.structure.Index`; update callers too. | +| Custom `HugeElement` subclass | Expose shared `BaseElement` through `element()` and adapt properties through `wrapProperty`. | -`org.apache.hugegraph.rpc.RpcServiceConfig4Client` and `RpcServiceConfig4Server` have one owner in common. -Their fully qualified names and signatures are unchanged, so this move requires no RPC import or configuration change. -`org.apache.hugegraph.auth.TokenGenerator` also resides in common. Callers supply the signing secret and translate JWT failures into local service responses. -JWT dependencies are optional in common; direct JWT consumers must declare their required libraries explicitly. +Keep element adapters on the shared state; do not restore removed engine state fields or add a second adjacency collection. +For Gremlin `classImports` and scripts, replace `org.apache.hugegraph.backend.id.IdGenerator` with `org.apache.hugegraph.id.IdGenerator`. +Configuration-only class names need a packaged Gremlin startup check; Java compilation does not exercise them. -Replace `org.apache.hugegraph.backend.id.IdGenerator` with `org.apache.hugegraph.id.IdGenerator` in custom Gremlin `classImports` and scripts. -Check these configuration-only references by starting the packaged Gremlin service as well as compiling Java sources. +The RPC interfaces `org.apache.hugegraph.rpc.RpcServiceConfig4Client` and `RpcServiceConfig4Server` now have one owner in common. +Their names and signatures stay the same, so this move requires no RPC import or configuration change. +`org.apache.hugegraph.auth.TokenGenerator` also belongs to common. Callers supply the signing secret and map JWT failures to their service responses. +Common's JWT dependencies are optional; direct JWT consumers must declare their required libraries. -### Downstream exceptions and classpaths +### Adapt downstream exceptions when matching artifacts are available -| Previous downstream entry | Required change | +| Previous downstream entry | Replacement | |---|---| | Hubble `org.apache.hugegraph.exception.HugeException` | `org.apache.hugegraph.exception.HubbleException` | | Java client `org.apache.hugegraph.exception.NotSupportException` | `org.apache.hugegraph.exception.ClientNotSupportException` | -| Hubble's copy of `org.apache.hugegraph.license.MachineInfo` | Retain the import and use the common implementation | - -Update imports, constructor calls and catch clauses. `HubbleException` retains its `RuntimeException` parent and string constructor; -`ClientNotSupportException` retains its `ClientException` parent and message/cause and message/arguments constructors. -Computer's `HgkvDirImpl` is an affected client-exception caller. Struct's similarly named exceptions have different contracts and are not substitutes. -Loader's REST boundary and independent client DTOs need no wholesale conversion to struct. - -These Toolchain and Computer adjustments require matching downstream publication, builds and packaged-classpath checks -before migration readiness can be claimed. -A source scan alone cannot establish that shipped jars have no stale or transitive class collisions. - -## Coordinated service upgrade - -Upgrade Server, PD and Store together to matching release artifacts. Mixed-version rolling upgrades are not supported for this migration. -Back up graph data using the existing [backup and restore procedure](/docs/guides/backup-restore/), and preserve deployment configuration before upgrading. -Verify historical data reads and packaged service startup in a validation environment before production use. -The consolidation introduces no automatic data rewrite. +| Hubble's copy of `org.apache.hugegraph.license.MachineInfo` | Keep the import and use the common implementation. | -Existing type codes, ID/property bytes, ordinary Server-created index keys, TTL formats and configuration defaults are preserved. -String IDs now consistently use Java UTF-16 ordering, including IDs loaded from UTF-8 bytes; persisted ID bytes are unchanged. -Historical Java names in query JSON and supported Kryo values still need legacy decoding after relocation. +Update constructors and catch clauses along with imports. `HubbleException` keeps its `RuntimeException` parent and string constructor. +`ClientNotSupportException` keeps its `ClientException` parent and message/cause and message/arguments constructors. +Computer's `HgkvDirImpl` is one affected client-exception caller. Struct's similarly named exceptions have different contracts. +Loader's REST boundary and independent client DTOs do not need an overall conversion to struct. -Schema map decoding keeps existing wire fields and ID identity. It now preserves primary-key and edge sort-key list order and duplicates, -accepts matching redundant endpoint metadata and legacy endpoint-only maps, and rejects conflicting or malformed endpoints. -Base-element classification also retains the producer context: storage treats `~variables` as task data; -engine elements treat `~server` and `~role_data` as server data and `~variables` as ordinary vertex data. Both retain `~task` and `~taskresult` as task data. +The Toolchain and Computer changes are still unpublished downstream handoffs. They need matching publication, builds and packaged-classpath checks. +Inspect the built jars as well as the source tree for stale or transitive copies of the same class. -### Assess legacy index rows +## Prepare a coordinated service upgrade -The previous Store-side `IndexBuilder` shortened long text values to 20 characters, while Server queries used the full value and its hash. -The shared builder now uses the Server contract. Old shortened rows remain decodable but do not automatically match full-value queries. -If an installation used Store-side index rebuilding, assess affected indexes and rebuild them from graph data where necessary. -The migration does not automatically rebuild indexes or recover missing historical entries. +Upgrade Server, PD and Store together to matching release artifacts. This migration does not support mixed-version rolling upgrades. +The sequence below applies once those matching artifacts are available. -Ordinary schema indexes retain the Server key/name-TTL layout. Store SYSTEM-label indexes with positive expiry retain stable keys and their legacy -13-byte value envelope; the core SYSTEM-label writer remains unchanged. The label reader recognizes that bounded envelope. -This does not add TTL suffixes to those keys, change prefix deletion, or enable previously unsupported index-only element reconstruction. +![Back up data, adapt Java code, upgrade matching services and verify; OLAP rows use property and vertex keys](/images/shared-foundation/migration.png) -### HStore OLAP physical keys +*Back up data/configuration and check affected indexes, update imports/SPI and recompile, then upgrade matching services and align the PD namespace. +Verify packaged services and graph reads/writes. OLAP keys change from one row per vertex to one row per property and vertex; +matching legacy rows remain readable, overwritten values cannot be recovered, and mixed-version rolling upgrades are unsupported.* -New OLAP rows use `[property ID][vertex ID]` keys rather than a vertex ID alone, allowing several OLAP properties on one vertex to coexist. -Existing row values remain readable. Readers first try the requested property's compound key, then use a legacy vertex-only row only if its value contains -the matching property ID. Deleting one property removes its compound row and a matching legacy row while preserving other properties. -Properties already overwritten by the old vertex-only writer cannot be recovered by this repair. +1. Back up graph data with the existing [backup and restore procedure](/docs/guides/backup-restore/) and preserve deployment configuration. +2. Prepare matching Server, PD and Store artifacts, plus rebuilt plugins and any required downstream integrations. +3. Align the metadata namespace using the settings below; preserve existing backend graph names. +4. Rehearse in a validation environment. Start the packaged services, read historical data and exercise affected queries and writes. +5. Pause writes for the coordinated deployment upgrade. Resume only after all Server and Store writers use matching artifacts and checks pass. -Upgrade all Server and Store writers together before resuming writes. An old writer can update a legacy row while a new reader still prefers an existing -compound row; mixed-version OLAP writes are outside the supported upgrade contract. +The consolidation does not automatically rewrite graph data, rebuild indexes or recover historical entries. +Review the compatibility cases below during the rehearsal. -### Keep the PD metadata namespace consistent +### Match the PD metadata namespace -Store's `pd.cluster` setting defaults to `hg`. In Store `application.yml`, configure: +Store's `pd.cluster` defaults to `hg`. Set it in Store `application.yml` as follows. ```yaml pd: cluster: hg ``` -The environment equivalent is `PD_CLUSTER`. Match Server's `cluster` when `usePD=true`, or the graph's `pd.cluster` when `usePD=false`. +The equivalent environment variable is `PD_CLUSTER`. + +| Server mode | Value that must match Store's `pd.cluster` | +|---|---| +| `usePD=true` | Server's `cluster` | +| `usePD=false` | The graph's `pd.cluster` | + This namespace covers schema, graph configuration, cache watches and TTL-cleaner metadata. One Store process cannot share a schema driver across conflicting namespaces. -Keep existing backend `graphspace/store/table` names, such as `DEFAULT/hugegraph/g`; the REST identity `DEFAULT-hugegraph` is not a metadata key. +Preserve backend `graphspace/store/table` names such as `DEFAULT/hugegraph/g`; the REST identity `DEFAULT-hugegraph` is not a metadata key. + +## Check data compatibility + +| Data or behavior | What the migration preserves or changes | +|---|---| +| Stored IDs and properties | Existing type codes and ID/property bytes are preserved, along with configuration defaults. | +| String ID comparison | Java UTF-16 order is used consistently, including IDs loaded from UTF-8 bytes. Stored ID bytes stay the same. | +| Query JSON and supported Kryo values | Historical Java names still need legacy decoding after relocation. | +| Schema maps | Wire fields and ID identity stay the same. Primary-key and edge sort-key lists keep order and duplicates. | +| Schema endpoints | Matching redundant metadata and legacy endpoint-only maps remain readable; conflicting or malformed endpoints are rejected. | +| Ordinary schema indexes | Server-created keys keep their key/name-TTL layout. | +| Store SYSTEM-label indexes with positive expiry | Stable keys and the old 13-byte value envelope remain; the label reader recognizes that bounded envelope. | + +The core SYSTEM-label writer keeps its existing bytes. This migration adds no TTL suffix to those keys and does not change prefix deletion. +Previously unsupported index-only element reconstruction remains unsupported. + +Element classification also keeps the producer context through `BaseVertex.TypeContext`. + +| Context | Existing system-vertex classification | +|---|---| +| `STORAGE` | `~variables` is task data. | +| `ENGINE` | `~server` and `~role_data` are server data; `~variables` is ordinary vertex data. | +| Both | `~task` and `~taskresult` are task data. | + +Engine wrappers select `ENGINE`; standalone storage elements retain `STORAGE` so table routing follows the existing behavior. + +### Rebuild affected legacy long-text indexes + +The previous Store-side `IndexBuilder` shortened long text values to 20 characters. Server queries used the full value and its hash. +The shared builder now follows the Server contract. An old shortened row can still be decoded, but it does not automatically match a full-value query. + +If your deployment used Store-side index rebuilding, identify affected indexes and rebuild them from graph data where necessary. +The migration performs no automatic rebuild and cannot recover missing historical entries. Ordinary Server-created index keys retain their existing contract. + +### Verify HStore OLAP reads and deletes + +New OLAP rows use `[property ID][vertex ID]` keys, allowing several OLAP properties on one vertex to coexist. +Legacy rows used only the vertex ID; their values remain readable without a rewrite. + +| Operation | Behavior with compound and legacy keys | +|---|---| +| Read a property | Prefer its compound key. Fall back to a vertex-only row only when its value contains the requested property ID. | +| Delete a property | Remove its compound row and a matching legacy row; preserve other properties. | +| Read a property already overwritten by the old writer | The lost value cannot be recovered by this repair. | + +Upgrade all Server and Store writers before resuming writes. An old writer can update a legacy row while a new reader prefers an existing compound row. +Mixed-version OLAP writes are outside the supported upgrade contract. + +## Verify packaged services and integrations -## Before adopting the migration +Run affected Java/SPI tests, then exercise RocksDB and HStore queries, authentication, Store schema/filter behavior and shared element state propagation. +Include historical ID, schema, property and index samples, pagination, TTL and OLAP cases. +Inspect resolved dependencies and shipped jars for forbidden module dependencies or duplicate HugeGraph classes, +and start the actual packaged services without classpath-ordering workarounds. +The [contribution guide](/docs/contribution-guidelines/contribute/) describes build and dependency-inventory prerequisites. -Validate historical ID/schema/property/index samples, pagination, TTL and OLAP behavior, and affected Java/SPI integrations. -Check RocksDB and HStore execution, authentication, Store schema/filter behavior and shared element state propagation. -Inspect resolved dependencies and packaged distributions for forbidden module dependencies and duplicate HugeGraph classes, -then start the actual packaged services without classpath-ordering workarounds. -Use the [contribution guidance](/docs/contribution-guidelines/contribute/) for build and dependency-inventory prerequisites. -Documentation, source compilation and skipped-test builds do not establish release readiness; coordinate implementation, website and downstream publication. +The implementation, website documentation and downstream artifacts need coordinated publication. +Use matching artifacts and their runtime results to decide when to adopt the migration; compiling source or skipping tests is insufficient. diff --git a/static/images/shared-foundation/migration.png b/static/images/shared-foundation/migration.png new file mode 100644 index 0000000000000000000000000000000000000000..7de3a61d0bfa09170ce9ae9ec4a65cab8178c76b GIT binary patch literal 1291328 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