---
title: "Troubleshooting ActiveMQ Producer Flow Control Blocks"
date: 2026-06-18
author: "TheFrameGuy"
featured_image: "https://www.meshiq.com/wp-content/uploads/blog_activeMQ-troubleshoot-flow_061726.jpg"
categories:
  - name: "Apache ActiveMQ®"
    url: "/sort-by/active-mq.md"
  - name: "Middleware Optimization"
    url: "/sort-by/middleware-optimization.md"
  - name: "Monitoring"
    url: "/sort-by/monitoring.md"
  - name: "Observability"
    url: "/sort-by/observability.md"
tags:
  - name: "middleware"
    url: "/sort-by/tag/middleware.md"
  - name: "monitoring"
    url: "/sort-by/tag/monitoring.md"
  - name: "Observability"
    url: "/sort-by/tag/observability.md"
---

# Troubleshooting ActiveMQ Producer Flow Control Blocks

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**:

**Field****Where to find it****What it tells you**Resource typeUsage(default:**store**:queue://…)store = KahaDB disk; temp = temp store; memory = RAMDestinationqueue://**orders.process**Which queue/topic triggered the blockPercent usedpercentUsage=**99%**How full is the resourceCurrent usageusage=**107374182400**Actual bytes consumedConfigured limitlimit=**107374182400**The systemUsage limitProducer IDStopping producer (**ID:app-1:1:1:1**)Which producer is blocked (trace to client)Block durationblocking for:**10800s**How 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 &amp; Handling**](https://www.meshiq.com/blog/activemq-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**

&lt;!– activemq.xml — production systemUsage configuration –&gt;  
&lt;**systemUsage**&gt;  
 &lt;**systemUsage**&gt;  
 &lt;!– Option A: percentage of JVM heap (recommended — scales with JVM sizing) –&gt;  
 &lt;**memoryUsage**&gt;  
 &lt;**memoryUsage** percentOfJvmHeap=”70″/&gt;  
 &lt;/**memoryUsage**&gt;  
 &lt;!– Option B: absolute value for environments with known heap sizes –&gt;  
 &lt;!– &lt;memoryUsage&gt;&lt;memoryUsage limit=”2 gb”/&gt;&lt;/memoryUsage&gt; –&gt;  
  
 &lt;!– Persistent store: should reflect actual disk capacity –&gt;  
 &lt;**storeUsage**&gt;  
 &lt;**storeUsage** limit=”500 gb”/&gt;  
 &lt;/**storeUsage**&gt;  
  
 &lt;!– Temp store: non-persistent message spooling –&gt;  
 &lt;**tempUsage**&gt;  
 &lt;**tempUsage** limit=”50 gb”/&gt;  
 &lt;/**tempUsage**&gt;  
 &lt;/**systemUsage**&gt;  
&lt;/**systemUsage**&gt;

**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**

\# Check actual KahaDB directory size vs. configured storeUsage limit  
du -sh /opt/activemq/data/kahadb/  
  
\# Via JMX: check which queues have messages  
\# (look for queues with large QueueSize that are not consuming)  
\# MBean: org.apache.activemq:type=Broker,brokerName=\*,  
\# destinationType=Queue,destinationName=\*  
  
&lt;!– Fix 1: Right-size storeUsage to match actual available disk –&gt;  
&lt;**storeUsage**&gt;  
 &lt;**storeUsage** limit=”400 gb”/&gt; &lt;!– 80% of 500GB disk –&gt;  
 &lt;!– From 5.15.x: use total= to prevent file system queries –&gt;  
 &lt;!– &lt;storeUsage limit=”400 gb” total=”500 gb”/&gt; –&gt;  
&lt;/**storeUsage**&gt;  
  
&lt;!– Fix 2: Reduce KahaDB cleanup interval for faster journal reclamation –&gt;  
&lt;**persistenceAdapter**&gt;  
 &lt;**kahaDB** directory=”/opt/activemq/data/kahadb”  
 cleanupInterval=”15000″/&gt; &lt;!– 15 seconds, down from default 30 –&gt;  
&lt;/**persistenceAdapter**&gt;  


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**](https://www.meshiq.com/blog/activemq-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 &lt; 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**

&lt;**systemUsage**&gt;  
 &lt;**systemUsage**&gt;  
 &lt;**memoryUsage**&gt;  
 &lt;**memoryUsage** percentOfJvmHeap=”70″/&gt;  
 &lt;/**memoryUsage**&gt;  
 &lt;**storeUsage**&gt;  
 &lt;**storeUsage** limit=”500 gb”/&gt;  
 &lt;/**storeUsage**&gt;  
 &lt;!– Rule of thumb: tempUsage should be &gt;= 2x journalMaxFileLength –&gt;  
 &lt;!– and should be sized for your expected non-persistent message volume –&gt;  
 &lt;**tempUsage**&gt;  
 &lt;**tempUsage** limit=”50 gb”/&gt;  
 &lt;/**tempUsage**&gt;  
 &lt;/**systemUsage**&gt;  
&lt;/**systemUsage**&gt;

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****](https://www.meshiq.com/apache-activemq/enterprise-support/)



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.

// WRONG: producer and consumer on same connection  
ConnectionFactory factory = new ActiveMQConnectionFactory(brokerUrl);  
Connection sharedConnection = factory.createConnection();  
sharedConnection.start();  
  
Session producerSession = sharedConnection.createSession(false, AUTO\_ACKNOWLEDGE);  
MessageProducer producer = producerSession.createProducer(orderQueue);  
  
Session consumerSession = sharedConnection.createSession(false, AUTO\_ACKNOWLEDGE);  
MessageConsumer consumer = consumerSession.createConsumer(replyQueue);  
// PFC on producerSession will ALSO block consumerSession → DEADLOCK RISK  
  
// CORRECT: separate connections  
Connection producerConn = factory.createConnection();  
producerConn.start();  
Session producerSession = producerConn.createSession(false, AUTO\_ACKNOWLEDGE);  
MessageProducer producer = producerSession.createProducer(orderQueue);  
  
Connection consumerConn = factory.createConnection();  
consumerConn.start();  
Session consumerSession = consumerConn.createSession(false, AUTO\_ACKNOWLEDGE);  
MessageConsumer consumer = consumerSession.createConsumer(replyQueue);  
// PFC on producerConn cannot affect consumerConn



## 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

&lt;**systemUsage**&gt;  
 &lt;**systemUsage** sendFailIfNoSpace=”true”&gt;  
 &lt;**memoryUsage**&gt;  
 &lt;**memoryUsage** percentOfJvmHeap=”70″/&gt;  
 &lt;/**memoryUsage**&gt;  
 &lt;**storeUsage**&gt;  
 &lt;**storeUsage** limit=”500 gb”/&gt;  
 &lt;/**storeUsage**&gt;  
 &lt;**tempUsage**&gt;  
 &lt;**tempUsage** limit=”50 gb”/&gt;  
 &lt;/**tempUsage**&gt;  
 &lt;/**systemUsage**&gt;  
&lt;/**systemUsage**&gt;

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.

### Option 2: sendFailIfNoSpaceAfterTimeout – Timed Block (Recommended)

&lt;**systemUsage**&gt;  
 &lt;!– Wait 30 seconds before failing: allows transient pressure to clear –&gt;  
 &lt;**systemUsage** sendFailIfNoSpaceAfterTimeout=”30000″&gt;  
 &lt;**memoryUsage**&gt;  
 &lt;**memoryUsage** percentOfJvmHeap=”70″/&gt;  
 &lt;/**memoryUsage**&gt;  
 &lt;**storeUsage**&gt;  
 &lt;**storeUsage** limit=”500 gb”/&gt;  
 &lt;/**storeUsage**&gt;  
 &lt;**tempUsage**&gt;  
 &lt;**tempUsage** limit=”50 gb”/&gt;  
 &lt;/**tempUsage**&gt;  
 &lt;/**systemUsage**&gt;  
&lt;/**systemUsage**&gt;

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

private void sendWithRetry(MessageProducer producer, Message message,  
 int maxRetries, long retryDelay)  
 throws JMSException, InterruptedException {  
 for (int attempt = 1; attempt &lt;= maxRetries; attempt++) {  
 try {  
 producer.send(message);  
 return; // Success  
 } catch (ResourceAllocationException e) {  
 if (attempt == maxRetries) {  
 logger.error(“Send failed after {} attempts: resource exhausted”, maxRetries);  
 throw e;  
 }  
 logger.warn(“Broker resource exhausted (attempt {}/{}), retrying in {}ms”,  
 attempt, maxRetries, retryDelay);  
 Thread.sleep(retryDelay);  
 retryDelay = Math.min(retryDelay \* 2, 60\_000L); // Exponential backoff, max 60s  
 }  
 }  
}

### 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:

&lt;**destinationPolicy**&gt;  
 &lt;**policyMap**&gt;  
 &lt;**policyEntries**&gt;  
 &lt;!– Critical order queue: fail fast, never block indefinitely –&gt;  
 &lt;**policyEntry** queue=”orders.&gt;”&gt;  
 &lt;**deadLetterStrategy**&gt;  
 &lt;**sharedDeadLetterStrategy** processNonPersistent=”true”/&gt;  
 &lt;/**deadLetterStrategy**&gt;  
 &lt;/**policyEntry**&gt;  
  
 &lt;!– Market data topic: allow brief blocking before failing –&gt;  
 &lt;**policyEntry** topic=”market.data.&gt;”  
 producerFlowControl=”true”&gt;  
 &lt;!– Per-destination sendFailIfNoSpaceAfterTimeout available 5.16+ –&gt;  
 &lt;/**policyEntry**&gt;  
 &lt;/**policyEntries**&gt;  
 &lt;/**policyMap**&gt;  
&lt;/**destinationPolicy**&gt;

## 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:

&lt;!– broker.xml — Artemis address full policy –&gt;  
&lt;**address-settings**&gt;  
  
 &lt;!– Critical queues: BLOCK producers when address is full –&gt;  
 &lt;**address-setting** match=”orders.#”&gt;  
 &lt;**max-size-bytes**&gt;209715200&lt;/**max-size-bytes**&gt; &lt;!– 200MB –&gt;  
 &lt;**page-size-bytes**&gt;10485760&lt;/**page-size-bytes**&gt; &lt;!– 10MB page files –&gt;  
 &lt;**address-full-policy**&gt;BLOCK&lt;/**address-full-policy**&gt;  
 &lt;!– Producer is blocked until address size drops below max-size-bytes –&gt;  
 &lt;/**address-setting**&gt;  
  
 &lt;!– High-volume telemetry: PAGE to disk, never block producers –&gt;  
 &lt;**address-setting** match=”telemetry.#”&gt;  
 &lt;**max-size-bytes**&gt;104857600&lt;/**max-size-bytes**&gt; &lt;!– 100MB –&gt;  
 &lt;**address-full-policy**&gt;PAGE&lt;/**address-full-policy**&gt;  
 &lt;!– Messages paged to disk when limit is reached; producer never blocked –&gt;  
 &lt;/**address-setting**&gt;  
  
 &lt;!– Low-priority data: DROP oldest messages when full –&gt;  
 &lt;**address-setting** match=”logs.low-priority.#”&gt;  
 &lt;**max-size-bytes**&gt;52428800&lt;/**max-size-bytes**&gt; &lt;!– 50MB –&gt;  
 &lt;**address-full-policy**&gt;DROP&lt;/**address-full-policy**&gt;  
 &lt;!– New messages are discarded when limit is reached; producer never blocked –&gt;  
 &lt;/**address-setting**&gt;  
  
 &lt;!– Default: PAGE for everything else –&gt;  
 &lt;**address-setting** match=”#”&gt;  
 &lt;**max-size-bytes**&gt;-1&lt;/**max-size-bytes**&gt; &lt;!– No limit –&gt;  
 &lt;**address-full-policy**&gt;PAGE&lt;/**address-full-policy**&gt;  
 &lt;/**address-setting**&gt;  
  
&lt;/**address-settings**&gt;

**Apache Artemis™ address-full-policy options:**

**Policy****Behavior****Use Case**BLOCKBlock producer send() until the address drops below the limitTransactional queues where message loss is unacceptablePAGESpool messages to disk paging files; producer never blockedHigh-volume queues with burst patternsDROPDiscard new messages silently when fullLow-priority, non-critical dataFAILThrow 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**](https://www.meshiq.com/blog/apache-activemq-vs-apache-artemis/).

## **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******](https://www.meshiq.com/request-a-demo/)



## PFC Root Cause Decision Matrix

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

**Log Shows****JMX Metric High****Primary Root Cause****Immediate Fix****Permanent Fix**memory resourceMemoryPercentUsage &gt; 70%Slow consumer / undersized memoryUsageIncrease memoryUsage limitFix slow consumer; use percentOfJvmHeapstore resourceStorePercentUsage &gt; 85%KahaDB journal pinning/disk fullIncrease storeUsage limit or free diskFix journal pinning (DLQ, slow consumer); mKahaDBtemp resourceTempPercentUsage &gt; 75%Non-persistent message spooling overflowIncrease tempUsage limitSize tempUsage ≥ 2× journalMaxFileLengthNo PFC logApplication hangs anywayPFC deadlock (shared connection)Restart the application connectionSeparate producer/consumer connectionsPFC 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 &amp; Alerting Setup**](https://www.meshiq.com/blog/activemq-monitoring-alerting-setup/) post map directly to PFC prevention:

- MemoryPercentUsage &gt; 70% (Warning) – PFC will trigger at 100%; this gives you a 30% buffer to act
- StorePercentUsage &gt; 70% (Warning), &gt; 85% (Critical) – store-triggered PFC is preventable with ahead-of-time alerts
- TempPercentUsage &gt; 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:

\# Alert on PFC log messages appearing in broker logs  
\# (via Loki or similar log aggregation)  
count\_over\_time({job=”activemq”} |= “Stopping producer” \[5m\]) &gt; 0

## 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](https://www.meshiq.com/apache-activemq/enterprise-support/)**

## **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.