Dubbo @Reference retries=0 Configuration Bug in Version 2.6.2
A production incident revealed duplicate data creation: a single save operation resulted in three database records. Investigation traced the issue to inconsistent retry behavior between Dubbo’s XML and annotation-based configuration for retries = 0.
In XML configuration, the following disables retries as expected:
<dubbo:reference id="testService" interface="heiidea.trade.service.sdk.interfice.TestService" retries="0"/>
However, using the @Reference annotation with identical semantics fails:
@Reference(retries = 0)
private TestService testService;
Debugging through the invocation chain — from InvokerInvocationHandler.invoke() → MockClusterInvoker.invoke() → AbstractClusterInvoker.invoke() → FailoverClusterInvoker.doInvoke() — revealed that retries resolved to null when set via @Reference, causing fallback to the default value of 2. Combined with the initial call, this produced three total invocations.
In contrast, the XML parser correct parsed retries="0" as integer 0, resulting in exactly one invocation (no retries).
Root Cause Analysis
The discrepancy stems from how attribute values are adapted during bean construction:
-
Annotation path:
ReferenceAnnotationBeanPostProcessorprocesses@Referencefields. DuringbuildReferenceBean(), the rawretries = 0is preserved initially. However, later inpreConfigureBean(),AnnotationPropertyValuesAdapterinvokesAnnotationUtils.getAttributes(), which internally callsnullSafeEquals(attributeValue, defaultValue). Since the defaultretriesvalue is2, and0 == 2evaulates tofalse, this should retain the attribute. But in Dubbo 2.6.2, the logic incorrectly treats0as equivalent to the default when comparing againstnullor unconfigured state — ultimately omittingretriesfrom the finalactualAttributesmap. Consequently,getRetries()returnsnull, triggering fallback to2. -
XML path: The
DubboNamespaceHandlerparses<dubbo:reference>by reflecting overReferenceBean’s setter methods (e.g.,setRetries(int)). It directly assigns the parsed integer0without equality-based filtering, preserving the explicit zero value.
Behavioral Summary
| Configuration Style | retries not specified |
retries = 0 |
retries = -1 |
|---|---|---|---|
XML (<dubbo:reference>) |
2 retries (3 total calls) | 0 retries (1 total call) | 0 retries |
Annotation (@Reference) |
2 retries | 2 retries (due to null fallback) |
0 retries |
To guarantee zero retries with annotations in affected versions, use retries = -1. For idempotent operations like writes, this avoids unintended side effects. Upgrading to Dubbo ≥2.7.3 resolves the isue: the annotation processor now consistently treats 0 as an explicit override rather than discarding it during attribute adaptation.