When a DatastoreNamespaceProvider returns dynamic/high-cardinality namespaces, GcpDatastoreAutoConfiguration creates and permanently caches one Datastore client per namespace:
ConcurrentHashMap<String, Datastore> store = new ConcurrentHashMap<>();
return () -> store.computeIfAbsent(keySupplier.get(), this::getDatastore);
The cache has no expiration, size limit, or shutdown cleanup. Each client owns GAX executors, gRPC channels, watchdog threads, and native buffers. Previously used namespaces therefore remain alive indefinitely.
Environment:
• Spring Cloud GCP: 8.0.5
• Spring Boot: 4.1.0
• Google Cloud Datastore: 3.2.0
• GAX: 2.82.0
• gRPC: 1.81.0
• Java: 25
The same cache pattern appears in earlier Spring Cloud GCP versions, but newer dependency versions make its resource cost more visible.
Minimal reproduction:
Configure a dynamic namespace provider:
@Bean
DatastoreNamespaceProvider datastoreNamespaceProvider() {
return () -> currentRequestDomainId();
}
Then perform Datastore operations using many distinct domain IDs. Every unique value creates another client that is retained for the application lifetime.
Expected behavior:
Dynamic namespace support should not cause unbounded retention of clients and their native resources. Cached clients should have a documented lifecycle and be closed when removed and during application shutdown.
Suggested approaches:
• Provide configurable expiration and/or cache limits.
• Call Datastore.close() when clients are evicted and on application shutdown.
• Expose an extension point for applications to provide a lifecycle-managed namespace client cache.
• Alternatively, share the underlying transport across namespace-specific clients.
Immediate eviction needs care because DatastoreProvider currently has no lease/release boundary, so a client could otherwise be closed while an operation is still using it.
When a
DatastoreNamespaceProviderreturns dynamic/high-cardinality namespaces,GcpDatastoreAutoConfigurationcreates and permanently caches one Datastore client per namespace:The cache has no expiration, size limit, or shutdown cleanup. Each client owns GAX executors, gRPC channels, watchdog threads, and native buffers. Previously used namespaces therefore remain alive indefinitely.
Environment:
• Spring Cloud GCP: 8.0.5
• Spring Boot: 4.1.0
• Google Cloud Datastore: 3.2.0
• GAX: 2.82.0
• gRPC: 1.81.0
• Java: 25
The same cache pattern appears in earlier Spring Cloud GCP versions, but newer dependency versions make its resource cost more visible.
Minimal reproduction:
Configure a dynamic namespace provider:
Then perform Datastore operations using many distinct domain IDs. Every unique value creates another client that is retained for the application lifetime.
Expected behavior:
Dynamic namespace support should not cause unbounded retention of clients and their native resources. Cached clients should have a documented lifecycle and be closed when removed and during application shutdown.
Suggested approaches:
• Provide configurable expiration and/or cache limits.
• Call Datastore.close() when clients are evicted and on application shutdown.
• Expose an extension point for applications to provide a lifecycle-managed namespace client cache.
• Alternatively, share the underlying transport across namespace-specific clients.
Immediate eviction needs care because DatastoreProvider currently has no lease/release boundary, so a client could otherwise be closed while an operation is still using it.