{"id":26594268,"url":"https://github.com/q3769/conottle","last_synced_at":"2025-03-23T15:52:23.691Z","repository":{"id":63860525,"uuid":"571068890","full_name":"q3769/conottle","owner":"q3769","description":"A Java concurrent API to throttle the maximum concurrency to process tasks for any given client while the total number of clients being serviced in parallel can also be throttled","archived":false,"fork":false,"pushed_at":"2024-05-01T05:31:36.000Z","size":217,"stargazers_count":2,"open_issues_count":5,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-05-07T18:05:54.425Z","etag":null,"topics":["concurrency-framework","concurrency-limiter","concurrency-throttle","java","java-concurrency-library","java-concurrency-management","java-concurrency-throttle","java-concurrent-api","java-multithreading","java-thread-management","middleware","middleware-framework","multithreading-framework","multithreading-library","throttle-requests"],"latest_commit_sha":null,"homepage":"https://q3769.github.io/conottle/","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/q3769.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","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},"funding":{"github":"q3769","patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":null}},"created_at":"2022-11-27T03:40:50.000Z","updated_at":"2023-03-29T18:34:00.000Z","dependencies_parsed_at":"2024-04-17T06:27:01.237Z","dependency_job_id":"e856f94c-e05e-4df8-a613-cd0c528d76b5","html_url":"https://github.com/q3769/conottle","commit_stats":{"total_commits":158,"total_committers":3,"mean_commits":"52.666666666666664","dds":0.069620253164557,"last_synced_commit":"ab49af1a0751b17a5833716f02fc78d7b91f4c03"},"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/q3769%2Fconottle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/q3769%2Fconottle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/q3769%2Fconottle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/q3769%2Fconottle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/q3769","download_url":"https://codeload.github.com/q3769/conottle/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245127937,"owners_count":20565203,"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":["concurrency-framework","concurrency-limiter","concurrency-throttle","java","java-concurrency-library","java-concurrency-management","java-concurrency-throttle","java-concurrent-api","java-multithreading","java-thread-management","middleware","middleware-framework","multithreading-framework","multithreading-library","throttle-requests"],"created_at":"2025-03-23T15:52:23.118Z","updated_at":"2025-03-23T15:52:23.675Z","avatar_url":"https://github.com/q3769.png","language":"Java","readme":"[![Maven Central](https://img.shields.io/maven-central/v/io.github.q3769/conottle.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22io.github.q3769%22%20AND%20a:%22conottle%22)\n\n# conottle\n\nA Java concurrent API to throttle the maximum concurrency to process tasks for any given client while the total number\nof clients being serviced in parallel can also be throttled\n\n- **conottle** is short for **con**currency thr**ottle**.\n\n## User story\n\nAs an API user, I want to execute tasks for any given client with a configurable maximum concurrency while the total\nnumber of clients being serviced in parallel can also be limited.\n\n## Prerequisite\n\nJava 8 or better\n\n## Get it...\n\n[![Maven Central](https://img.shields.io/maven-central/v/io.github.q3769/conottle.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22io.github.q3769%22%20AND%20a:%22conottle%22)\n\nInstall as a compile-scope dependency in Maven or other build tools alike.\n\n## Use it...\n\n### API\n\n```java\npublic interface ClientTaskExecutor {\n    /**\n     * @param command {@link Runnable} command to run asynchronously. All such commands under the same {@code clientId}\n     *     are run in parallel, albeit throttled at a maximum concurrency.\n     * @param clientId A key representing a client whose tasks are throttled while running in parallel\n     * @return {@link Future} holding the run status of the {@code command}\n     */\n    default Future\u003cVoid\u003e execute(Runnable command, Object clientId) {\n        return submit(Executors.callable(command, null), clientId);\n    }\n\n    /**\n     * @param task {@link Callable} task to run asynchronously. All such tasks under the same {@code clientId} are run\n     *     in parallel, albeit throttled at a maximum concurrency.\n     * @param clientId A key representing a client whose tasks are throttled while running in parallel\n     * @param \u003cV\u003e Type of the task result\n     * @return {@link Future} representing the result of the {@code task}\n     */\n    \u003cV\u003e Future\u003cV\u003e submit(Callable\u003cV\u003e task, Object clientId);\n}\n\n```\n\nThe interface uses `Future` as the return type, mainly to reduce conceptual weight of the API. The implementation\nactually returns `CompletableFuture`, and can be used/cast as such if need be.\n\n### Sample usage\n\n```java\nimport java.util.concurrent.Executors;\n\nclass submit {\n    Conottle conottle = Conottle.builder()\n            .maxClientsInParallel(100)\n            .maxParallelismPerClient(4)\n            .workerExecutorService(Executors.newCachedThreadPool())\n            .build();\n\n    @Test\n    void customized() {\n        int clientCount = 2;\n        int clientTaskCount = 10;\n        List\u003cFuture\u003cTask\u003e\u003e futures = new ArrayList\u003c\u003e(); // class Task implements Callable\u003cTask\u003e\n        int maxActiveExecutorCount = 0;\n        for (int c = 0; c \u003c clientCount; c++) {\n            String clientId = \"clientId-\" + (c + 1);\n            for (int t = 0; t \u003c clientTaskCount; t++) {\n                futures.add(this.conottle.submit(new Task(clientId + \"-task-\" + t, MIN_TASK_DURATION), clientId));\n                maxActiveExecutorCount = Math.max(maxActiveExecutorCount, conottle.countActiveExecutors());\n            }\n        }\n        assertEquals(clientCount, maxActiveExecutorCount, \"should be 1:1 between a client and its executor\");\n        int taskTotal = futures.size();\n        assertEquals(clientTaskCount * clientCount, taskTotal);\n        int doneCount = 0;\n        for (Future\u003cTask\u003e future : futures) {\n            if (future.isDone()) {\n                doneCount++;\n            }\n        }\n        assertTrue(doneCount \u003c futures.size());\n        info.log(\"not all of the {} tasks were done immediately\", taskTotal);\n        info.atDebug().log(\"{} out of {} were done\", doneCount, futures.size());\n        for (Future\u003cTask\u003e future : futures) {\n            await().until(future::isDone);\n        }\n        info.log(\"all of the {} tasks were done eventually\", taskTotal);\n        await().until(() -\u003e this.conottle.countActiveExecutors() == 0);\n        info.log(\"no active executor lingers when all tasks complete\");\n    }\n\n    @AfterEach\n    void close() {\n        this.conottle.close();\n    }\n}\n```\n\nAll builder parameters are optional:\n\n- `maxParallelismPerClient` is the maximum concurrency at which one single client's tasks can execute. If omitted or set\n  to a non-positive integer, then the default is `Runtime.getRuntime().availableProcessors()`.\n- `maxClientsInParallel` is the maximum number of clients that can be serviced in parallel. If omitted or set to a\n  non-positive integer, then the default is `Runtime.getRuntime().availableProcessors()`.\n- `workerExecutorService` is the global async thread pool to service all requests for all clients. If omitted, the\n  default is a fork-join thread pool whose capacity is and `Runtime.getRuntime().availableProcessors()`.\n\nThis API has no technical/programmatic upper limit on the parameter values to set for total number of parallelism or\nclients to be supported. Once set, the only limit is on runtime concurrency at any given moment: Before proceeding,\nexcessive tasks or clients will have to wait for active ones to run for completion - that is, the throttling effect.","funding_links":["https://github.com/sponsors/q3769"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fq3769%2Fconottle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fq3769%2Fconottle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fq3769%2Fconottle/lists"}