Skip to content

Conversation

@ahmad-sanad
Copy link

@ahmad-sanad ahmad-sanad commented Sep 26, 2025

Note: tests fail for me without #925

Issue

When calling snapshot then using the snapshot id with clone --follow --snapshot the code fails with BUG: call to pgsql_commit() without holding an open multi statement connection

This happens because:

  1. clone_and_follow calls copydb_fetch_schema_and_prepare_specs which calls copydb_prepare_snapshot, since specs->sourceSnapshot.state is initially SNAPSHOT_STATE_UNKNOWN. copydb_fetch_schema_and_prepare_specs later calls copydb_close_snapshot which commits the transaction and sets the snapshot state to SNAPSHOT_STATE_CLOSED
  2. clone_and_follow later calls start_clone_process which calls cloneDB which calls copydb_fetch_schema_and_prepare_specs again. This time specs->sourceSnapshot.state is SNAPSHOT_STATE_CLOSED so copydb_prepare_snapshot is not called. The code later tries to set the search path, which fails with a warning, then tries to commit the transaction, which fails with this error since no transaction was opened.

Fix:

copydb_prepare_snapshot checks if the source snapshot state is SNAPSHOT_STATE_UNKNOWN or SNAPSHOT_STATE_CLOSED in order to prepare the snapshshot and open a transaction.
copydb_fetch_schema_and_prepare_specs on the other hand, only calls copydb_prepare_snapshot if the snapshot state is SNAPSHOT_STATE_UNKNOWN.
This code changes copydb_fetch_schema_and_prepare_specs to match copydb_prepare_snapshot and call it in either state.

Full code invocation notes:

clone --snapshot --follow:

cli_clone_follow.cli_clone
  cli_clone_follow.clone_and_follow
    copydb_schema.copydb_fetch_schema_and_prepare_specs
      snapshot.copydb_prepare_snapshot (because specs->sourceSnapshot.state == SNAPSHOT_STATE_UNKNOWN)
        snapshot.copydb_set_snapshot
          pgsql.pgsql_init (setup db connection)
          pgsql.pgsql_begin (open transaction (BEGIN) and keep it open (PGSQL_CONNECTION_MULTI_STATEMENT))
          pgsql.pgsql_set_transaction (set transaction isolation level)
          pgsql.pgsql_set_snapshot (SET TRANSACTION SNAPSHOT)
      copydb_schema.copydb_fetch_source_schema
        pgsql.pgsql_prepend_search_path
          pgsql.pgsql_set_search_path
            successfully sets search path since the transaction is still open
      snapshot.copydb_close_snapshot (copySpecs->sourceSnapshot.state = SNAPSHOT_STATE_CLOSED)
        end transaction (COMMIT) and close DB connection and set the state to SNAPSHOT_STATE_CLOSED
        copySpecs->sourceSnapshot.state = SNAPSHOT_STATE_CLOSED;
    cli_clone_follow.start_clone_process
      cli_clone_follow.cloneDB
        copydb_schema.copydb_fetch_schema_and_prepare_specs (because specs->sourceSnapshot.state == SNAPSHOT_STATE_CLOSED so we do not call copydb_prepare_snapshot)
          copydb_schema.copydb_fetch_source_schema
            pgsql.pgsql_prepend_search_path
              pgsql.pgsql_set_search_path
                pgsql.pgsql_execute
                  pgsql.pgsql_execute_with_params
                    fails to sets search path since there is no open transaction
                    WARNING:  SET LOCAL can only be used in transaction blocks
          pgsql.pgsql_commit (specs->sourceSnapshot.state == SNAPSHOT_STATE_UNKNOWN is false since SNAPSHOT_STATE_CLOSED is set. preparedSnapshot is false)
            There is no open transaction since we closed it earlier and did not call copydb_prepare_snapshot again since the state was not SNAPSHOT_STATE_UNKNOWN
            BUG: call to pgsql_commit() without holding an open multi statement connection

clone --snapshot without follow, works without any issues because:

cli_clone_follow.cli_clone
  snapshot.copydb_should_export_snapshot (false since we have --snapshot so we do not call copydb_prepare_snapshot)
  cli_clone_follow.start_clone_process
    cli_clone_follow.cloneDB (same as above. We never ran copydb_prepare_snapshot. This means the state will be SNAPSHOT_STATE_UNKNOWN so we will run it later and properly have a transaction open)

When calling snapshot then using the snapshot id with `clone --follow
--snapshot` the code fails with "BUG: call to pgsql_commit() without holding an open multi statement connection"

`clone_and_follow` calls `copydb_fetch_schema_and_prepare_specs` which calls
`copydb_prepare_snapshot` since `specs->sourceSnapshot.state` is initially `SNAPSHOT_STATE_UNKNOWN`. `copydb_fetch_schema_and_prepare_specs` later calls `copydb_close_snapshot` which commits the transaction and sets the snapshot state to `SNAPSHOT_STATE_CLOSED`

`clone_and_follow` later calls `start_clone_process` which calls `cloneDB` which calls `copydb_fetch_schema_and_prepare_specs` again. This time `specs->sourceSnapshot.state` is `SNAPSHOT_STATE_CLOSED` so `copydb_prepare_snapshot` is not called. The code later tries to set the search path, which fails with a warning, then tries to commit the transaction, which fails with this error since no transaction was opened.

`copydb_prepare_snapshot` checks if the source snapshot state is `SNAPSHOT_STATE_UNKNOWN` or `SNAPSHOT_STATE_CLOSED` in order to prepare the snapshshot and open a transaction.
`copydb_fetch_schema_and_prepare_specs` on the other hand, only calls `copydb_prepare_snapshot` if the snapshot state is `SNAPSHOT_STATE_UNKNOWN`.
This code changes `copydb_fetch_schema_and_prepare_specs` to match `copydb_prepare_snapshot` and call it in either state.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant