Uploaded image for project: 'Spring Data for Apache Cassandra'
  1. Spring Data for Apache Cassandra
  2. DATACASS-675

Add constructor to CassandraAdminTemplate accepting CqlOperations

    XMLWordPrintable

    Details

    • Type: Improvement
    • Status: Closed
    • Priority: Minor
    • Resolution: Complete
    • Affects Version/s: 2.1.9 (Lovelace SR9)
    • Fix Version/s: 2.2 RC2 (Moore)
    • Component/s: Core
    • Labels:
      None
    • Last commented by a User:
      true
    • Sprint:
      Moore RC2

      Description

      Use Case:
      I'm trying to capture Cassandra client-side query latency metrics using Micrometer. Cassandra Java driver exposes some metrics through Dropwizard, but there's no way to bridge Dropwizard -> Micrometer for general meters like timers and histograms (there exists a Dropwizard MetricRegistryListener, but the methods in it are invoked after a meter has been added to the registry, thus eliminating any chance of interception - filedĀ https://github.com/dropwizard/dropwizard/issues/2840).

      Implementation Approach:
      The idea is to time all calls to CqlOperations. This is, however, easier said than done due to the following problems in the code.

      1. For some unknown reason, AbstractCassandraConfiguration.cassandraTemplate returns a concrete class CassandraAdminTemplate, not the interface type CassandraAdminOperations. This makes it very difficult to proxy the returned object.
      2. CassandraAdminTemplate does not implement a constructor that allows invocation of the superclass constructor CassandraTemplate(CqlOperations cqlOperations, CassandraConverter converter). Had this been possible, simply passing in a proxied CqlOperations would have allowed for a clean way of capturing the metrics.

      I'm currently forced to resort to ugly reflections hacks.

      public CassandraAdminTemplate cassandraTemplate() throws Exception {
          CassandraAdminTemplate cassandraAdminTemplate = super.cassandraTemplate();
      
          if (getMetricsEnabled()) {
              Timer latencyTimer = meterRegistry.timer("cassandra.requests");
              ReflectionUtils.doWithFields(cassandraAdminTemplate.getClass(), cqlOperations -> {
                          cqlOperations.setAccessible(true);
                          Field modifiersField = ReflectionUtils.findField(Field.class, "modifiers");
                          modifiersField.setAccessible(true);
                          modifiersField.setInt(cqlOperations, cqlOperations.getModifiers() & ~Modifier.FINAL);
                          ProxyFactory proxyFactory = new ProxyFactory(cqlOperations.get(cassandraAdminTemplate));
                          proxyFactory.addAdvice((MethodInterceptor) invocation -> latencyTimer.recordCallable(() -> {
                              try {
                                  return invocation.proceed();
                              } catch (Throwable t) {
                                  throw new RuntimeException(t);
                              }
                          }));
                          cqlOperations.set(cassandraAdminTemplate, proxyFactory.getProxy());
                      }, field -> field.getName().equals("cqlOperations")
              );
          }
          return cassandraAdminTemplate;
      }
      

        Attachments

          Activity

            People

            Assignee:
            mp911de Mark Paluch
            Reporter:
            asarkar Abhijit Sarkar
            Last updater:
            Mark Paluch
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Dates

              Created:
              Updated:
              Resolved:
              Days since last comment:
              1 year, 11 weeks ago