You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs: Clarify transaction/commit semantics with non-autocommit clients
Adds a "Using AGE with Non-Autocommit Clients" section explaining
PostgreSQL transaction visibility rules as they apply to AGE DDL-like
functions (create_graph, create_vlabel, etc.), with broken/fixed
psycopg v3 examples and a JDBC note.
Refs #2195
Copy file name to clipboardExpand all lines: README.md
+98Lines changed: 98 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -215,7 +215,105 @@ LOAD 'age';
215
215
SET search_path = ag_catalog, "$user", public;
216
216
```
217
217
218
+
<h2><imgheight="20"src="/img/contents.svg"> Using AGE with Non-Autocommit Clients (psycopg, JDBC, etc.)</h2>
218
219
220
+
If you are using AGE from a database client that does **not** default to autocommit — most commonly `psycopg` v3 or JDBC — you must understand how PostgreSQL's transaction semantics apply to AGE's setup and DDL-like functions. Otherwise, you may see graphs or labels that appear to be created successfully, but are not visible from new connections.
221
+
222
+
This is **not** a bug in AGE — it is standard PostgreSQL behavior. AGE's DDL-like functions write to the catalog, and catalog writes only become visible to other sessions after the enclosing transaction is committed.
223
+
224
+
### What is and isn't transactional
225
+
226
+
| Statement | Scope | Needs commit to be visible elsewhere? |
227
+
|---|---|---|
228
+
|`LOAD 'age'`| Session-local (loads the .so into the current backend) | No |
229
+
|`SET search_path = ag_catalog, "$user", public`| Session-local | No |
230
+
|`SELECT create_graph('g')`|**Writes** to `ag_graph` and creates a schema |**Yes**|
231
+
|`SELECT create_vlabel('g', 'L')` / `create_elabel(...)`|**Writes** to `ag_label` and creates a table |**Yes**|
232
+
|`SELECT drop_graph('g', true)` / `drop_label(...)`|**Writes** to catalog |**Yes**|
233
+
|`SELECT load_labels_from_file(...)` / `load_edges_from_file(...)`|**Writes** to catalog + data |**Yes**|
234
+
|`cypher('g', $$ CREATE (:L {...}) $$)`|**Writes** data |**Yes**|
235
+
236
+
In a client that defaults to autocommit (e.g. `psql`), every statement commits automatically, so this is never noticed. In a non-autocommit client, the first statement you run implicitly opens a transaction that stays open until you call `commit()`, `rollback()`, or close the connection.
237
+
238
+
### psycopg v3 — the "savepoint gotcha"
239
+
240
+
The common pitfall is that `with connection.transaction():` in psycopg does **not** start a new top-level transaction when one is already open — it creates a **savepoint** inside the existing outer transaction. Releasing a savepoint is not a commit, so your `create_graph` write stays invisible to other sessions until the outer transaction is explicitly committed.
241
+
242
+
#### ❌ Broken: graph is not visible from a new connection
conn.execute("SELECT * FROM create_graph('my_graph')") # commits immediately
291
+
conn.close()
292
+
```
293
+
294
+
You can also toggle autocommit at runtime with `conn.set_autocommit(True)`.
295
+
296
+
### JDBC
297
+
298
+
JDBC connections also default to autocommit **true** per the JDBC spec, but many frameworks (Spring, etc.) flip it off. If you are running AGE DDL-like calls from JDBC, either:
299
+
300
+
```java
301
+
connection.setAutoCommit(true);
302
+
// ... LOAD 'age'; SET search_path ...; SELECT create_graph(...);
303
+
```
304
+
305
+
or keep autocommit off and explicitly commit after DDL-like calls:
connection.commit(); // make the graph visible to other sessions
312
+
```
313
+
314
+
### Rule of thumb
315
+
316
+
> If an AGE call creates, drops, or modifies a graph, label, vertex, edge, or property, it is a **transactional write**. In a non-autocommit client, it will not be visible to other sessions until you explicitly `commit()`.
0 commit comments