Skip to content

Commit 2a59183

Browse files
committed
Confirm auto_delete_addresses_with_shared_messages
1 parent 9f7ebd6 commit 2a59183

9 files changed

Lines changed: 1739 additions & 1 deletion

File tree

ADDRESS_AUTO_DELETE_SIZE_ISSUE.md

Lines changed: 365 additions & 0 deletions
Large diffs are not rendered by default.

artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1068,7 +1068,8 @@ private void deleteDuplicateCache(SimpleString address) throws Exception {
10681068
@Override
10691069
public boolean isAddressBound(final SimpleString address) throws Exception {
10701070
Collection<Binding> bindings = getDirectBindings(address);
1071-
return bindings != null && !bindings.isEmpty();
1071+
1072+
return (bindings != null && !bindings.isEmpty());// || pagingManager.getPageStore(address).getAddressSize() > 0;
10721073
}
10731074

10741075
@Override

tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/JmsWildcardAutoDeleteAddressTest.java

Lines changed: 402 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# MQTT Wildcard Queue Auto-Delete Bug Analysis
2+
3+
## Bug Confirmed: Auto-Delete Logic Does Not Check Wildcard Queue Bindings
4+
5+
The diagnostic output from the test proves this is an **auto-delete bug**, not a console bug.
6+
7+
## Evidence from Test Execution
8+
9+
### BEFORE RESTART (testWildcardQueueBindingPreventsAutoDelete)
10+
11+
```
12+
Addresses and their queues:
13+
Address: TEST.1 (AutoCreated: true, RoutingTypes: [MULTICAST], AddressSize: 798 bytes)
14+
Bindings (1):
15+
- mqtt-subscriber.TEST.# (Type: LOCAL_QUEUE)
16+
Queues (1):
17+
- mqtt-subscriber.TEST.# (Durable: true, Messages: 1, Consumers: 0, PersistentSize: 240 bytes)
18+
19+
Address: TEST.# (AutoCreated: true, RoutingTypes: [MULTICAST], AddressSize: 72 bytes)
20+
Bindings (1):
21+
- mqtt-subscriber.TEST.# (Type: LOCAL_QUEUE)
22+
Queues (1):
23+
- mqtt-subscriber.TEST.# (Durable: true, Messages: 1, Consumers: 0, PersistentSize: 240 bytes)
24+
```
25+
26+
**Key Findings:**
27+
1. ✓ The wildcard queue `mqtt-subscriber.TEST.#` **IS** bound to address `TEST.1`
28+
2. ✓ The binding is real and active (queue has 1 message received from TEST.1)
29+
3. ✓ The same queue appears in both addresses' binding lists
30+
4. ✓ Address `TEST.1` has **798 bytes** in paging store (proving it holds real data)
31+
5. ✓ Address `TEST.#` has **72 bytes** in paging store
32+
6. ✓ Queue persistent size is **240 bytes** (the actual message size)
33+
34+
### AFTER RESTART
35+
36+
```
37+
AMQ224113: Auto removing address TEST.1
38+
39+
Addresses and their queues:
40+
Address: TEST.# (AutoCreated: true, RoutingTypes: [MULTICAST], AddressSize: 72 bytes)
41+
Bindings (1):
42+
- mqtt-subscriber.TEST.# (Type: LOCAL_QUEUE)
43+
Queues (1):
44+
- mqtt-subscriber.TEST.# (Durable: true, Messages: 1, Consumers: 0, PersistentSize: 240 bytes)
45+
```
46+
47+
**Key Findings:**
48+
1. ✗ Address `TEST.1` was **incorrectly auto-deleted** during broker startup
49+
2. ✗ The **798 bytes** in `TEST.1` paging store was lost when the address was deleted
50+
3. ✓ The wildcard queue `mqtt-subscriber.TEST.#` survived (it's durable)
51+
4. ✓ The **240 bytes** of persistent queue data survived the restart
52+
5. ✗ The auto-delete logic did not detect the wildcard queue's binding to `TEST.1`
53+
6. ✗ Despite having 798 bytes in the paging store, `TEST.1` was still deleted
54+
55+
## Root Cause Analysis
56+
57+
The auto-delete logic during broker restart is checking if an address has queues bound to it, but it's **failing to detect wildcard queue bindings**.
58+
59+
### How Wildcard Queues Work
60+
61+
1. MQTT client subscribes to `TEST/#` (wildcard subscription)
62+
2. Artemis creates queue `mqtt-subscriber.TEST.#` bound to address `TEST.#`
63+
3. When a message is published to `TEST.1`:
64+
- Address `TEST.1` is auto-created
65+
- The message is routed via multicast to matching wildcard subscriptions
66+
- The wildcard queue `mqtt-subscriber.TEST.#` receives the message
67+
- **The queue gets bound to the specific address `TEST.1`**
68+
69+
4. On broker restart:
70+
- The durable queue `mqtt-subscriber.TEST.#` is restored
71+
- Address `TEST.#` is restored with the queue binding
72+
- Address `TEST.1` is restored from bindings journal
73+
- **BUG**: Auto-delete logic checks `TEST.1` and fails to find the wildcard queue binding
74+
- Address `TEST.1` is incorrectly removed
75+
76+
## Why This Is a Problem
77+
78+
1. **Data Loss Risk**: Messages in the queue for `TEST.1` topics could be lost
79+
2. **Inconsistent State**: Before restart, the binding exists; after restart, it doesn't
80+
3. **User Confusion**: The console shows the binding before restart, then it disappears
81+
4. **Violates Expectations**: Addresses with bound queues should not be auto-deleted
82+
5. **Persistent Data Ignored**: Even with 240 bytes of persistent data, the address is deleted
83+
6. **Wildcard Routing Broken**: After restart, new messages to `TEST.1` may not route to the wildcard queue
84+
85+
## Expected Behavior
86+
87+
When checking if an address can be auto-deleted, the logic should:
88+
1. Check for direct queue bindings (currently working)
89+
2. **Check for wildcard queue bindings** (currently NOT working)
90+
3. Only delete if there are no bindings of any kind
91+
92+
An address like `TEST.1` should survive restart if:
93+
- Any durable queue is bound to it, OR
94+
- Any wildcard queue (e.g., `TEST.#`) has a binding to it
95+
96+
## Fix Required
97+
98+
The auto-delete logic in the broker startup code needs to be updated to:
99+
1. When checking if address `TEST.1` can be deleted
100+
2. Look for ALL queues bound to it, including wildcard queues
101+
3. If ANY queue (direct or wildcard) is bound, do NOT auto-delete
102+
103+
## Test Results
104+
105+
Both test methods successfully reproduce the bug:
106+
107+
1. **testWildcardQueueBindingPreventsAutoDelete**:
108+
- Status: **FAILS** (expected behavior after fix: PASS)
109+
- Proves the wildcard queue IS bound before restart
110+
- Proves TEST.1 is incorrectly deleted after restart
111+
112+
2. **testWildcardSubscriptionPreventsAutoDeleteAfterRestart**:
113+
- Status: **PASSES** (currently expects buggy behavior)
114+
- Confirms TEST.1 is deleted after restart
115+
- Includes diagnostic message: "BUG REPRODUCED"
116+
117+
## Files Created
118+
119+
1. `MqttWildcardAutoDeleteAddressTest.java` - Test reproduction
120+
2. `MqttWildcardAutoDeleteAddressTest_README.md` - Test documentation
121+
3. `BUG_ANALYSIS.md` - This file with detailed analysis
122+
123+
## Next Steps for Developers
124+
125+
1. Review the auto-delete logic in the broker startup code
126+
2. Identify where address auto-deletion decisions are made
127+
3. Update the logic to check for wildcard queue bindings
128+
4. Run the test to verify the fix
129+
5. Update test assertions to expect correct behavior
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# Code Location Causing Auto-Delete Bug
2+
3+
## Call Stack During Auto-Delete
4+
5+
### 1. Entry Point (During Broker Startup/Sweep)
6+
**File**: `artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java`
7+
**Method**: `reapAddresses()`
8+
**Lines**: 2090-2100
9+
10+
```java
11+
Set<SimpleString> addresses = addressManager.getAddresses();
12+
13+
for (SimpleString address : addresses) {
14+
AddressInfo addressInfo = getAddressInfo(address);
15+
AddressSettings settings = addressSettingsRepository.getMatch(address.toString());
16+
17+
try {
18+
if (addressManager.checkAutoRemoveAddress(addressInfo, settings, initialCheck)) {
19+
if (initialCheck || addressInfo.isSwept()) {
20+
// THIS LINE TRIGGERS THE DELETION
21+
server.autoRemoveAddressInfo(address, null); // LINE 2100
22+
```
23+
24+
### 2. Auto-Delete Decision Logic (THE BUG IS HERE)
25+
**File**: `artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/SimpleAddressManager.java`
26+
**Method**: `checkAutoRemoveAddress()`
27+
**Line**: 414
28+
29+
```java
30+
@Override
31+
public boolean checkAutoRemoveAddress(AddressInfo addressInfo,
32+
AddressSettings settings,
33+
boolean ignoreDelay) throws Exception {
34+
return settings.isAutoDeleteAddresses() &&
35+
addressInfo != null &&
36+
addressInfo.isAutoCreated() &&
37+
!bindingsFactory.isAddressBound(addressInfo.getName()) && // BUG: This check is incomplete
38+
(ignoreDelay || addressWasUsed(addressInfo, settings)) &&
39+
(ignoreDelay || delayCheck(addressInfo, settings));
40+
}
41+
```
42+
43+
**The Problem**: The check `!bindingsFactory.isAddressBound(addressInfo.getName())` returns `true` (meaning "no bindings, safe to delete") even when wildcard queues are bound to the address.
44+
45+
### 3. Binding Check (Root Cause)
46+
**File**: `artemis-server/src/main/java/org/apache/activemq/artemis/core/postoffice/impl/PostOfficeImpl.java`
47+
**Method**: `isAddressBound()`
48+
**Lines**: 1069-1072
49+
50+
```java
51+
@Override
52+
public boolean isAddressBound(final SimpleString address) throws Exception {
53+
Collection<Binding> bindings = getDirectBindings(address); // LINE 1070 - BUG!
54+
return bindings != null && !bindings.isEmpty(); // LINE 1071
55+
}
56+
```
57+
58+
**The Root Cause**:
59+
- Line 1070 uses `getDirectBindings(address)`
60+
- This method **ONLY returns direct bindings** to the address
61+
- It **DOES NOT return wildcard queue bindings**
62+
- For address `TEST.1`, it won't find the wildcard queue `TEST.#` even though that queue is actively bound to and consuming from `TEST.1`
63+
64+
### 4. Actual Deletion
65+
**File**: `artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ActiveMQServerImpl.java`
66+
**Method**: `autoRemoveAddressInfo()`
67+
**Lines**: 4047-4050
68+
69+
```java
70+
public void autoRemoveAddressInfo(SimpleString address, SecurityAuth auth) throws Exception {
71+
removeAddressInfo(address, auth, true);
72+
// THIS IS WHERE THE LOG MESSAGE IS GENERATED
73+
ActiveMQServerLogger.LOGGER.autoRemoveAddress(String.valueOf(address)); // LINE 4050
74+
}
75+
```
76+
77+
This generates the log: `AMQ224113: Auto removing address TEST.1`
78+
79+
## The Fix Needed
80+
81+
The `isAddressBound()` method in PostOfficeImpl.java needs to be updated to check **ALL** bindings, not just direct bindings.
82+
83+
### Current (Buggy) Code:
84+
```java
85+
public boolean isAddressBound(final SimpleString address) throws Exception {
86+
Collection<Binding> bindings = getDirectBindings(address); // Only checks direct bindings
87+
return bindings != null && !bindings.isEmpty();
88+
}
89+
```
90+
91+
### Proposed Fix:
92+
```java
93+
public boolean isAddressBound(final SimpleString address) throws Exception {
94+
// Check direct bindings
95+
Collection<Binding> bindings = getDirectBindings(address);
96+
if (bindings != null && !bindings.isEmpty()) {
97+
return true;
98+
}
99+
100+
// Also check for wildcard bindings
101+
Bindings allBindings = getBindingsForAddress(address);
102+
return allBindings != null && !allBindings.getBindings().isEmpty();
103+
}
104+
```
105+
106+
## Why This Matters
107+
108+
For MQTT wildcard subscriptions:
109+
1. Client subscribes to `TEST/#` → creates queue `client.TEST.#`
110+
2. Message sent to `TEST.1` → queue receives it (wildcard binding works)
111+
3. Broker restart → `isAddressBound("TEST.1")` returns `false` (doesn't see wildcard binding)
112+
4. Address `TEST.1` is deleted despite having an active wildcard queue binding
113+
114+
## Test Evidence
115+
116+
From `MqttWildcardAutoDeleteAddressTest`:
117+
- **BEFORE restart**: Queue `mqtt-subscriber.TEST.#` is listed in bindings for `TEST.1` ✓
118+
- **AFTER restart**: `TEST.1` deleted, log shows `AMQ224113: Auto removing address TEST.1` ✗
119+
120+
The diagnostic output proves the wildcard queue **IS** bound before restart but the auto-delete logic **DOESN'T DETECT** it.

0 commit comments

Comments
 (0)