- 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)
Construct CoapServer and the LwM2M bootstrap server inside the init try block so a failure in the constructor or build() is cleaned up by the existing catch. Guard CoAP shutdown() against a null server. Add a DTLS-enabled CoAP test that covers the dtlsSessionsExecutor shutdown branch.
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).
Moved NioEventLoopGroup allocations into the try block so that a
constructor failure for the second group no longer leaks the first.
Channel close failures during cleanup now attach via addSuppressed
instead of replacing the original BindException. Narrowed the outer
catch from Throwable to Exception, removing the brittle (Error) cast
that would have masked any direct Throwable subclass.
Routes get/set/del/scan/getSet/hGet/hSet/hDel/lRange/zRem/getRange calls
through stringCommands()/keyCommands()/hashCommands()/listCommands()/
zSetCommands() instead of the deprecated shortcut methods on
DefaultedRedisConnection. Behavior-preserving — Spring Data Redis
delegates the deprecated overloads to these same typed commands.
Eliminates 70 deprecation warnings across RedisOtaPackageDataCache, five
Leshan-forked lwm2m stores (TbLwM2mRedisRegistrationStore,
TbLwM2mRedisSecurityStore, TbRedisLwM2MClientStore,
TbRedisLwM2MModelConfigStore, TbLwM2mRedisClientOtaInfoStore,
TbLwM2MDtlsSessionRedisStore) and the TbRedisLwM2MClientStoreTest unit
test, which now uses Answers.RETURNS_DEEP_STUBS to mock the typed
command chain.
Part of #15481 Tier 3 cleanup.