Skip to content

symfonic.memory.backends.postgres_native_node_delete

postgres_native_node_delete

Native node cascades validate locked owners.

Existing incident edges and the node commit together. Native edge writers hold both endpoints FOR SHARE (postgres_native_edge_write.lock_native_endpoints), so an edge cannot attach to a node this cascade has locked FOR UPDATE. Public admission is graph_native_admission (CORE-194); this module admits nothing. A native cascade also deletes the row's claim generations from a ledger that keeps other rows' history, so it runs as index probes and requires the operator-installed claim record index, as batched discard does.

delete_claimed async

delete_claimed(pool: Any, scope: StorageScope, node_id: str, token: str, edge_upsert_mode: str) -> None

Cascading pending deletion commits only its locked token version.

Source code in src/symfonic/memory/backends/postgres_native_node_delete.py
async def delete_claimed(
    pool: Any, scope: StorageScope, node_id: str, token: str,
    edge_upsert_mode: str,
) -> None:
    """Cascading pending deletion commits only its locked token version."""
    prefix_sql, prefix_params = sql_prefix_condition(scope, "scope_path", 3)
    conn = await pool.acquire()
    try:
        async with conn.transaction(), index_probes(conn):
            await require_claim_record_index(conn)
            await _native_cascade(
                conn, scope, node_id, prefix_sql, prefix_params,
                edge_upsert_mode, claim_token=token,
            )
    finally:
        await pool.release(conn)

delete_node async

delete_node(pool: Any, scope: StorageScope, node_id: str, cascade: bool, edge_upsert_mode: str) -> None

Keep v0/non-cascade SQL; native cascades lock and validate stored keys.

Source code in src/symfonic/memory/backends/postgres_native_node_delete.py
async def delete_node(pool: Any, scope: StorageScope, node_id: str,
                      cascade: bool, edge_upsert_mode: str) -> None:
    """Keep v0/non-cascade SQL; native cascades lock and validate stored keys."""
    prefix_sql, prefix_params = sql_prefix_condition(scope, "scope_path", 3)
    conn = await pool.acquire()
    try:
        if cascade and _is_native_scope(scope):
            async with conn.transaction(), index_probes(conn):
                await _native_cascade(conn, scope, node_id, prefix_sql, prefix_params,
                                      edge_upsert_mode)
            return
        if cascade:
            # An opt-in Working guard may refuse the node deletion. Keep the
            # preceding edge removal in its rollback domain.
            async with conn.transaction():
                await require_direct_edge_index(conn, edge_upsert_mode)
                await conn.execute(
                    "DELETE FROM memory_edges "
                    'WHERE tenant_id=$1 AND tenant_id COLLATE "C"=$1 AND '
                    '((source=$2 AND source COLLATE "C"=$2) OR '
                    '(target=$2 AND target COLLATE "C"=$2)) '
                    f"AND {prefix_sql}",
                    scope.tenant_id, node_id, *prefix_params,
                )
                await _delete_v0_node(conn, scope, node_id, prefix_sql, prefix_params)
        elif _is_native_scope(scope):
            # A non-cascade native delete shares the v0 SQL; CORE-199 fences it.
            async with conn.transaction():
                await fence_scope(conn, scope)
                await _delete_v0_node(conn, scope, node_id, prefix_sql, prefix_params)
        else:
            await _delete_v0_node(conn, scope, node_id, prefix_sql, prefix_params)
    finally:
        await pool.release(conn)