Guaranteed delivery is the promise enterprises make to customers and regulators: payments, orders, and claims will not silently disappear when a server restarts or a network blips. IBM MQ fulfills that promise through persistent messages, logging, syncpoint commit and backout, and operational patterns for poison messages—not through a single switch labeled "guaranteed." This page explains persistent versus non-persistent messages, how DEFPSIST and MQPMO control durability, what at-least-once delivery means for application developers using MQI, and how queue managers on z/OS and distributed platforms store messages until MQGET succeeds. You will work through MQSC examples you can run on a lab queue manager and see how guaranteed delivery connects to decoupling: producers trust the queue manager to hold work until consumers are ready.
In middleware terms, delivery guarantees describe what happens to a message across failures. IBM MQ does not magically deliver to an application that never runs; it guarantees that the infrastructure will retain a committed persistent message until a valid consumer removes it (subject to expiry, discard policies, and administrative actions). The boundary of responsibility splits clearly: the queue manager owns storage and logging; the application owns processing logic, duplicate detection, and poison handling.
Beginners often confuse guaranteed delivery with exactly-once processing. MQ under syncpoint and with persistent messages provides strong infrastructure durability, but if a consumer gets a message, crashes before commit, and the message becomes available again, processing may run twice. Architects call that at-least-once semantics. Exactly-once business outcomes require idempotent updates—natural keys, duplicate tables, or compare-and-set in the database—layered on top of MQ.
Every put specifies persistence (explicitly in the message descriptor MQMD Persistence field, or implicitly via queue default DEFPSIST). Persistent messages (MQPER_PERSISTENT) are written through the queue manager log to durable media before MQPUT returns success outside syncpoint, or before syncpoint commit when inside a unit of work. Non-persistent messages (MQPER_NOT_PERSISTENT) stay in volatile storage; they are appropriate for high-frequency telemetry, transient status, or scratch traffic where recreation is cheaper than disk I/O.
Mixing both types on one queue is allowed but demands clear standards. Operations monitoring must know which business events are durable. A common anti-pattern is copying production queue names into a lab with DEFPSIST(NO) while applications still assume banking-grade durability. Performance tests that disable persistence skew capacity plans and mislead stakeholders about restart behavior.
On z/OS, persistent messages interact with log streams and possibly Coupling Facility queue structures for sharing across queue managers in a sysplex. On Linux, file systems hosting log and queue directories must meet IBM recommendations for fsync behavior and RAID levels. Guaranteed delivery fails operationally when disks fill, logs cannot extend, or queue MAXDEPTH is reached—MQ will not lose committed persistent messages silently, but puts may fail with reason codes until capacity is restored.
DEFPSIST is defined on local queue objects. DEFPSIST(YES) tells the queue manager to treat incoming messages as persistent when the application leaves persistence as MQPER_AS_Q_DEF. DEFPSIST(NO) defaults to non-persistent. Administrators align DEFPSIST with the queue purpose: PAYMENT.REQUEST with YES, METRICS.STREAM with NO. Applications can override per put using the MQMD or MQPMO persistence options—essential when a single queue carries mixed traffic, though separate queues are cleaner for operations.
Related attributes include DEFPRTY (default priority) and DEFPRESP (default put response). While not strictly delivery guarantees, they affect ordering and whether the putter waits for confirmation. DEFPRESP with synchronous responses can strengthen the application's knowledge that persistence completed before continuing—useful in some payment designs.
The Put Message Options (MQPMO) structure controls put behavior. MQPMO_SYNCPOINT includes the put in the current unit of work; the message is not visible to other applications until MQCMIT. MQPMO_NO_SYNCPOINT puts outside the UOW—still persistent if the descriptor says so, committed when the put completes. MQPMO_NEW_MSG_ID and correlation options support request/reply tracking across retries.
Language bindings map these options: Java JMS delivery mode persistent versus non-persistent; .NET and C MQI structures mirror MQPMO and MQMD fields. A CICS program might issue EXEC CICS SYNCPOINT after successful MQPUT under the same UOW as file updates. Understanding the pairing of MQMD persistence, MQPMO syncpoint, and MQCMIT is central to guaranteed delivery in transactional systems.
Example conceptual flow: open queue, set MQMD Persistence to MQPER_PERSISTENT, set MQPMO with SYNCPOINT, MQPUT, update Db2, MQCMIT. If Db2 fails, MQBACK rolls back the put and the message never appears on the queue. If MQPUT fails, the application should not commit the database. That coordination is how mainframe estates keep ledger and messaging aligned.
Syncpoint defines atomic units across one or more MQ operations and optionally other resource managers. MQCONN or MQCONNX establishes whether the connection is syncpoint capable. Within a unit of work, multiple puts and gets are provisional until commit. MQCMIT makes them permanent; MQBACK undoes them.
On the consumer side, get-commit processing is equally important. A common pattern: MQGET under syncpoint, process, update database, MQCMIT. If processing fails, MQBACK returns the message to the queue (incrementing backout count). That is how at-least-once interacts with failure: the message was never committed as consumed, so another consumer can receive it again.
Global units of work and two-phase commit appear when MQ coordinates with Db2 on z/OS or other XA resource managers. The beginner takeaway: guaranteed delivery to disk is not the same as guaranteed exactly-once business processing—syncpoint spans both messaging and application data when configured.
At-least-once means a message may be delivered one or more times until processing commits successfully. IBM MQ with persistent messages and get under syncpoint typically provides at-least-once when applications back out on failure. At-most-once can occur with non-persistent messages or when consumers commit gets before finishing work—risky for money. Exactly-once end-to-end is a business property: MQ plus idempotent consumer plus deduplication store.
| Semantic | MQ role | Application role |
|---|---|---|
| At-least-once | Persistent storage; get under syncpoint; backout on failure | Tolerate duplicates; use idempotent updates |
| At-most-once | Non-persistent or commit get before process | Accept possible loss on failure |
| Exactly-once (business) | Durable messaging plus transactional coordination | Dedup keys; unique constraints; outbox patterns |
A poison message causes repeated processing failure—every get ends in backout. Without intervention, it reaches BOTHRESH (backout threshold) and moves to BOQNAME (backout queue) if defined, or blocks the queue when consumers cannot skip it. Operations move poison messages to a dead-letter queue (DLQ) for analysis, fix the payload or code, and optionally requeue.
Poison messages do not mean MQ broke guaranteed delivery; they mean the application cannot process valid data. DLQ depth alerts are as important as business queue depth. Request/reply flows can poison reply queues if correlation IDs are wrong. Report messages can help trace where delivery failed in multi-hop channel routes.
Designing poison handling early: set BOTHRESH and BOQNAME on critical queues, document requeue procedures, and log message IDs. Mainframe and distributed teams share responsibility—COBOL and Java consumers must use consistent backout logic.
When messages route between queue managers, persistent messages are transmitted so the receiving queue manager logs them before acknowledging. Channel failures trigger retry; messages remain on the transmission queue until the channel succeeds or operations intervene. Guaranteed delivery across sites assumes channels are correctly paired, TLS and auth are sound, and disk capacity exists on both sides.
Decoupling across data centers depends on this behavior: a payment put in London persists locally, crosses the channel to z/OS, and persists again before the hub acknowledges. Monitoring channel status (RUNNING, RETRYING) is part of delivery assurance, not optional networking detail.
Create a durable payments queue with persistence default and backout handling, then display attributes. Replace QM1 with your lab queue manager name.
12345678910# Define persistent payment queue with backout handling echo "DEFINE QLOCAL('PAYMENT.REQUEST') REPLACE + DESCR('Persistent payment requests') + DEFPSIST(YES) MAXDEPTH(100000) MAXMSGL(4194304) + BOTHRESH(3) BOQNAME('PAYMENT.REQUEST.BOQ') DEFINE QLOCAL('PAYMENT.REQUEST.BOQ') REPLACE + DESCR('Backout queue for poison messages') + DEFPSIST(YES) MAXDEPTH(10000) ALTER QMGR DEADQ('SYSTEM.DEAD.LETTER.QUEUE') DISPLAY QLOCAL('PAYMENT.REQUEST') DEFPSIST BOTHRESH BOQNAME CURDEPTH" | runmqsc QM1
DEFPSIST(YES) ensures default persistent messages. BOTHRESH(3) moves a message to PAYMENT.REQUEST.BOQ after three backouts. Verify DEADQ is set on the queue manager so unroutable messages have a destination. Application puts should still set MQPMO_SYNCPOINT for transactional payments and MQMD persistence when overriding defaults.
On Linux lab systems, persistent messages depend on log and queue directories. Inspect paths configured on the queue manager (values vary by installation).
1234# Display queue manager log and queue paths (Linux example) echo "DISPLAY QMGR LOGPATH QMNAME STATUS" | runmqsc QM1 # Example follow-up: verify disk space on those filesystems df -h $(echo "DISPLAY QMGR LOGPATH" | runmqsc QM1 | grep LOGPATH | awk '{print $2}')
Full disk on the log volume prevents commits and breaks guaranteed delivery from an operations perspective. Include filesystem monitoring in runbooks alongside DISPLAY QLOCAL CURDEPTH.
You put a drawing in a metal locker (persistent message) instead of on a sticky note on a windy playground table (non-persistent). The school (queue manager) promises the drawing stays in the locker until your friend opens it with the right key (MQGET commit). If your friend tries to copy the drawing but drops their pencil and gives up (application failure before commit), the drawing goes back in the locker for another try (at-least-once). If the drawing is impossible to read—scribbles with no picture—a teacher moves it to a special problem locker (backout or dead-letter queue) so other kids' drawings can still be collected.
Classify each as persistent or non-persistent and justify: wire transfer instruction, heartbeat ping every second, settlement file pointer, session cookie for a shopping cart. Note which would use DEFPSIST on the queue definition.
Write step-by-step what happens when a consumer MQGETs under syncpoint, crashes after processing but before MQCMIT, and restarts. Why might the business see duplicate posting if the consumer is not idempotent?
For queue CLAIMS.INTAKE, propose BOTHRESH, BOQNAME, and DLQ handling in five sentences. Who requeues messages after a fix—the application team or operations?
Define a lab queue EXERCISE.PERSIST with DEFPSIST(YES) and BOTHRESH(2). Put two test messages with amqsput if available. DISPLAY CURDEPTH and DEFPSIST. Alter DEFPSIST to NO, put again, and describe restart risk in two sentences.
1. When is a persistent message considered safely on the queue?
2. What does at-least-once delivery imply for application design?
3. Which queue attribute sets default message persistence?
4. What is the purpose of BOTHRESH and BOQNAME?