{"id":18752998,"url":"https://github.com/spotify/futuristic-feline","last_synced_at":"2025-09-06T18:39:50.606Z","repository":{"id":40505198,"uuid":"348112836","full_name":"spotify/futuristic-feline","owner":"spotify","description":"futuristic-feline is a library for detecting blocking Java futures at runtime","archived":false,"fork":false,"pushed_at":"2024-12-23T18:08:05.000Z","size":685,"stargazers_count":12,"open_issues_count":5,"forks_count":10,"subscribers_count":15,"default_branch":"main","last_synced_at":"2025-06-26T02:45:57.152Z","etag":null,"topics":["futures","java"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/spotify.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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,"zenodo":null}},"created_at":"2021-03-15T20:26:40.000Z","updated_at":"2025-02-16T17:09:25.000Z","dependencies_parsed_at":"2024-04-29T19:39:53.980Z","dependency_job_id":"ef6b65a8-426f-445b-9359-7916c5482d03","html_url":"https://github.com/spotify/futuristic-feline","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/spotify/futuristic-feline","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spotify%2Ffuturistic-feline","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spotify%2Ffuturistic-feline/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spotify%2Ffuturistic-feline/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spotify%2Ffuturistic-feline/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/spotify","download_url":"https://codeload.github.com/spotify/futuristic-feline/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spotify%2Ffuturistic-feline/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262820525,"owners_count":23369649,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["futures","java"],"created_at":"2024-11-07T17:23:40.442Z","updated_at":"2025-06-30T17:39:44.477Z","avatar_url":"https://github.com/spotify.png","language":"Java","funding_links":[],"categories":["并发编程"],"sub_categories":[],"readme":"# futuristic-feline\n\n![Build status](https://github.com/spotify/futuristic-feline/actions/workflows/maven.yml/badge.svg)\n[![license](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://github.com/spotify/futuristic-feline/blob/main/LICENSE)\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.spotify/futuristic-feline/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.spotify/futuristic-feline)\n\n![](images/future-is-ours.jpg)\n\nfuturistic-feline is a library for detecting blocking Java futures at runtime. It is inspired by the excellent [BlockHound](https://github.com/reactor/BlockHound).\n\nWriting asynchronous code is hard and blocking futures can lead to thread pool starvation. This has been a common source of incidents at Spotify and we monitor our services with this library to detect and fix services at risk.\n\n## What does it do?\n\nfuturistic-feline detects when `Future.get()`, `CompletableFuture.get`, or `CompletableFuture.join` is called in a blocking fashion. That is, if they are called before being completed. futuristic-feline does this by injecting a check into these methods using byte code manipulation, and provides a callback function where you can act on any blocking calls, for example by collecting metrics or failing.\n\nFor example, the following will print to stdout on any blocking calls:\n\n```java\nFeline.addConsumerLast(System.out::println);\n```\n\nor throw an exception:\n\n```java\nFeline.addConsumerLast(call -\u003e {throw new RuntimeException(call);});\n```\n\nTo get started, add a dependency on:\n\n```xml\n    \u003cdependency\u003e\n      \u003cgroupId\u003ecom.spotify\u003c/groupId\u003e\n      \u003cartifactId\u003efuturistic-feline\u003c/artifactId\u003e\n      \u003cversion\u003e$VERSION\u003c/version\u003e\n    \u003c/dependency\u003e\n```\n\n## Metrics integration\n\nIt is often useful to create a metric for the rate of blocking calls. At Spotify we have built this into our service framework using [semantic-metrics](https://github.com/spotify/semantic-metrics).\n\nIf you use a different metrics collection framework, it should be straight-forward to integrate futuristic-feline. We're happy to accept contributions for other framework integrations.\n\n### semantic-metrics integration\n\nAdd a dependency on:\n\n```xml\n    \u003cdependency\u003e\n      \u003cgroupId\u003ecom.spotify\u003c/groupId\u003e\n      \u003cartifactId\u003efuturistic-feline-semantic-metrics\u003c/artifactId\u003e\n      \u003cversion\u003e$VERSION\u003c/version\u003e\n    \u003c/dependency\u003e\n```\n\nThen install the `FelineMetricsRecorder`:\n\n```java\nFelineMetricsRecorder.install(new SemanticMetricRegistry());\n```\n\nThis will create a meter tagged with `what: blocking-calls` and `call`\nreferring to the class and method name that called the blocking\n`Future` or `CompletableFuture` method.\nThere is also a tag with `thread_name` referring to the thread that called\nthe blocking method. To prevent a metrics cardinality explosion, this\nname is sanitized by replacing all integers with the character `N`.\n\nSimilarly, there's a meter to measure total time blocked:\n`what: blocking-calls-time` measured in nano-seconds (also tagged with `unit: ns`) \nwith the same tags as above (`call` and `thread_name`).\n\nYou can customize how the caller is identified by\ninjecting a custom `CallFinder` to the `FelineMetricsRecorder` - take a look at the\ncode in `com.spotify.feline.FelineMetricsRecorder` for more detail.\n\n## JUnit integration\n\nFeline can also be used to detect blocking futures in your tests. We provide integration with Junit 4 and 5.\n\n### JUnit 4\n\nWhen using JUnit 4, you can add automatic detection of blocking calls to all tests by adding a dependency on:\n\n```xml\n    \u003cdependency\u003e\n      \u003cgroupId\u003ecom.spotify\u003c/groupId\u003e\n      \u003cartifactId\u003efuturistic-feline-junit4\u003c/artifactId\u003e\n      \u003cversion\u003e$VERSION\u003c/version\u003e\n      \u003cscope\u003etest\u003c/scope\u003e\n    \u003c/dependency\u003e\n```\n\nAnd registering `FelineRunListener` with your JUnit runs. One way of doing this is with the Maven Surefire plugin:\n\n```xml\n      \u003cplugin\u003e\n        \u003cartifactId\u003emaven-surefire-plugin\u003c/artifactId\u003e\n        \u003cversion\u003e2.22.2\u003c/version\u003e\n        \u003cconfiguration\u003e\n          \u003cproperties\u003e\n            \u003cproperty\u003e\n              \u003cname\u003elistener\u003c/name\u003e\n              \u003cvalue\u003ecom.spotify.feline.FelineRunListener\u003c/value\u003e\n            \u003c/property\u003e\n          \u003c/properties\u003e\n        \u003c/configuration\u003e\n      \u003c/plugin\u003e\n```\n\nThis will inject a `RunListener` that will fail tests on any blocking calls.\n\n### JUnit 5\n\nWhen using JUnit 5 (not vintage), you can add automatic detection of blocking calls to all tests simply by adding a dependency on:\n\n```xml\n    \u003cdependency\u003e\n      \u003cgroupId\u003ecom.spotify\u003c/groupId\u003e\n      \u003cartifactId\u003efuturistic-feline-junit5\u003c/artifactId\u003e\n      \u003cversion\u003e$VERSION\u003c/version\u003e\n      \u003cscope\u003etest\u003c/scope\u003e\n    \u003c/dependency\u003e\n```\n\nThis will inject a `TestExecutionListener` that will fail tests on any blocking calls.\n\n### Ignoring blocking calls in tests\n\nIt's sometimes convenient to have blocking calls within your test methods themselves. In this case, you can ignore blocking calls by annotating your method:\n\n```java\n  @Test\n  @IgnoreBlocking\n  public void ignoreBlockingJoin() {\n    final CompletableFuture\u003cVoid\u003e future = CompletableFuture.runAsync(RUNNABLE);\n\n    // blocking, but works since we're ignoring blocking calls\n    future.join();\n  }\n```\n\nNote that this will still fail as `@IgnoreBlocking` only applies to the annotated method:\n\n```\n  @Test\n  @IgnoreBlocking\n  public void indirectIgnoreBlockingJoin() {\n    blockingCall();\n  }\n\n  private void blockingCall() {\n    final CompletableFuture\u003cVoid\u003e future = CompletableFuture.runAsync(RUNNABLE);\n\n    future.join();\n  }\n```\n\n\n## Development notes\n\n### Test failures when IntelliJ's debugger is attached\n\nIf you are working on futuristic-feline and have a need to attach IntelliJ's debugger to a test, you might find that all tests that depend on applying FelineTransformer are failing for mysterious reasons.\n\nIt seems that in some versions of IntelliJ, the debugger will attach an agent of its own to the Java process that it launches to run tests, which interferes with how Feline sets up its own agent.\n\nThis can be easily addressed by opening IntelliJ's settings and under Build, Execution, Deployment \u003e Debugger \u003e Async Stack Traces, uncheck the box for \"Instrumenting Agent\":\n\n![Screenshot of IntelliJ settings to disable](images/intellij-settings.png)\n\nReferences:\n- https://intellij-support.jetbrains.com/hc/en-us/community/posts/360000166640-Running-Debug-in-Java-can-t-find-CaptureAgent\n- https://www.jetbrains.com/help/idea/async-stacktraces.html\n\n\n## Code of Conduct\n\nThis project adheres to the [Open Code of Conduct][code-of-conduct]. By participating, you are expected to honor this code.\n\n[code-of-conduct]: https://github.com/spotify/code-of-conduct/blob/master/code-of-conduct.md\n\n## Ownership\n\nThe Weaver squad is currently owning this project internally.\nWe are currently in the evaluating process of the ownership of this and other OSS Java libraries.\nThe ownership takes into account **ONLY** security maintenance.\n\nThis repo is also co-owned by other people:\n\n* [mattnworb](https://github.com/mattnworb)\n* [spkrka](https://github.com/spkrka)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspotify%2Ffuturistic-feline","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fspotify%2Ffuturistic-feline","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspotify%2Ffuturistic-feline/lists"}