jsr107 / jsr107spec

JSR107 Cache Specification
Apache License 2.0
415 stars 164 forks source link

Provide an API to retrieve statistics #423

Open chrisknoll opened 3 months ago

chrisknoll commented 3 months ago

It appears that the JCache API does provide some mechanism to specify that statistics could be enabled:

    @Component
    public static class CachingSetup implements JCacheManagerCustomizer {

        @Override
        public void customize(CacheManager cacheManager) {
            cacheManager.createCache("person", new MutableConfiguration<Integer, PersonDTO>()
                            .setTypes(Integer.class, PersonDTO.class)
                            .setStoreByValue(false)
                            .setStatisticsEnabled(true));
            cacheManager.createCache("personList", new MutableConfiguration<Object, List<PersonDTO>>()
                            .setTypes(Object.class, (Class<List<PersonDTO>>)(Class<?>)List.class)
                            .setStoreByValue(false)
                            .setStatisticsEnabled(true));

        }
    }

But there's no API to retrieve these statistics. It seems that there is a CacheStatisticsMXBean that is used to access these statistics, but it's quite confusing and cumbersome to retrieve:

    private CacheStatisticsMXBean getCacheStats(CacheManager cacheManager, String cacheName) throws Exception {
        final MBeanServer beanServer = ManagementFactory.getPlatformMBeanServer();

        final Set<ObjectInstance> cacheBeans = beanServer.queryMBeans(
                        ObjectName.getInstance("javax.cache:type=CacheStatistics,CacheManager=*,Cache=*"),
                        null);
        // the MXBean's CacheManager seems to leverage the URI of the cache manager, but : is replaced with .
        // which I am not sure can be depended on in a cross-provider perspective
        String cacheManagerName = cacheManager.getURI().toString().replace(":", ".");
        ObjectInstance cacheBean = cacheBeans.stream()
                        .filter(b -> 
                                b.getObjectName().getKeyProperty("CacheManager").equals(cacheManagerName)
                                && b.getObjectName().getKeyProperty("Cache").equals(cacheName)
                        ).findFirst().orElseThrow(() -> new IllegalAnnotationException(String.format("No cache found for cache manager = %s, cache = %s", cacheManagerName, cacheName)));
        final CacheStatisticsMXBean cacheStatisticsMXBean = MBeanServerInvocationHandler.newProxyInstance(beanServer, cacheBean.getObjectName(), CacheStatisticsMXBean.class, false);
        return cacheStatisticsMXBean;
    }

So, many potential pitfalls about what we need to do here:

  1. The name of the cache manager isn't exactly a URI because ObjectNames can not have : in them, but URIs do.
  2. The name that is registered as a MXBean may be provider dependant and therefore difficult to mae provider-agnostic. Would be nice if you could name a CacheProvider that mapps directly to the queryMBean() call.
  3. ManagementFactory.getPlatformMBeanServer() actually lets you query for beans that are produced system-wide, not application specific, leading to potential conflicts if you have 2 applications running that share cache names.

So, the ask here is if a minor update to the JCache (JSR107) API could provide an accessor to fetch a CacheStatisticsMXBean from a cache instance so that all these error-prone hurdles can be avoided, and you ask the cache directly for its statistics.