| # Development Guide |
| |
| Comprehensive guide for developing, testing, and contributing to HugeGraph Store. |
| |
| ## Table of Contents |
| |
| - [Development Environment Setup](#development-environment-setup) |
| - [Module Architecture](#module-architecture) |
| - [Build and Test](#build-and-test) |
| - [gRPC Development](#grpc-development) |
| - [Debugging](#debugging) |
| - [Contribution Guidelines](#contribution-guidelines) |
| |
| --- |
| |
| ## Development Environment Setup |
| |
| ### Prerequisites |
| |
| **Required**: |
| - Java: 11 or higher (OpenJDK or Oracle JDK) |
| - Maven: 3.5 or higher |
| - Git: Latest version |
| - IDE: IntelliJ IDEA (recommended) or Eclipse |
| |
| **Optional** (for testing): |
| - Docker: For containerized testing |
| - grpcurl: For gRPC API testing |
| - Prometheus/Grafana: For metrics testing |
| |
| ### Clone Repository |
| |
| ```bash |
| # Clone HugeGraph repository |
| git clone https://github.com/apache/hugegraph.git |
| cd hugegraph |
| |
| # Checkout development branch |
| git checkout 1.7-rebase |
| ``` |
| |
| ### IDE Setup (IntelliJ IDEA) |
| |
| **Import Project**: |
| 1. File → Open → Select `hugegraph` directory |
| 2. IntelliJ detects Maven project → Click "Import" |
| 3. Wait for Maven to download dependencies |
| |
| **Code Style**: |
| ```bash |
| # Configure IDE code style |
| # Ensure EditorConfig support is enabled |
| # Code style is defined in .editorconfig at repository root |
| ``` |
| |
| **Run Configuration**: |
| 1. Run → Edit Configurations |
| 2. Add new "Application" configuration: |
| - Main class: `org.apache.hugegraph.store.node.StoreNodeApplication` |
| - VM options: `-Xms4g -Xmx4g -Dconfig.file=conf/application.yml` |
| - Working directory: `hugegraph-store/apache-hugegraph-store-1.7.0` |
| - Use classpath of module: `hg-store-node` |
| |
| ### Build from Source |
| |
| **Build entire project**: |
| ```bash |
| # From hugegraph root |
| mvn clean install -DskipTests |
| ``` |
| |
| **Build Store module only**: |
| ```bash |
| # Build hugegraph-struct first (required dependency) |
| mvn install -pl hugegraph-struct -am -DskipTests |
| |
| # Build Store |
| cd hugegraph-store |
| mvn clean install -DskipTests |
| ``` |
| |
| **Build with tests**: |
| ```bash |
| mvn clean install |
| ``` |
| |
| --- |
| |
| ## Module Architecture |
| |
| ### Module Dependency Graph |
| |
| ``` |
| hugegraph-struct (external dependency) |
| ↓ |
| hg-store-common |
| ↓ |
| ├─→ hg-store-grpc (proto definitions) |
| ├─→ hg-store-rocksdb |
| ↓ |
| hg-store-core |
| ↓ |
| ├─→ hg-store-client |
| ├─→ hg-store-node |
| ↓ |
| ├─→ hg-store-cli |
| ├─→ hg-store-dist |
| └─→ hg-store-test |
| ``` |
| |
| ### Module Details |
| |
| #### hg-store-common |
| |
| **Location**: `hugegraph-store/hg-store-common` |
| |
| **Purpose**: Shared utilities and query abstractions |
| |
| **Key Packages**: |
| - `buffer`: ByteBuffer utilities |
| - `constant`: Constants and enums |
| - `query`: Query abstraction classes |
| - `Condition`: Filter conditions |
| - `Aggregate`: Aggregation types |
| - `QueryCondition`: Query parameters |
| - `term`: Term matching utilities |
| - `util`: General utilities |
| |
| **Adding New Utility**: |
| 1. Create class in appropriate package (e.g., `util`) |
| 2. Add Javadoc comments |
| 3. Add unit tests in `hg-store-test` |
| |
| #### hg-store-grpc |
| |
| **Location**: `hugegraph-store/hg-store-grpc` |
| |
| **Purpose**: gRPC protocol definitions |
| |
| **Structure**: |
| ``` |
| hg-store-grpc/ |
| ├── src/main/proto/ # Protocol definitions |
| │ ├── store_session.proto |
| │ ├── query.proto |
| │ ├── graphpb.proto |
| │ ├── store_state.proto |
| │ ├── store_stream_meta.proto |
| │ ├── healthy.proto |
| │ └── store_common.proto |
| └── target/generated-sources/ # Generated Java code (git-ignored) |
| ``` |
| |
| **Generated Code**: Excluded from source control and Apache RAT checks |
| |
| #### hg-store-core |
| |
| **Location**: `hugegraph-store/hg-store-core` |
| |
| **Purpose**: Core storage engine logic |
| |
| **Key Classes**: |
| |
| **`HgStoreEngine.java`** (~500 lines): |
| - Singleton per Store node |
| - Manages all `PartitionEngine` instances |
| - Coordinates with PD |
| - Entry point for partition lifecycle |
| |
| **`PartitionEngine.java`** (~300 lines): |
| - One instance per partition replica |
| - Wraps Raft node |
| - Delegates to `BusinessHandler` |
| |
| **`HgStoreStateMachine.java`** (~400 lines): |
| - Implements JRaft's `StateMachine` |
| - Applies Raft log entries |
| - Handles snapshot save/load |
| |
| **`BusinessHandler.java`** (interface) / `BusinessHandlerImpl.java`** (~800 lines): |
| - Implements data operations (put, get, delete, scan) |
| - Processes queries with filters and aggregations |
| |
| **Key Packages**: |
| - `business/`: Business logic layer |
| - `meta/`: Metadata management |
| - `raft/`: Raft integration |
| - `pd/`: PD client and integration |
| - `cmd/`: Command processing |
| - `snapshot/`: Snapshot management |
| |
| #### hg-store-client |
| |
| **Location**: `hugegraph-store/hg-store-client` |
| |
| **Purpose**: Java client library |
| |
| **Key Classes**: |
| - `HgStoreClient`: Main client interface |
| - `HgStoreSession`: Session-based operations |
| - `HgStoreNodeManager`: Connection management |
| - `HgStoreQuery`: Query builder |
| |
| **Usage**: See [Integration Guide](integration-guide.md) |
| |
| #### hg-store-node |
| |
| **Location**: `hugegraph-store/hg-store-node` |
| |
| **Purpose**: Store node server |
| |
| **Key Classes**: |
| - `StoreNodeApplication`: Spring Boot main class |
| - `HgStoreSessionService`: gRPC service implementation |
| - `HgStoreQueryService`: Query service implementation |
| |
| **Start Server**: |
| ```bash |
| cd hugegraph-store/apache-hugegraph-store-1.7.0 |
| bin/start-hugegraph-store.sh |
| ``` |
| |
| If you want to run store module in debug mode |
| Directly run HgStoreNodeService in your IDE (ensure PD is on). |
| --- |
| |
| ## Build and Test |
| |
| ### Build Commands |
| |
| **Clean build**: |
| ```bash |
| mvn clean install -DskipTests |
| ``` |
| |
| **Compile only**: |
| ```bash |
| mvn compile |
| ``` |
| |
| **Package distribution**: |
| ```bash |
| mvn clean package -DskipTests |
| |
| # Output: hugegraph-store/apache-hugegraph-store-<version>.tar.gz |
| ``` |
| |
| **Regenerate gRPC stubs** (after modifying `.proto` files): |
| ```bash |
| cd hugegraph-store/hg-store-grpc |
| mvn clean compile |
| |
| # Generated files: target/generated-sources/protobuf/ |
| ``` |
| |
| ### Testing |
| |
| #### Test Profiles |
| |
| Store tests use Maven profiles (all active by default): |
| |
| ```xml |
| <profile> |
| <id>store-client-test</id> |
| <activation><activeByDefault>true</activeByDefault></activation> |
| </profile> |
| <profile> |
| <id>store-core-test</id> |
| <activation><activeByDefault>true</activeByDefault></activation> |
| </profile> |
| <profile> |
| <id>store-common-test</id> |
| <activation><activeByDefault>true</activeByDefault></activation> |
| </profile> |
| <profile> |
| <id>store-rocksdb-test</id> |
| <activation><activeByDefault>true</activeByDefault></activation> |
| </profile> |
| <profile> |
| <id>store-server-test</id> |
| <activation><activeByDefault>true</activeByDefault></activation> |
| </profile> |
| <profile> |
| <id>store-raftcore-test</id> |
| <activation><activeByDefault>true</activeByDefault></activation> |
| </profile> |
| ``` |
| |
| #### Run Tests |
| |
| **All tests**: |
| ```bash |
| cd hugegraph-store |
| mvn test |
| ``` |
| |
| **Specific profile**: |
| ```bash |
| mvn test -P store-core-test |
| ``` |
| |
| **Specific test class**: |
| ```bash |
| mvn test -Dtest=HgStoreEngineTest |
| ``` |
| |
| **Specific test method**: |
| ```bash |
| mvn test -Dtest=HgStoreEngineTest#testPartitionCreation |
| ``` |
| |
| **From IntelliJ**: |
| - Right-click test class → Run 'TestClassName' |
| - Right-click test method → Run 'testMethodName' |
| |
| #### Test Structure |
| |
| **Location**: `hugegraph-store/hg-store-test/src/main/java` (non-standard location) |
| |
| **Packages**: |
| - `client/`: Client library tests |
| - `common/`: Common utilities tests |
| - `core/`: Core storage tests |
| - `raft/`: Raft tests |
| - `snapshot/`: Snapshot tests |
| - `store/`: Storage engine tests |
| - `meta/`: Metadata tests |
| - `raftcore/`: Raft core tests |
| - `rocksdb/`: RocksDB tests |
| - `service/`: Service tests |
| |
| **Base Test Class**: `BaseTest.java` |
| - Provides common test utilities |
| - Sets up test environment |
| |
| #### Writing Tests |
| |
| **Example Test Class**: |
| ```java |
| package org.apache.hugegraph.store.core; |
| |
| import org.apache.hugegraph.store.BaseTest; |
| import org.junit.Test; |
| |
| import static org.junit.Assert.*; |
| |
| public class HgStoreEngineTest extends BaseTest { |
| |
| @Test |
| public void testEngineCreation() { |
| // Arrange |
| HgStoreEngineConfig config = HgStoreEngineConfig.builder() |
| .dataPath("./test-data") |
| .build(); |
| |
| // Act |
| HgStoreEngine engine = HgStoreEngine.getInstance(); |
| engine.init(config); |
| |
| // Assert |
| assertNotNull(engine); |
| assertTrue(engine.isInitialized()); |
| |
| // Cleanup |
| engine.shutdown(); |
| } |
| } |
| ``` |
| |
| **Integration Test Example**: |
| ```java |
| @Test |
| public void testRaftConsensus() throws Exception { |
| // Setup 3-node Raft group |
| List<PartitionEngine> engines = new ArrayList<>(); |
| for (int i = 0; i < 3; i++) { |
| PartitionEngine engine = createPartitionEngine(i); |
| engines.add(engine); |
| engine.start(); |
| } |
| |
| // Wait for leader election |
| Thread.sleep(2000); |
| |
| // Perform write on leader |
| PartitionEngine leader = findLeader(engines); |
| leader.put("key1".getBytes(), "value1".getBytes()); |
| |
| // Wait for replication |
| Thread.sleep(1000); |
| |
| // Verify on all nodes |
| for (PartitionEngine engine : engines) { |
| byte[] value = engine.get("key1".getBytes()); |
| assertEquals("value1", new String(value)); |
| } |
| |
| // Cleanup |
| for (PartitionEngine engine : engines) { |
| engine.stop(); |
| } |
| } |
| ``` |
| |
| ### Code Coverage |
| |
| **Generate Coverage Report**: |
| ```bash |
| mvn clean test jacoco:report |
| |
| # Report: hg-store-test/target/site/jacoco/index.html |
| ``` |
| |
| **View in Browser**: |
| ```bash |
| open hg-store-test/target/site/jacoco/index.html |
| ``` |
| |
| --- |
| |
| ## gRPC Development |
| |
| ### Adding a New gRPC Service |
| |
| #### Step 1: Define Protocol |
| |
| Create or edit `.proto` file in `hg-store-grpc/src/main/proto/`: |
| |
| **Example**: `my_service.proto` |
| ```protobuf |
| syntax = "proto3"; |
| |
| package org.apache.hugegraph.store.grpc; |
| |
| import "store_common.proto"; |
| |
| service MyService { |
| rpc MyOperation(MyRequest) returns (MyResponse); |
| } |
| |
| message MyRequest { |
| Header header = 1; |
| string key = 2; |
| } |
| |
| message MyResponse { |
| bytes value = 1; |
| } |
| ``` |
| |
| #### Step 2: Generate Java Stubs |
| |
| ```bash |
| cd hg-store-grpc |
| mvn clean compile |
| |
| # Generated classes: |
| # - MyServiceGrpc.java (service stub) |
| # - MyRequest.java |
| # - MyResponse.java |
| ``` |
| |
| #### Step 3: Implement Service |
| |
| Create service implementation in `hg-store-node/src/main/java/.../service/`: |
| |
| ```java |
| package org.apache.hugegraph.store.node.service; |
| |
| import io.grpc.stub.StreamObserver; |
| import org.apache.hugegraph.store.grpc.MyServiceGrpc; |
| import org.apache.hugegraph.store.grpc.MyRequest; |
| import org.apache.hugegraph.store.grpc.MyResponse; |
| |
| public class MyServiceImpl extends MyServiceGrpc.MyServiceImplBase { |
| |
| @Override |
| public void myOperation(MyRequest request, StreamObserver<MyResponse> responseObserver) { |
| try { |
| // Extract request parameters |
| String key = request.getKey(); |
| |
| // Perform operation (delegate to HgStoreEngine) |
| byte[] value = performOperation(key); |
| |
| // Build response |
| MyResponse response = MyResponse.newBuilder() |
| .setValue(ByteString.copyFrom(value)) |
| .build(); |
| |
| // Send response |
| responseObserver.onNext(response); |
| responseObserver.onCompleted(); |
| } catch (Exception e) { |
| responseObserver.onError(e); |
| } |
| } |
| |
| private byte[] performOperation(String key) { |
| // Implementation |
| return new byte[0]; |
| } |
| } |
| ``` |
| |
| #### Step 4: Register Service |
| |
| In `StoreNodeApplication.java`: |
| ```java |
| @Bean |
| public Server grpcServer() { |
| return ServerBuilder.forPort(grpcPort) |
| .addService(new HgStoreSessionService()) |
| .addService(new HgStoreQueryService()) |
| .addService(new MyServiceImpl()) // Add new service |
| .build(); |
| } |
| ``` |
| |
| #### Step 5: Test Service |
| |
| **Using grpcurl**: |
| ```bash |
| # List services |
| grpcurl -plaintext localhost:8500 list |
| |
| # Call method |
| grpcurl -plaintext -d '{"key": "test"}' localhost:8500 org.apache.hugegraph.store.grpc.MyService/MyOperation |
| ``` |
| |
| **Unit Test**: |
| ```java |
| @Test |
| public void testMyService() { |
| // Setup gRPC channel |
| ManagedChannel channel = ManagedChannelBuilder |
| .forAddress("localhost", 8500) |
| .usePlaintext() |
| .build(); |
| |
| // Create stub |
| MyServiceGrpc.MyServiceBlockingStub stub = MyServiceGrpc.newBlockingStub(channel); |
| |
| // Build request |
| MyRequest request = MyRequest.newBuilder() |
| .setKey("test") |
| .build(); |
| |
| // Call service |
| MyResponse response = stub.myOperation(request); |
| |
| // Verify |
| assertNotNull(response.getValue()); |
| |
| // Cleanup |
| channel.shutdown(); |
| } |
| ``` |
| |
| --- |
| |
| ## Debugging |
| |
| ### Local Debugging |
| |
| **Debug Store Node in IntelliJ**: |
| 1. Set breakpoints in source code |
| 2. Run → Debug 'StoreNodeApplication' |
| 3. Debugger pauses at breakpoints |
| |
| **Debug with Remote Store**: |
| 1. Start Store with debug port: |
| ```bash |
| # Edit start-hugegraph-store.sh |
| JAVA_OPTS="$JAVA_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005" |
| bin/start-hugegraph-store.sh |
| ``` |
| |
| 2. Attach debugger in IntelliJ: |
| - Run → Edit Configurations → Add "Remote JVM Debug" |
| - Host: localhost, Port: 5005 |
| - Run → Debug 'Remote Store' |
| |
| ### Logging |
| |
| **Log Configuration**: `hg-store-dist/src/assembly/static/conf/log4j2.xml` |
| |
| **Enable Debug Logging**: |
| ```xml |
| <!-- Store core --> |
| <Logger name="org.apache.hugegraph.store" level="DEBUG"/> |
| |
| <!-- Raft --> |
| <Logger name="com.alipay.sofa.jraft" level="DEBUG"/> |
| |
| <!-- RocksDB --> |
| <Logger name="org.rocksdb" level="DEBUG"/> |
| |
| <!-- gRPC --> |
| <Logger name="io.grpc" level="DEBUG"/> |
| ``` |
| |
| **Restart to apply**: |
| ```bash |
| bin/restart-hugegraph-store.sh |
| ``` |
| |
| **View Logs**: |
| ```bash |
| tail -f logs/hugegraph-store.log |
| tail -f logs/hugegraph-store.log | grep ERROR |
| ``` |
| |
| ### Debugging Raft |
| |
| **Check Raft State**: |
| ```bash |
| # Raft logs location |
| ls -lh storage/raft/partition-*/log/ |
| |
| # Raft snapshots |
| ls -lh storage/raft/partition-*/snapshot/ |
| ``` |
| |
| **Raft Metrics** (in code): |
| ```java |
| // Get Raft node status |
| RaftNode node = partitionEngine.getRaftNode(); |
| NodeStatus status = node.getNodeStatus(); |
| System.out.println("Term: " + status.getTerm()); |
| System.out.println("State: " + status.getState()); // Leader, Follower, Candidate |
| System.out.println("Peers: " + status.getPeers()); |
| ``` |
| |
| **Enable Raft Logging**: |
| ```xml |
| <Logger name="com.alipay.sofa.jraft" level="DEBUG"/> |
| ``` |
| |
| ### Debugging RocksDB |
| |
| **RocksDB Statistics**: |
| ```java |
| // In code |
| RocksDB db = rocksDBSession.getDb(); |
| String stats = db.getProperty("rocksdb.stats"); |
| System.out.println(stats); |
| ``` |
| |
| **Dump RocksDB Data** (for inspection): |
| ```bash |
| # Using ldb tool (included with RocksDB) |
| ldb --db=storage/rocksdb scan --max_keys=100 |
| ``` |
| |
| ### Performance Profiling |
| |
| **JVM Profiling** (using async-profiler): |
| ```bash |
| # Download async-profiler |
| wget https://github.com/jvm-profiling-tools/async-profiler/releases/download/v2.9/async-profiler-2.9-linux-x64.tar.gz |
| tar -xzf async-profiler-2.9-linux-x64.tar.gz |
| |
| # Start profiling |
| ./profiler.sh -d 60 -f flamegraph.html $(pgrep -f hugegraph-store) |
| |
| # View flamegraph |
| open flamegraph.html |
| ``` |
| |
| **Memory Profiling**: |
| ```bash |
| # Heap dump |
| jmap -dump:format=b,file=heap.bin $(pgrep -f hugegraph-store) |
| |
| # Analyze with VisualVM or Eclipse MAT |
| ``` |
| |
| --- |
| |
| ## Contribution Guidelines |
| |
| ### Code Style |
| |
| **Java**: |
| - Follow Apache HugeGraph code style (configured via `.editorconfig`) |
| - Use 4 spaces for indentation (no tabs) |
| - Max line length: 120 characters |
| - Braces on same line (K&R style) |
| |
| **Example**: |
| ```java |
| public class MyClass { |
| private static final Logger LOG = LoggerFactory.getLogger(MyClass.class); |
| |
| public void myMethod(String param) { |
| if (param == null) { |
| throw new IllegalArgumentException("param cannot be null"); |
| } |
| // Implementation |
| } |
| } |
| ``` |
| |
| ### Commit Messages |
| |
| **Format**: |
| ``` |
| <type>(<scope>): <subject> |
| |
| <body> |
| |
| <footer> |
| ``` |
| |
| **Types**: |
| - `feat`: New feature |
| - `fix`: Bug fix |
| - `docs`: Documentation changes |
| - `refactor`: Code refactoring |
| - `test`: Test additions or changes |
| - `chore`: Build or tooling changes |
| |
| **Example**: |
| ``` |
| feat(store): add query aggregation pushdown |
| |
| Implement COUNT, SUM, MIN, MAX, AVG aggregations at Store level |
| to reduce network traffic and improve query performance. |
| |
| Closes #1234 |
| ``` |
| |
| ### Pull Request Process |
| |
| 1. **Fork and Clone**: |
| ```bash |
| # Fork on GitHub |
| git clone https://github.com/YOUR_USERNAME/hugegraph.git |
| cd hugegraph |
| git remote add upstream https://github.com/apache/hugegraph.git |
| ``` |
| |
| 2. **Create Branch**: |
| ```bash |
| git checkout -b feature-my-feature |
| ``` |
| |
| 3. **Develop and Test**: |
| ```bash |
| # Make changes |
| # Add tests |
| mvn clean install # Ensure all tests pass |
| ``` |
| |
| 4. **Check Code Quality**: |
| ```bash |
| # License header check |
| mvn apache-rat:check |
| |
| # Code style check |
| mvn editorconfig:check |
| ``` |
| |
| 5. **Commit**: |
| ```bash |
| git add . |
| git commit -m "feat(store): add new feature" |
| ``` |
| |
| 6. **Push and Create PR**: |
| ```bash |
| git push origin feature-my-feature |
| # Create PR on GitHub |
| ``` |
| |
| 7. **Code Review**: |
| - Address review comments |
| - Update PR with fixes |
| - Request re-review |
| |
| 8. **Merge**: |
| - Maintainers merge after approval |
| |
| ### License and Dependencies |
| |
| **Adding Dependencies**: |
| |
| When adding third-party dependencies: |
| 1. Add to `pom.xml` |
| 2. Add license file to `install-dist/release-docs/licenses/` |
| 3. Update `install-dist/release-docs/LICENSE` |
| 4. If upstream has NOTICE, update `install-dist/release-docs/NOTICE` |
| 5. Update `install-dist/scripts/dependency/known-dependencies.txt` |
| |
| **Run Dependency Check**: |
| ```bash |
| cd install-dist/scripts/dependency |
| ./regenerate_known_dependencies.sh |
| ``` |
| |
| ### Documentation |
| |
| **When to Update Docs**: |
| - New feature: Add usage examples |
| - API changes: Update API reference |
| - Configuration changes: Update configuration guide |
| - Bug fixes: Update troubleshooting section |
| |
| **Documentation Location**: |
| - Main README: `hugegraph-store/README.md` |
| - Detailed docs: `hugegraph-store/docs/` |
| |
| --- |
| |
| ## Additional Resources |
| |
| **Official Documentation**: |
| - HugeGraph Docs: https://hugegraph.apache.org/docs/ |
| - Apache TinkerPop: https://tinkerpop.apache.org/docs/ |
| |
| **Community**: |
| - Mailing List: dev@hugegraph.apache.org |
| - GitHub Issues: https://github.com/apache/hugegraph/issues |
| - Slack: (link in project README) |
| |
| **Related Projects**: |
| - SOFA-JRaft: https://github.com/sofastack/sofa-jraft |
| - RocksDB: https://rocksdb.org/ |
| - gRPC: https://grpc.io/docs/languages/java/ |
| |
| --- |
| |
| For operational procedures, see [Operations Guide](operations-guide.md). |
| |
| For production best practices, see [Best Practices](best-practices.md). |