This is an automated email from the ASF dual-hosted git repository. kenhuuu pushed a commit to branch tx-diag in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit bd598c2c041fa5442de3666f7076dba9fc6812c1 Author: Ken Hu <[email protected]> AuthorDate: Fri Jan 23 11:08:50 2026 -0800 Some diagrams for potential tx design --- docs/src/dev/transactions/http-api-spec.md | 145 +++++++++++++++++++++ .../dev/transactions/remote-transaction-flow.md | 100 ++++++++++++++ pom.xml | 12 +- 3 files changed, 246 insertions(+), 11 deletions(-) diff --git a/docs/src/dev/transactions/http-api-spec.md b/docs/src/dev/transactions/http-api-spec.md new file mode 100644 index 0000000000..31bcffcf83 --- /dev/null +++ b/docs/src/dev/transactions/http-api-spec.md @@ -0,0 +1,145 @@ +# HTTP Transaction API Specification + +## Overview + +The Gremlin Server exposes a REST API for transaction management. Transactions are modeled as resources with unique IDs, and all operations within a transaction include the transaction ID in the URL path. + +**NOTE** This is very preliminary and can easily be modeled by a single endpoint. + +## API Endpoints + +### Non-Transactional Traversal +``` +POST /gremlin +``` +Standard Gremlin Server endpoint for executing traversals without a transaction. + +### Begin Transaction +``` +POST /gremlin/tx + +Response: 201 Created +Location: /gremlin/tx/{txId} +{ + "txId": "550e8400-e29b-41d4-a716-446655440000" +} +``` +Creates a new transaction and returns a unique transaction ID. + +### Execute Traversal Within Transaction +``` +POST /gremlin/tx/{txId} + +Response: 200 OK +(Standard Gremlin response) +``` +Executes a traversal within the context of the specified transaction. Request body is the same as `POST /gremlin`, but the traversal is scoped to the transaction. + +### Get Transaction Status +``` +GET /gremlin/tx/{txId} + +Response: 200 OK +{ + "txId": "550e8400-e29b-41d4-a716-446655440000", + "status": "ACTIVE" +} +``` +Returns the current status of a transaction (ACTIVE, COMMITTED, or ROLLED_BACK). + +### Commit Transaction +``` +POST /gremlin/tx/{txId}/commit + +Response: 200 OK +{ + "txId": "550e8400-e29b-41d4-a716-446655440000", + "status": "COMMITTED" +} +``` +Commits all changes made within the transaction. + +### Rollback Transaction +``` +POST /gremlin/tx/{txId}/rollback + +Response: 200 OK +{ + "txId": "550e8400-e29b-41d4-a716-446655440000", + "status": "ROLLED_BACK" +} +``` +Rolls back all changes made within the transaction. + +### Abort Transaction +``` +DELETE /gremlin/tx/{txId} + +Response: 200 OK +{ + "txId": "550e8400-e29b-41d4-a716-446655440000", + "status": "ROLLED_BACK" +} +``` +Alternative to rollback - aborts the transaction and rolls back all changes. + +## Error Responses + +### Transaction Not Found +``` +GET /gremlin/tx/invalid-id + +Response: 404 Not Found +{ + "error": "Transaction not found", + "txId": "invalid-id" +} +``` + +### Transaction Already in Terminal State +``` +POST /gremlin/tx/{txId}/commit + +Response: 409 Conflict +{ + "error": "Transaction already in terminal state", + "txId": "550e8400-e29b-41d4-a716-446655440000", + "status": "COMMITTED" +} +``` + +### Transaction Execution Error +``` +POST /gremlin/tx/{txId} + +Response: 500 Internal Server Error +{ + "error": "Traversal execution failed", + "message": "...", + "txId": "550e8400-e29b-41d4-a716-446655440000" +} +``` + +## Transaction ID Format + +- **Format**: UUID +- **Example**: `550e8400-e29b-41d4-a716-446655440000` +- **Generation**: Server-generated on `POST /gremlin/tx` +- **Usage**: Included in all subsequent requests as URL path parameter +- **Purpose**: Routes requests to correct transaction context on server + +## Endpoint Summary Diagram + +```mermaid +flowchart TD + A[Client] -->|POST /gremlin| B[Non-Transactional Execution] + A -->|POST /gremlin/tx| C[Begin Transaction] + C -->|Returns txId| D[Transaction Active] + D -->|POST /gremlin/tx/{txId}| E[Execute in Transaction] + D -->|GET /gremlin/tx/{txId}| F[Get Status] + D -->|POST /gremlin/tx/{txId}/commit| G[Commit] + D -->|POST /gremlin/tx/{txId}/rollback| H[Rollback] + D -->|DELETE /gremlin/tx/{txId}| H + G --> I[Transaction Committed] + H --> J[Transaction Rolled Back] +``` diff --git a/docs/src/dev/transactions/remote-transaction-flow.md b/docs/src/dev/transactions/remote-transaction-flow.md new file mode 100644 index 0000000000..a72a1dee49 --- /dev/null +++ b/docs/src/dev/transactions/remote-transaction-flow.md @@ -0,0 +1,100 @@ +# Remote Transaction Flow + +## Overview + +This diagram shows the complete client-server interaction for remote transactions over HTTP. Transactions are thread-bound: when `tx.begin()` is called, all subsequent traversals on that thread automatically participate in the transaction. + +Making these remote transactions thread-bound on the GLV-side makes it match the behavior for embedded transactions which are threaded by default. The API will likely force transactions to disallow multithreaded transactions which matches traditional relation database behavior. However, a subsequent update will likely allow multiple of these thread-bound transactions to exist, that is, a single thread will be able to have one or more transactions. + +## Sequence Diagram + +```mermaid +sequenceDiagram + participant Client as Client (Thread 1) + participant GTS as GraphTraversalSource + participant Conn as DriverRemoteConnection + participant Server as Gremlin Server + + Note over Client,Server: Setup + Client->>Conn: traversal().with(connection) + Conn-->>Client: GraphTraversalSource (g) + + Note over Client,Server: Begin Transaction + Client->>GTS: g.tx() + GTS-->>Client: Transaction (tx) + Client->>Conn: tx.begin() + Conn->>Server: POST /gremlin/tx + Server-->>Conn: 201 Created<br/>{txId: "abc-123"} + Note over Conn: Store txId in ThreadLocal + Conn-->>Client: void + + Note over Client,Server: Execute Traversals (Thread-Bound) + Client->>GTS: g.addV("person") + GTS->>Conn: submit(traversal) + Note over Conn: Check ThreadLocal: txId = "abc-123" + Conn->>Server: POST /gremlin/tx/abc-123 + Server-->>Conn: 200 OK + Conn-->>GTS: Result + GTS-->>Client: Traversal + + Client->>GTS: g.V().has("name", "x") + GTS->>Conn: submit(traversal) + Note over Conn: Check ThreadLocal: txId = "abc-123" + Conn->>Server: POST /gremlin/tx/abc-123 + Server-->>Conn: 200 OK + Conn-->>GTS: Result + GTS-->>Client: Traversal + + Note over Client,Server: Commit Transaction + Client->>Conn: tx.commit() + Conn->>Server: POST /gremlin/tx/abc-123/commit + Server-->>Conn: 200 OK<br/>{status: "COMMITTED"} + Note over Conn: Clear ThreadLocal + Conn-->>Client: void +``` + +## Rollback Flow + +```mermaid +sequenceDiagram + participant Client + participant Conn as DriverRemoteConnection + participant Server as Gremlin Server + + Note over Client,Server: Transaction is Active + Client->>Conn: tx.rollback() + Conn->>Server: POST /gremlin/tx/abc-123/rollback + Server-->>Conn: 200 OK<br/>{status: "ROLLED_BACK"} + Note over Conn: Clear ThreadLocal + Conn-->>Client: void +``` + +## Thread-Bound Behavior + +### Key Points + +1. **ThreadLocal Storage**: When `tx.begin()` is called, the transaction ID is stored in a ThreadLocal variable +2. **Automatic Binding**: All traversals submitted on that thread automatically check ThreadLocal for an active transaction +3. **Endpoint Selection**: + - If transaction ID found in ThreadLocal → use `POST /gremlin/tx/{txId}` + - If no transaction ID → use `POST /gremlin` (non-transactional) +4. **Thread Cleanup**: `tx.commit()` or `tx.rollback()` clears the ThreadLocal for that thread +5. **Thread Isolation**: Different threads can have different active transactions + +### Behavior Diagram + +```mermaid +flowchart TD + A[Client calls tx.begin] --> B[POST /gremlin/tx] + B --> C[Server returns txId] + C --> D[Store txId in ThreadLocal] + D --> E[Client executes g.addV...] + E --> F{Check ThreadLocal} + F -->|txId found| G[POST /gremlin/tx/txId] + F -->|No txId| H[POST /gremlin] + G --> I[Execute in transaction] + H --> J[Execute non-transactional] + I --> K[Client calls tx.commit] + K --> L[POST /gremlin/tx/txId/commit] + L --> M[Clear ThreadLocal] +``` diff --git a/pom.xml b/pom.xml index 8ea5fcd9ca..4ca108bb17 100644 --- a/pom.xml +++ b/pom.xml @@ -127,23 +127,13 @@ limitations under the License. <module>gremlin-core</module> <module>gremlin-annotations</module> <module>gremlin-test</module> - <module>gremlin-util</module> <module>gremlin-groovy</module> <module>tinkergraph-gremlin</module> - <module>gremlin-javascript</module> - <module>gremlin-python</module> - <module>gremlin-dotnet</module> - <module>gremlin-go</module> - <module>hadoop-gremlin</module> - <module>spark-gremlin</module> - <module>neo4j-gremlin</module> - <module>sparql-gremlin</module> <module>gremlin-driver</module> <module>gremlin-console</module> <module>gremlin-server</module> <module>gremlin-tools</module> - <module>gremlint</module> - <module>gremlin-mcp</module> + <module>gremlin-util</module> </modules> <scm> <connection>scm:git:[email protected]:apache/tinkerpop.git</connection>
