{"id":37024778,"url":"https://github.com/icefrozen/attempt","last_synced_at":"2026-01-14T03:00:25.134Z","repository":{"id":44928816,"uuid":"445034810","full_name":"IceFrozen/Attempt","owner":"IceFrozen","description":"Attempt is a lightweight component provides declarative retry support for applications,  not only but a polling strategy.  With Attempt, you can easily poll for something with retry functionality. Non-spring and lightweight applications are friendly for a fewer dependencies.","archived":false,"fork":false,"pushed_at":"2022-09-08T10:27:25.000Z","size":555,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-01T05:58:05.222Z","etag":null,"topics":["java","poll-strategies","polling","retry","retry-library","retry-strategies"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/IceFrozen.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}},"created_at":"2022-01-06T04:02:17.000Z","updated_at":"2024-06-19T06:18:23.000Z","dependencies_parsed_at":"2022-09-26T22:11:26.472Z","dependency_job_id":null,"html_url":"https://github.com/IceFrozen/Attempt","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/IceFrozen/Attempt","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IceFrozen%2FAttempt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IceFrozen%2FAttempt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IceFrozen%2FAttempt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IceFrozen%2FAttempt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/IceFrozen","download_url":"https://codeload.github.com/IceFrozen/Attempt/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IceFrozen%2FAttempt/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28408799,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T01:52:23.358Z","status":"online","status_checked_at":"2026-01-14T02:00:06.678Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["java","poll-strategies","polling","retry","retry-library","retry-strategies"],"created_at":"2026-01-14T03:00:22.079Z","updated_at":"2026-01-14T03:00:23.421Z","avatar_url":"https://github.com/IceFrozen.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n\u003cp align=\"center\"\u003e\n\t\u003ca href=\"https://icefrozen.github.io/\"\u003e\u003cimg src=\"https://github.com/IceFrozen/Attempt/blob/main/docs/logo1.png\" width=\"45%\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n\t\u003cstrong\u003eA lightweight component provides declarative retry support for applications.\u003c/strong\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n\t\u003ca href=\"https://icefrozen.github.io/\"\u003ehttps://icefrozen.github.io/\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca target=\"_blank\" href=\"https://search.maven.org/artifact/io.github.icefrozen/Attempt\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/maven-central/v/io.github.icefrozen/Attempt.svg?label=Maven%20Central\" /\u003e\n\t\u003c/a\u003e\n    \u003ca target=\"_blank\" href=\"https://www.oracle.com/technetwork/java/javase/downloads/index.html\"\u003e\n\t\t\u003cimg src=\"https://img.shields.io/badge/JDK-8+-green.svg\" /\u003e\n\t\u003c/a\u003e\n\t\u003ca target=\"_blank\" href=\"https://license.coscl.org.cn/MulanPSL2/\"\u003e\n    \t\u003cimg src=\"https://img.shields.io/:license-MIT-blue.svg\" /\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n\n\n[**中文文档**](./README_CN.md)\n\n## Introduction\n\n**Attempt** is a lightweight component provides declarative retry support for applications,  not only but a polling strategy.  With Attempt, you can easily poll for something with retry functionality. Non-spring and lightweight applications are friendly for a fewer dependencies.\n\n## Quick Start\nThis section provides a quick introduction to getting started with Attempt. It includes a static method call example, and an object call example.\n\n### maven dependency\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.github.icefrozen\u003c/groupId\u003e\n    \u003cartifactId\u003eAttempt\u003c/artifactId\u003e\n    \u003cversion\u003e0.1.1\u003c/version\u003e\n\u003c/dependency\u003e\n\n```\n\n###  An Object Call example\n\n+ First, define the base class\n```java\npublic class User {\n    private int id;\n    private String name;\n    private Integer age;\n    // the set get method and constructor are ignored\n}\n\npublic class UserService {\n    public User queryUser (int id) {\n        return new User(id, \"test\" + id, 1);\n    }\n}\n```\n\n\n+ Second, build Attempt\n\n```java\nUserService userService = new UserService();\n// Building a Retry Policy\nAttemptBuilder.Retry\u003cUserService\u003e userRetry = new AttemptBuilder.Retry\u003cUserService\u003e(userService);\n// Generate the retry proxy Object\nUserService userServiceAttempt = userRetry.build();\n// invoke the method and get the result\nUser user = userServiceAttempt.queryUser(1);\n```\nSince there are no exceptions in queryUser, this code returns immediately, no different from a direct call.\n\n+ Retry if an exception occurs.\n\nNow we throw an RuntimeException in the queryUser method and call it again.\n```java\npublic class UserService {\n    public int count = 0;\n    public User queryUser (int id) {\n        count ++;\n        throw new RuntimeException(\"queryUser error\");\n    }\n\n    public static void main(String[] args) {\n        UserService userService = new UserService();\n        AttemptBuilder.Retry\u003cUserService\u003e userRetry = new AttemptBuilder.Retry\u003cUserService\u003e(userService);\n        UserService userServiceAttempt = userRetry.build();\n        try {\n            User user = userServiceAttempt.queryUser(1);\n        } catch (RuntimeException e) {\n            System.out.println(e.getMessage());     // queryUser error\n            // original object here\n            System.out.println(userService.count);      // The original method has been called three times\n        }\n    }\n}\n```\nAs we see, when we execute the userServiceAttempt's queryUser method, the userServiceAttempt will be automatically retried three times. After that, the exception has been thrown. \n\nAttemptBuilder can make the method in a proxy object to retries when the exception has been thrown automatically? \n\nHow do we assign retry to static methods?\n\n## a static method call example\n\n```java\npublic class UserService {\n    public int count = 0;\n    public static int staticCount = 0;\n    public User queryUser (int id) {\n        count ++;\n        throw new RuntimeException(\"queryUser error\");\n    }\n\n    public static User queryUserStatic (int id) {\n        staticCount ++;\n        throw new RuntimeException(\"queryUser error\");\n    }\n\n    public static void main(String[] args) {\n        UserService userService = new UserService();\n        try {\n            AttemptBuilder.retry(() -\u003e UserService.queryUserStatic(1)).exec(); \n        } catch (RuntimeException e) {\n           // ... staticCount \u003e 3 then throw exception\n        }\n    }\n}\n\n```\n\n\n### Polling Strategy\n\nSuppose there is such a case, you upload a task, the server does not support callback or message queue to notify you whether the task is finish, then you need a polling strategy to know the status of the task.\nFor stability, you need to meet the following characteristics:\n\n+ if the process of querying the progress fails, then in order for the task to continue, it must be retried, for example, a call timeout occurs.\n+ if the call fails after retries 3 times (the maximum number of retries is tentatively set to 3 times), then report an error directly and return failure.\n+ if the network resumes trial operation at this stage, then clear the history three times and continue to poll the call.\n\n![](docs/Attempt.png)\n\nAs shown in the figure above, Attempt sets the polling strategy. If an exception occurs during the polling process, it will enter retry stage.\nIn retry stage, the number of retries will be accumulated. If retry is successful, it will continue to enter the polling phase, and the number of retries in retry phase will be cleared.\n\nexample：\n\n```java\npublic class TaskService {\n    public List\u003cInteger\u003e history = new ArrayList\u003c\u003e();\n    public Integer nowProgress = 0;             // process\n    public Integer queryProgressStep = 0;      // queryProgress invoke time\n    //  Progress step need to throw exception\n    public List\u003cInteger\u003e errorThrowOrder = new ArrayList\u003c\u003e();\n\n    public Integer queryProgress () {\n        history.add(nowProgress);\n        queryProgressStep++;\n        if(errorThrowOrder.contains(queryProgressStep)) {\n            throw new RuntimeException(\"timeout exception:\" + nowProgress);\n        }\n        SecurityThreadWaitSleeper.sleep(500);\n        nowProgress +=10;\n\n        return nowProgress;\n    }\n\n    public static void main(String[] args) {\n        Integer retryCount = 3;\n        TaskService taskService = new TaskService();\n        // 2 3 3 count will throw RuntimeException\n        taskService.errorThrowOrder = Stream.of(2, 3, 4).collect(Collectors.toList());\n        // poll builder\n        AttemptBuilder.Polling\u003cTaskService\u003e taskServicePollBuilder = new AttemptBuilder.Polling\u003c\u003e(taskService);\n        // set end point\n        TaskService taskServicePoll = taskServicePollBuilder.endPoint(context -\u003e {\n            // get last result\n            AttemptResult result = context.getLastResult();\n            if (result.isSuccess()) {\n                Integer progress = result.getRetValue(Integer.class);\n                return progress == 100;      //  progress \u003c 100 poll continue\n            }\n            return false;\n        })\n         .maxPollCount(100)      // max poll times\n         .registerExceptionRetryTime(RuntimeException.class, retryCount)   // the exception that should entry retry stage\n         .build();\n\n        try {\n            Integer integer = taskServicePoll.queryProgress();\n        }catch (RuntimeException e) {\n            System.out.println(\"queryProgressStep:\" + taskService.queryProgressStep); //\n            System.out.println(\"history:\" + taskService.history);//\n        }\n    }\n\n}\n\n```\n\n+ It can be seen that when we set (2, 3, 4) calls, an exception will be thrown, and the result will be obtained after encountering the exception RuntimeException.class, and retrying 3 times.\n    ```\n    queryProgressStep:4\n    history:[0, 10, 10, 10]\n    ```\n\nCalled 4 times, progress is in 10. That is to say, when the number of calls is 2, 3, and 4 three consecutive calls, an exception has been thrown.\n\n+ When we set an exception RuntimeException.class, retry 4 times. But we said that the exception will be thrown when 2, 3, and 4 are called three times in a row, so the call returns 100 successfully.\n    ```java\n    // ......\n    Integer retryCount = 4;\n    // ......\n    ```\n+ When we set the exception sequence to 2, 3, 5, 6, 7, since 23 is not continuous, it will continue to be called after retrying in stage 23, and it will not end until it encounters 567 three consecutive exceptions.\n    ```java\n      Integer retryCount = 3;\n      taskService.errorThrowOrder = Stream.of(2, 3, 5,6,7).collect(Collectors.toList());\n    ```\n    返回结果\n    ```\n    queryProgressStep:7\n    queryProgressStep:[0, 10, 10, 10, 20, 20, 20]\n    ```\n\n\n## Performance VS SpringRetry\n\ncode [here](src/test/java/io/github/icefrozen/jmh/AttemptVsSpringRetry.java)\n\nBenchmark         |                                  Mode  |Cnt      |Score      |Error  |Units\n---|---|---|---|---|---\nAttemptVsSpringRetry.testAttempt   |   avgt  | 10   |  165.921 ± |    26.558  |ns/op\nAttemptVsSpringRetry.testGuavaRetry  | avgt |  10 | 909259.747 ± |323278.426 | ns/op\nAttemptVsSpringRetry.testSpringRetry  |avgt|   10 |  50681.819 ±  | 2848.606  |ns/op\n\n![](docs/JMH.png)\n\n\n## Author\n\n![](./docs/gongzhonghao.png)\n\n## Advanced\n\nmore:[Wiki](docs/README_WIKI_EN.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ficefrozen%2Fattempt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ficefrozen%2Fattempt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ficefrozen%2Fattempt/lists"}