{"id":30117062,"url":"https://github.com/hackermondev/batched","last_synced_at":"2025-08-10T10:36:47.146Z","repository":{"id":291012289,"uuid":"976144546","full_name":"hackermondev/batched","owner":"hackermondev","description":"rust macro util for batching expensive operations","archived":false,"fork":false,"pushed_at":"2025-07-20T03:43:24.000Z","size":70,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-20T04:17:15.049Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Rust","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/hackermondev.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":"2025-05-01T15:22:03.000Z","updated_at":"2025-07-20T03:43:27.000Z","dependencies_parsed_at":"2025-05-01T22:27:06.247Z","dependency_job_id":"dca75ba9-028d-4e4a-b17c-e279ed8bf535","html_url":"https://github.com/hackermondev/batched","commit_stats":null,"previous_names":["hackermondev/batched"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/hackermondev/batched","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hackermondev%2Fbatched","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hackermondev%2Fbatched/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hackermondev%2Fbatched/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hackermondev%2Fbatched/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hackermondev","download_url":"https://codeload.github.com/hackermondev/batched/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hackermondev%2Fbatched/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269712268,"owners_count":24463209,"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","status":"online","status_checked_at":"2025-08-10T02:00:08.965Z","response_time":71,"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":[],"created_at":"2025-08-10T10:36:46.603Z","updated_at":"2025-08-10T10:36:47.133Z","avatar_url":"https://github.com/hackermondev.png","language":"Rust","funding_links":[],"categories":["Rust"],"sub_categories":[],"readme":"# batched\nRust macro utility for batching expensive async operations.\n\n## Installation\n```sh\ncargo add batched \n```\n\nOr add this to your `Cargo.toml`:\n```toml\n[dependencies]\nbatched = \"0.2.3\"\n```\n\n## #[batched]\n- **window**: Minimum amount of time (in milliseconds) the background thread waits before processing a batch.\n- **limit**: Maximum amount of items that can be grouped and processed in a single batch.\n- **concurrent**: Maximum amount of concurrent batched tasks running (default: `Infinity`)\n\nThe target function must have a single argument, a vector of items (`Vec\u003cT\u003e`). \n\nThe return value of the batched function is propagated (cloned) to all async calls of the batch, unless the batched function returns a `Vec\u003cT\u003e`, in which case the return value for each call is pulled from the iterator in the same order of the input.\n\nIf the return value is not an iterator, The target function return type must implement `Clone` to propagate the result. Use `batched::error::SharedError` to wrap your error types (if they don't implement Clone).\n\n\n## Prerequisites \n- Built for async environments (tokio), will not work without a tokio async runtime\n- Target function must have async\n- Not supported inside structs:\n```rust\nstruct A;\n\nimpl A {\n    #[batched(window = 1000, limit = 100)]\n    fn operation() {\n        ...\n    }\n}\n```\n\n\n## Examples\n\n### Simple add batch\n```rust\n#[batched(window = 100, limit = 1000)]\nasync fn add(numbers: Vec\u003cu32\u003e) -\u003e u32 {\n    numbers.iter().sum()\n}\n\nasync fn main() {\n    for _ in 0..99 {\n        tokio::task::spawn(async move {\n            add(1).await\n        });\n    }\n\n    let result = add(1).await;\n    assert_eq!(result, 100);\n}\n```\n\n### Batch insert rows\n\n```rust\nuse batched::{batched, error::SharedError};\n\n// Macros creates functions [`insert_message`] and [`insert_message_multiple`]\n#[batched(window = 100, limit = 100_000, boxed)]\nasync fn insert_message(messages: Vec\u003cString\u003e) -\u003e Result\u003c(), SharedError\u003canyhow::Error\u003e\u003e {\n    let pool = PgPool::connect(\"postgres://user:password@localhost/dbname\").await?;\n    let mut query = String::from(\"INSERT INTO messages (content) VALUES \");\n    ...\n}\n\n#[post(\"/message\")]\nasync fn service(message: String) -\u003e Result\u003c(), anyhow::Error\u003e {\n    insert_message(message).await?;\n    Ok(())\n}\n\n#[post(\"/bulk_messages\")]\nasync fn service(messages: Vec\u003cString\u003e) -\u003e Result\u003c(), anyhow::Error\u003e {\n    insert_message_multiple(messages).await?;\n    Ok(())\n}\n```\n\n### Batch insert rows and return them\n\n```rust\nuse batched::{batched, error::SharedError};\n\nstruct Row {\n    pub id: usize,\n    pub content: String,\n}\n\n#[batched(window = 100, limit = 100_000)]\nasync fn insert_message_batched(messages: Vec\u003cString\u003e) -\u003e Result\u003cVec\u003cRow\u003e, SharedError\u003canyhow::Error\u003e\u003e {\n    let pool = PgPool::connect(\"postgres://user:password@localhost/dbname\").await?;\n    let mut query = String::from(\"INSERT INTO messages (content) VALUES \");\n    ...\n}\n\n#[post(\"/message\")]\nasync fn service(message: String) -\u003e Result\u003c(), anyhow::Error\u003e {\n    let message: Row = insert_message(message).await?;\n    Ok(())\n}\n\n#[post(\"/bulk_messages\")]\nasync fn service(messages: Vec\u003cString\u003e) -\u003e Result\u003c(), anyhow::Error\u003e {\n    let messages: Vec\u003cRow\u003e = insert_message_multiple(messages).await?;\n    Ok(())\n}\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhackermondev%2Fbatched","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhackermondev%2Fbatched","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhackermondev%2Fbatched/lists"}