VoIP.info Troubleshooting

Asterisk ODBC write commits, rolls back or duplicates data

Trace one database mutation from func_odbc input and affected rows through active transaction, commit or rollback, channel teardown and any later retry.

Reviewed 2026-10-15Current Asterisk database-transaction guidance, latest generated ODBC transaction, commit and rollback references, upstream func_odbc and res_odbc samples, and res_odbc configuration guidance reviewed October 15, 2026; installed Asterisk branch, module packaging, driver, DSN, DBMS, schema, statements, account permissions, isolation support, transaction lifetime, concurrency, idempotency, retry policy and caller treatment remain deployment specific; no host, module, driver, DSN, credential, database, schema, statement, row, transaction, channel or live call was inspected or testedAdvancedDocumentation based · not lab tested

Preserve one mutation timeline without replaying it

Capture timestamp and timezone, installed Asterisk branch, channel and linked identifiers, dialplan location, function class, redacted argument and value shapes, write handle, transaction ID, active transaction at each step, statement status, affected-row count, insert-fallback decision, explicit commit or rollback result, hangup or transfer point and final caller treatment. Correlate a database transaction or audit identifier when the owner can provide one.

Classify the symptom as no statement, rejected statement, zero affected rows, unexpected insertsql, wrong transaction, missing commit, explicit rollback, channel-destruction rollback, hangup commit, uncertain completion, duplicate execution, lock wait, deadlock or later read inconsistency. Do not reproduce a production write merely to obtain cleaner logs.

Separate execution, row match and intended state

Compare the loaded func_odbc function with its intended writehandle, fixed writesql, minimum arguments and VALn or VALUE substitutions. Preserve the database error class and affected-row result without exposing credentials or sensitive data. A syntactically successful statement can match zero rows, match too many rows or write a value that later validation rejects.

If insertsql exists, verify that it ran only after the initial write succeeded with zero affected rows, as documented by the sample. Then inspect database uniqueness and concurrency evidence. Do not label the two-statement pattern an upsert or assume it is atomic unless the selected database contract proves that property.

Trace the active transaction by explicit ID

Confirm that res_odbc_transaction is available, then preserve the transaction-creation statement, database argument, assigned transaction ID and every later change of active transaction. When several transactions share a channel, compare the explicit ID intended by the dialplan with the active ID immediately before each write and completion application.

A connected DSN, a reused connection or single_db_connection does not prove that the statement joined the intended transaction. The upstream sample says the single-connection option is disabled by default, requires additional dialplan locks for its documented use case and recommends stored procedures or functions as a better approach.

Distinguish explicit completion from channel teardown

Locate the exact ODBC_Commit() or ODBC_Rollback() branch and whether it supplied a transaction ID or relied on the current active transaction. Correlate its timing with database evidence. Asterisk documents automatic rollback at channel destruction when no explicit commit occurred and forcecommit is off; separately verify the DSN and per-transaction forcecommit property before assigning a rollback to a database fault.

For unexpected persistence after a hangup, inspect forcecommit, inherited or generated dialplan, hangup handlers and any earlier explicit commit. For unexpected loss, inspect early hangup, transfer or exception paths, transaction selection and completion order. Do not enable forcecommit as a diagnostic shortcut.

Investigate duplicates and concurrency at the database boundary

Correlate each duplicate row or repeated update with channel identity, unique business event, statement execution and database commit record. Separate actual dialplan re-entry, application retry after an uncertain result, two concurrent calls, insertsql racing another writer and a read replica or cache presenting stale state.

Confirm the database isolation level is both configured and supported by the installed driver and DBMS. Review unique constraints, conditional updates, stored routines, lock waits, deadlock detection and idempotency controls with the database owner. Dialplan locks can serialize selected Asterisk paths but do not create a cross-process or database-wide transaction guarantee.

Correct one owner and rehearse failure paths safely

Correct only the evidenced owner: authorization, function contract, transaction selection, statement, schema constraint, isolation, completion branch or retry policy. Use a dedicated non-production fixture or an approved reversible record, then test accepted change, invalid input, zero-row match, concurrent attempt, database rejection, timeout, explicit rollback, caller hangup before commit and uncertain completion.

Confirm the final database state, transaction-resource release, audit evidence and caller treatment separately. Protect credentials and row data throughout. No host, module, driver, DSN, credential, statement, row, transaction, channel, concurrency condition or live call was inspected or tested for this diagnostic.

Sources & applicability

Primary references for the technical details above. Operational examples and planning checklists are VoIP.info editorial guidance.

Examples require adaptation to your topology. No live PBX or hardware testing is claimed.