Troubleshooting ActiveMQ Producer Flow Control Blocks

meshIQ June 18, 2026

The alert comes in at 2 AM: your order processing service is unresponsive. The application is not crashed, threads are running, the JVM is healthy, but no messages are being sent. Your operations team traces it to a blocked send() call on an ActiveMQ connection. Hours later, after restarting the application, someone finds this line in the broker log from 11 PM the previous day:

INFO | Usage(default:store:queue://orders.process:store) percentUsage=99%,
usage=107374182400, limit=107374182400: Persistent store is Full, 100% of 107374182400.
Stopping producer (ID:app-server-1:1:1:1) to prevent flooding queue://orders.process.
See http://activemq.apache.org/producer-flow-control.html for more info (blocking for: 10800s)

Three hours of blocking. The answer was in the log the whole time.

This guide covers ActiveMQ producer flow control troubleshooting from first principles: what PFC is, how to diagnose it immediately from broker logs, what each resource trigger means and how to fix it, the catastrophic deadlock scenario that occurs when producers and consumers share connections, and the configuration changes that prevent recurrence.

Step 1: Read the Broker Log – The PFC Signal

Every producer flow-control event is logged at the INFO level in activemq.log. This is the primary diagnostic tool – no JMX query, no thread dump, no metric dashboard is more direct.

The PFC Log Message Anatomy

INFO | Usage(default:RESOURCE:DESTINATION_TYPE://DESTINATION:RESOURCE)
      percentUsage=99%, usage=BYTES, limit=BYTES, percentUsageMinDelta=1%;
      Parent:Usage(default:RESOURCE) percentUsage=100%, usage=BYTES, limit=BYTES:
      RESOURCE is Full, 100% of LIMIT_BYTES.
      Stopping producer (PRODUCER_ID) to prevent flooding DESTINATION_TYPE://DESTINATION.
      See http://activemq.apache.org/producer-flow-control.html (blocking for: Xs)

How to read it:

FieldWhere to find itWhat it tells you
Resource typeUsage(default:store:queue://…)store = KahaDB disk; temp = temp store; memory = RAM
Destinationqueue://orders.processWhich queue/topic triggered the block
Percent usedpercentUsage=99%How full is the resource
Current usageusage=107374182400Actual bytes consumed
Configured limitlimit=107374182400The systemUsage limit
Producer IDStopping producer (ID:app-1:1:1:1)Which producer is blocked (trace to client)
Block durationblocking for:10800sHow long PFC has been active (seconds)

The most important field is the resource type in the Usage() string. It identifies immediately whether this is a memory problem (memory), a disk problem (store), or a temp store problem (temp).

Real examples from production logs:

Memory trigger:
Usage(default:memory:queue://orders.process:memory) percentUsage=98%…
Memory Usage is Full…  Stopping producer…

Store trigger:
Usage(default:store:queue://orders.process:store) percentUsage=100%…
Persistent store is Full, 100% of 107374182400.  Stopping producer…

Temp store trigger:
Usage(default:temp:queue://orders.process:temp) percentUsage=100%…
Temp Store is Full (100% of 268435456).  Stopping producer…

Step 2: Identify the Root Cause

Root Cause 1: memoryUsage Exhausted

Symptoms: memory in the Usage() string. MemoryPercentUsage JMX metric at or near 100%.

What it means: The broker’s in-memory buffer for holding messages before dispatch (and for non-persistent messages that haven’t been spooled to disk yet) is full. Any new message sent blocks until existing messages are consumed and memory is freed.

The 64MB default problem: Apache ActiveMQ® ships with memoryUsage limit=”64 mb”. This is appropriate for development environments and completely inadequate for production workloads with any meaningful message volume or consumer lag. A single slow consumer holding 65,000 messages of 1KB each exhausts the entire default memory allocation. This is why newly deployed production instances trigger PFC almost immediately under real load.

Primary root causes:

  1. Slow consumer: Messages accumulate faster than they are consumed, filling broker memory. This is the most common cause. We covered slow consumer detection and handling in our Slow Consumer Detection & Handling post.
  2. Under-configured memoryUsage limit: The default 64MB is too small for most production workloads.
  3. Large messages: even a few messages of 1MB+ can exhaust a small memoryUsage budget quickly.

Fix: Right-size memoryUsage

percentOfJvmHeap is the correct approach for production: it automatically scales with JVM heap sizing changes, avoids the need to coordinate memoryUsage changes with JVM -Xmx adjustments, and reflects the actual memory available to the broker. The 70% figure leaves 30% for JVM overhead, message dispatch, threading, and KahaDB index cache.

Fix the slow consumer first: If the root cause is slow consumer accumulation rather than under-configuration, increasing memoryUsage only delays the next PFC event, it does not address the underlying consumption rate problem. Identify the slow consumer via JMX PendingQueueSize before treating the symptom with increased limits.

Root Cause 2: storeUsage Exhausted

Symptoms: store in the Usage() string. StorePercentUsage JMX metric at or near 100%.

What it means: The KahaDB persistent store has reached its configured disk limit. New persistent message sends block until disk space is freed through message consumption and journal file reclamation.

The storeUsage limit vs. actual disk space: There is a subtle trap here that catches many operators. The storeUsage limit is not automatically set to the available disk space, it is a configured cap. If you set storeUsage limit=”100 gb” but KahaDB is on a 50GB partition, PFC triggers from KahaDB filling the physical disk before the configured limit is reached. 

The log message will show the actual disk-capped limit (~50GB), not the configured limit (100GB), which confuses operators who believe they have 100GB configured.

From Apache ActiveMQ® 5.15.x: the total attribute on StoreUsage allows explicit configuration of the total available space so the file system is not queried. This is particularly important for cloud storage (EFS, NFS) where java.io.File.getTotalSpace() may return values exceeding Long.MAX_VALUE, causing overflow.

Primary root causes:

  1. KahaDB journal pinning by slow consumer: An offline or slow consumer prevents journal file reclamation. The journal grows unboundedly until storeUsage is exhausted.
  2. DLQ accumulation: messages accumulating in Dead Letter Queues pin journal files indefinitely.
  3. Undersized storeUsage limit relative to actual disk space.
  4. Missing KahaDB cleanup: long cleanup intervals allow file accumulation.

Fix: Diagnose what is pinning the journal

If the root cause is journal pinning by a slow consumer or DLQ accumulation, fixing the storeUsage limit is insufficient, it buys time until the limit is hit again. Consume or delete the DLQ messages, bring the offline consumer online, or configure mKahaDB destination sharding to isolate the slow destination. We covered both journal pinning and mKahaDB in our Message Persistence Strategies post.

Root Cause 3: tempUsage Exhausted

Symptoms: temp in the Usage() string. TempPercentUsage JMX metric at or near 100%.

What it means: The temporary file store, where non-persistent messages are spooled to disk when memory pressure builds, has reached its configured limit. This is less commonly encountered than memory or store exhaustion, but when it does, it is often mystifying because operators focus on memoryUsage and storeUsage while overlooking tempUsage.

The tempUsage < memoryUsage trap: If tempUsage is configured smaller than memoryUsage, the following sequence occurs: memory fills, the broker tries to spool non-persistent messages to the temp store to free memory, the temp store fills first because it has a smaller limit, and PFC triggers on temp exhaustion even though the configured memoryUsage limit has not been reached. The log will show tempUsage at 100% while memoryUsage is at 70-80%.

The additional complication: even a small number of non-persistent messages can exhaust a small tempUsage because KahaDB creates temp store journal files at the configured journalMaxFileLength (default 32MB) per file. If tempUsage limit=”50 mb” and the journal file size is 32MB, two journal files fill the entire temp store budget.

Fix: Size tempUsage relative to storeUsage and journalMaxFileLength

If your broker has predominantly persistent messaging, tempUsage can be modest (10-50GB). If you have significant non-persistent message volume with consumers that may lag, size tempUsage generously, it is the spooling buffer that protects memoryUsage under burst conditions.

The Critical Failure Mode: PFC Deadlock via Shared Connections

This is the most dangerous PFC scenario, and it is not intuitive from reading the documentation. It requires a specific understanding of how ActiveMQ connection threading works.

The setup: A client application has a single Connection from which it creates both a Session with a producer (for sending messages) and a Session with a consumer (for receiving replies or acknowledgments).

The sequence:

  1. PFC activates, a resource limit is hit
  2. The broker blocks the producer’s send(), this blocks the thread holding the producer
  3. PFC in Apache ActiveMQ® blocks the entire connection, not just the producing session
  4. The consumer’s session is on the same connection, it is also frozen
  5. Messages pending in the consumer’s buffer cannot be acknowledged
  6. The broker is waiting for consumer acknowledgments to free memory
  7. Consumer acknowledgments cannot arrive because the connection is frozen
  8. The producer cannot unblock because the memory it needs never frees
  9. Permanent deadlock requires a connection restart to break

The Apache mailing list has a precise description of this phenomenon: “PFC might be better named ‘producer connection flow control’ because the entire connection of the producer blocks, not just the one producer.”

Experiencing Unexplained Producer Blocks or Application Hangs?

If your application has been producing messages and suddenly freezes with no crash, no error, just silence, then producer flow control deadlock is the most likely cause. meshIQ’s team diagnoses PFC root causes daily and can help you identify whether your current architecture is at risk.

Talk to an Expert

The prevention rule: Always use separate connections for producers and consumers. This is the single most important architectural rule for avoiding PFC deadlock. A producer connection and a consumer connection operate independently. PFC on the producer connection cannot freeze the consumer connection.

Configuring PFC Behavior: From Blocking to Exceptions

The default PFC behavior, indefinite blocking, is the worst outcome for production applications. An application that blocks indefinitely on send() is effectively frozen. It holds threads, consumes connection resources, and gives no signal to upstream systems that something is wrong. The correct production configuration replaces indefinite blocking with exception-based notification.

Option 1: sendFailIfNoSpace – Immediate Exception

When any resource limit is hit, the broker immediately throws javax.jms.ResourceAllocationException to the producer. The application must catch this and implement retry logic. Advantage: the application is never frozen. Disadvantage: brief, transient resource spikes (a GC pause, a momentary consumer lag) produce exceptions rather than resolving automatically.

This is the recommended production configuration. The broker blocks for up to 30 seconds (configurable), giving temporary resource pressure time to resolve naturally. Only if the resource is still exhausted after 30 seconds does the broker throw ResourceAllocationException. Applications must still catch and retry, but brief spikes resolve without application-level errors.

Client-Side: Catching ResourceAllocationException

Per-Destination Configuration (Apache ActiveMQ® 5.16+)

From Apache ActiveMQ® 5.16.0, sendFailIfNoSpace and sendFailIfNoSpaceAfterTimeout can be configured per destination via destinationPolicy. This allows critical queues to fail fast (immediate exception) while high-volume topics tolerate brief blocking:

Apache Artemis™: address-full-policy

Apache Artemis™’s flow control model is fundamentally different from Apache ActiveMQ®. Rather than a broker-wide resource limit with a single PFC trigger, Apache Artemis™ uses per-address policies with four configurable behaviors:

Apache Artemis™ address-full-policy options:

PolicyBehaviorUse Case
BLOCKBlock producer send() until the address drops below the limitTransactional queues where message loss is unacceptable
PAGESpool messages to disk paging files; producer never blockedHigh-volume queues with burst patterns
DROPDiscard new messages silently when fullLow-priority, non-critical data
FAILThrow an exception to the producer immediately when fullCritical queues where the application must handle backpressure

The key Apache Artemis™ advantage over Apache ActiveMQ®: PFC is per-address, not broker-wide. A full address blocks only producers sending to that address. Other destinations continue unaffected. This eliminates the Apache ActiveMQ® scenario where a slow consumer on one queue blocks producers on completely unrelated queues.

We covered the Apache ActiveMQ® vs Apache Artemis™ architectural differences, including flow control as a key migration consideration in our Classic vs Artemis: 2026 Definitive Guide.

Detect Producer Flow Control Before It Freezes Your Application

meshIQ Console monitors MemoryPercentUsage, StorePercentUsage, and TempPercentUsage across all your brokers with pre-configured PFC prevention alerts, notifying your team when resource limits are approaching so you can act before producers block.

See It in Action

PFC Root Cause Decision Matrix

Use this table to map your log message and symptoms to the correct remediation:

Log ShowsJMX Metric HighPrimary Root CauseImmediate FixPermanent Fix
memory resourceMemoryPercentUsage > 70%Slow consumer / undersized memoryUsageIncrease memoryUsage limitFix slow consumer; use percentOfJvmHeap
store resourceStorePercentUsage > 85%KahaDB journal pinning/disk fullIncrease storeUsage limit or free diskFix journal pinning (DLQ, slow consumer); mKahaDB
temp resourceTempPercentUsage > 75%Non-persistent message spooling overflowIncrease tempUsage limitSize tempUsage ≥ 2× journalMaxFileLength
No PFC logApplication hangs anywayPFC deadlock (shared connection)Restart the application connectionSeparate producer/consumer connections
PFC on NoB bridgeMultiple brokers block simultaneouslyBridge connection blocked by PFCClear consuming destinationReduce message rate or add consumers

Monitoring PFC to Prevent Recurrence

PFC is a symptom of an underlying resource imbalance. The goal is not just to fix the current incident, it is to alert before the next one. The alert thresholds from our Monitoring & Alerting Setup post map directly to PFC prevention:

  • MemoryPercentUsage > 70% (Warning) – PFC will trigger at 100%; this gives you a 30% buffer to act
  • StorePercentUsage > 70% (Warning), > 85% (Critical) – store-triggered PFC is preventable with ahead-of-time alerts
  • TempPercentUsage > 75% (Warning) – temp store exhaustion is the most frequently missed alert
  • ConsumerCount = 0 on any critical queue (Critical) – the most common slow-consumer trigger for memory PFC

Adding a Prometheus alert for the PFC log message itself (via log scraping) provides the earliest possible warning:

Fix the Root Cause, Not Just the Limit

ActiveMQ Producer flow control is not a bug to eliminate, it is a protection mechanism to understand and configure correctly. The default systemUsage values that ship with ActiveMQ are development defaults, not production defaults. Every production deployment needs explicit systemUsage configuration aligned with the broker’s JVM heap, available disk, and expected message volume.

The goal after resolving a PFC incident is not just to increase the limit, it is to identify the root cause (slow consumer, journal pinning, undersized memory, DLQ accumulation), fix it, and put monitoring in place so the next pressure event triggers an alert rather than a 3 AM application freeze.

Get your ActiveMQ systemUsage configuration reviewed by our team → Talk to an Expert

Frequently Asked Questions

Q1: Why is my ActiveMQ producer blocking? 

Producer flow control activates when one of three resource limits is hit: memoryUsage (in-memory buffer), storeUsage (KahaDB persistent store disk limit), or tempUsage (non-persistent message spooling). Check the broker log for the INFO | Stopping producer message, it identifies the specific resource and destination. The default 64MB memoryUsage limit is the most common trigger in production.

Q2: How do I read the ActiveMQ PFC log message? 

The key field is the resource type inside Usage(default:TYPE:…) – store means disk storage exhausted, temp means temp store exhausted, and memory means RAM buffer exhausted. The destination name, percentage used, and blocking duration are also in the message. This single line contains everything needed to diagnose a PFC event.

Q3: What is the default systemUsage configuration in ActiveMQ? 

Apache ActiveMQ® ships with memoryUsage=64MB, storeUsage=100GB, tempUsage=50GB. The 64MB memoryUsage default is the primary reason production deployments hit PFC quickly, it is a development-appropriate value, not production-appropriate. Change it to percentOfJvmHeap=”70″ for production.

Q4: What is the difference between sendFailIfNoSpace and sendFailIfNoSpaceAfterTimeout? 

Both convert PFC from indefinite blocking to exception-based notification. sendFailIfNoSpace=true throws immediately. sendFailIfNoSpaceAfterTimeout=MILLIS blocks for the configured time before throwing. The timeout variant is recommended for production, it handles transient pressure without generating false-alarm exceptions.

Q5: Can a producer block a consumer on the same connection? 

Yes, this is the PFC deadlock. PFC blocks the entire connection, not just the producer session. A consumer on the same connection cannot send acknowledgments, so the broker cannot free memory, and the producer cannot unblock. Prevention: always use separate connections for producers and consumers.

Cookies preferences

Others

Other uncategorized cookies are those that are being analyzed and have not been classified into a category as yet.

Necessary

Necessary
Necessary cookies are absolutely essential for the website to function properly. These cookies ensure basic functionalities and security features of the website, anonymously.

Advertisement

Advertisement cookies are used to provide visitors with relevant ads and marketing campaigns. These cookies track visitors across websites and collect information to provide customized ads.

Analytics

Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics the number of visitors, bounce rate, traffic source, etc.

Functional

Functional cookies help to perform certain functionalities like sharing the content of the website on social media platforms, collect feedbacks, and other third-party features.

Performance

Performance cookies are used to understand and analyze the key performance indexes of the website which helps in delivering a better user experience for the visitors.