{"id":22152136,"url":"https://github.com/findinpath/spring-data-cassandra-repository-methods-timing","last_synced_at":"2026-04-28T21:01:27.724Z","repository":{"id":112791037,"uuid":"222011093","full_name":"findinpath/spring-data-cassandra-repository-methods-timing","owner":"findinpath","description":"Proof of concept on timing spring data cassandra repository methods","archived":false,"fork":false,"pushed_at":"2019-11-15T22:08:07.000Z","size":15,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-24T13:27:21.421Z","etag":null,"topics":["cassandra","micrometer","monitoring","testcontainers"],"latest_commit_sha":null,"homepage":"https://www.findinpath.com/spring-data-cassandra-repository-methods-timing/","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/findinpath.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-11-15T22:07:37.000Z","updated_at":"2019-11-15T22:45:02.000Z","dependencies_parsed_at":null,"dependency_job_id":"daba8d52-0db9-4af0-96dd-8502a4f14ca8","html_url":"https://github.com/findinpath/spring-data-cassandra-repository-methods-timing","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/findinpath/spring-data-cassandra-repository-methods-timing","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/findinpath%2Fspring-data-cassandra-repository-methods-timing","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/findinpath%2Fspring-data-cassandra-repository-methods-timing/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/findinpath%2Fspring-data-cassandra-repository-methods-timing/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/findinpath%2Fspring-data-cassandra-repository-methods-timing/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/findinpath","download_url":"https://codeload.github.com/findinpath/spring-data-cassandra-repository-methods-timing/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/findinpath%2Fspring-data-cassandra-repository-methods-timing/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32399010,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-28T19:38:08.556Z","status":"ssl_error","status_checked_at":"2026-04-28T19:37:55.688Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cassandra","micrometer","monitoring","testcontainers"],"created_at":"2024-12-02T00:47:22.292Z","updated_at":"2026-04-28T21:01:27.692Z","avatar_url":"https://github.com/findinpath.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"Time the duration of methods belonging to spring data cassandra repositories\n============================================================================\n\nThis project showcases how to make use of Spring AOP / AspectJ for timing the\nduration of methods belonging to \n[spring data cassandra](https://spring.io/projects/spring-data-cassandra) repositories.\n\n## Metrics in the data access layer of the application\n\nHaving an overview on the [Prometheus](https://prometheus.io/) for how each of the operations\nexposed by the _Data Access Layer_ performs is a tremendous improvement\nfor monitoring and tackling production issues on a live application.\n\nTo give a pretty common example, say that with a new release of the application \none `SELECT` statement from a repository class gets changed that causes the\napplication to run normal on the testing environment, but on the productive environment\nwhere there's much more data to perform much worse than it did before.\nIn that case after the release, the [Quality of Service](https://en.wikipedia.org/wiki/Quality_of_service)\nwill drop to a significant amount, and the engineer responsible for the release\nwill need to investigate what has changed for the worse since the last release.\n\nThis is where nowadays metric collector services (like [Prometheus](https://prometheus.io/))\nshine and provide a lot of meaningful answers in such situations.\nIt would obviously be very helpful to see in [Grafana](https://grafana.com/) \nor even receive a [Prometheus alert](https://prometheus.io/docs/practices/alerting/)\nwhether an operation or a set of operations performed by the application performs\nmuch more slowly than before.\n\nSeeing a continuous spike or repeated failures on the data access layer for a\ncertain operation from a repository in the data access layer of the application\nsince the last release would help to identify very fast \nthe problem and subsequently revert the change or fix the problem.\n\n\n## Timing the methods of spring data repositories\n\nWhen using the `io.micrometer.core.annotation.Timed` annotation that comes \nwith [micrometer](https://micrometer.io/) library in a \n[spring-boot](https://spring.io/projects/spring-boot) application\nthere is obtained the emission of timing metrics for the methods annotated\nwith this annotation.\n\nWhen annotating the class with the `@Timed` annotation, all the public methods \nof the spring bean class will be timed. \n\nThis seems like a good solution, but very often it happens that through \nomission/refactoring in the code, the annotation is being removed by mistake\nfrom the class and therefore the valuable metrics and not emitted anymore\nto the micrometer's `MeterRegistry`.\n \nAnother drawback of the `io.micrometer.core.annotation.Timed` annotation\n(and the `io.micrometer.core.aop.TimedAspect` which takes care of intercepting\nthe methods annotated with `Timed` annotation) is that it doesn't have support\nfor dealing with asynchronous methods.\n\nThrough [spring data cassandra](https://spring.io/projects/spring-data-cassandra) utility classes:\n\n- `org.springframework.data.cassandra.core.CassandraTemplate`\n- `org.springframework.data.cassandra.core.AsyncCassandraTemplate`\n\nthere can be executed statements on the Cassandra database in both fashions, \nsynchronous and asynchronous.\n\nThis project developed a custom [AspectJ](https://en.wikipedia.org/wiki/AspectJ) class\nfor taking care of timing all the public methods exposed by the spring data repository bean\nclasses from the project.\n\nSpring data repository classes are considered any of the following:\n\n- the class is annotated with the `@org.springframework.stereotype.Repository` annotation\n- the class implements `org.springframework.data.repository.Repository` class\n  \n  \nIn a similar fashion to the  micrometer's AspectJ `io.micrometer.core.aop.TimedAspect`, the\nAspectJ `com.findinpath.aop.RepositoryTimerAspect` class from this proof of concept project intercepts\nthe methods of spring data repository beans from the project and emits timer information\nto micrometer's `io.micrometer.core.instrument.MeterRegistry`.\n\nThe information get emitted to the micrometer metric named `repository` with the following tags:\n\n```java\n.tags(\"class\", className) \n.tags(\"method\", methodName)\n.tags(\"successful\", successful) \n```\n\nThe `className` can be on of the following:\n\n- the interface corresponding to the repository - \nsee `com.findinpath.repository.ConfigRepository`\n- the concrete class annotated with `@org.springframework.stereotype.Repository` \nannotation - see `com.findinpath.repository.UserBookmarkRepository`\n\n\nThe `successful` flag states whether the call was or not successfully executed.\n\n\n### Dealing with asynchronous methods\n\nAs mentioned earlier, there can be executed asynchronous methods on Cassandra.\nThe implementation of the asynchronous methods with spring data cassandra\nis straightforward (see `com.findinpath.repository.UserBookmarkRepository` for more details):\n\n```java\n  public ListenableFuture\u003cUserBookmark\u003e saveAsync(UserBookmark userBookmark) {\n    return asyncCassandraOperations.insert(userBookmark);\n  }\n```\n\nA little trick needs therefor to be employed in the `AspectJ` `RepositoryTimerAspect`class\nfor timing such non-blocking methods. After retrieving the result from the target method\ninvocation, a callback is being registered for emiting the metrics in both success and\nfailure cases after the completion of the method:\n\n```java\n    var asyncResult = (ListenableFuture) proceedingJoinPoint.proceed();\n    asyncResult.addCallback(\n        result -\u003e emitMetrics(meterRegistry, sample, className, methodName, Optional.empty()),\n        ex -\u003e emitMetrics(meterRegistry, sample, className, methodName, Optional.of(ex)));\n    return asyncResult;\n```\n\n## Spring AOP\n\nIn case that it is needed for reference a Spring AOP implementation \nfor timing the spring data repositories, \nthe project source code provides also such an implementation, although commented,\nin order to avoid causing issues with the `AspectJ` `RepositoryTimerAspect.\n\nSee `com.findinpath.config.RepositoryTimerConfiguration` for details.\n\n## Demo\n\nThe project comes with a `com.findinpath.DemoTest` class which contains test scenarios\nfor timing both kinds of spring data repositories:\n\n- the class is annotated with the `@org.springframework.stereotype.Repository` annotation\n- the class implements `org.springframework.data.repository.Repository` class\n\nand also both synchronous and asynchronous methods.\n\nBelow are the measurements logged from one of the runs of the `DemoTest`:\n\n```\nThe timer MeterId{name='repository', tags=[tag(class=ConfigRepository),tag(method=save),tag(successful=true)]} has max value: 176 ms and mean value: 25 ms\nThe timer MeterId{name='repository', tags=[tag(class=ConfigRepository),tag(method=findById),tag(successful=true)]} has max value: 22 ms and mean value: 8 ms\n```\n\n```\nThe timer MeterId{name='repository', tags=[tag(class=UserBookmarkRepository),tag(method=save),tag(successful=true)]} has max value: 32 ms and mean value: 9 ms\nThe timer MeterId{name='repository', tags=[tag(class=UserBookmarkRepository),tag(method=saveAsync),tag(successful=true)]} has max value: 33 ms and mean value: 8 ms\nThe timer MeterId{name='repository', tags=[tag(class=UserBookmarkRepository),tag(method=findLatestBookmarks),tag(successful=true)]} has max value: 12 ms and mean value: 7 ms\nThe timer MeterId{name='repository', tags=[tag(class=UserBookmarkRepository),tag(method=findLatestBookmarksAsync),tag(successful=true)]} has max value: 25 ms and mean value: 12 ms\n\n``` \n\n\nThe test is being executed against a throwaway Cassandra database container (through the \nhelp of the genius team formed from [docker](https://www.docker.com/) and the\n [testcontainers](https://www.testcontainers.org/) library).\n \nIn order to keep the demo project simple to go through, no setup has been \nmade for a web application that would expose operations to interact with Cassandra\ndatabase. So for testing the application, only the  `com.findinpath.DemoTest` class\nis provided.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffindinpath%2Fspring-data-cassandra-repository-methods-timing","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffindinpath%2Fspring-data-cassandra-repository-methods-timing","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffindinpath%2Fspring-data-cassandra-repository-methods-timing/lists"}