- Extract the tenant-profile fetch lock stripe count to a named constant (PR #15744)
- Trim the stale global-lock sentence from the per-tenant lock comment (PR #15744)
- Rename the rate-limit onCreate callback to onMiss and document its idempotency requirement (PR #15744)
- Reuse a single tenant profile local in update(TenantProfileUpdateResult) (PR #15744)
- Make the transport callback thread pool size configurable via transport.callback_thread_pool_size (PR #15744)
- Add a parameterized test locking the TransportLimitsType enum-to-profile-field mapping (PR #15744)
- Add device/gateway rate-limit coverage asserting update(tenantId) reaches tracked entities (PR #15744)
- Add a same-tenant fetch-dedup test and pin the cross-tenant test to distinct stripes (PR #15744)
The defective code lives in common/transport/transport-api and is shared by all
transports (MQTT, HTTP, CoAP, LwM2M, SNMP); the production incident happened to
surface on MQTT.
On a cold tenant-profile cache (e.g. after a cache clear + restart), a device
reconnect storm could serialize the whole transport instance behind tenant-profile
resolution, saturating the callback pool and stalling the node for ~15 minutes.
Two compounding causes are addressed:
- DefaultTransportTenantProfileCache held a single process-wide ReentrantLock across
the synchronous cross-service getEntityProfile round-trip, so every tenant-profile
cache miss in the whole process was serialized one-at-a-time. Replace it with a
bounded set of per-tenant locks (Guava Striped) so different tenants resolve
concurrently while concurrent misses for the same tenant are still de-duplicated.
- DefaultTransportRateLimitService performed that blocking fetch inside
ConcurrentHashMap.computeIfAbsent's mapping function, holding a CHM bin lock across
the remote round-trip. Pre-fetch the tenant profile before computeIfAbsent so no bin
lock is held across I/O.
Also de-duplicate the four near-identical getXRateLimits methods into one generic
helper, move the per-type rate-limit getters onto the TransportLimitsType enum, and
avoid fetching the tenant profile four times in update(TenantId).
Rename `openConnections`/`connectionsCounter` to `statsName`/`number`
to match DefaultTransportService and avoid misleading future readers.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Rename TagsKey → tagsKey and make it private (Java naming convention)
- Make isSSL() private in MqttTransportHandler (internal use only)
- Fix double space in if (isSSL) in MqttTransportContext
- Extract getClientAddr() helper and move clientAddr computation inside
logging guards so address resolution is skipped when logging is disabled
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>