{"id":20476669,"url":"https://github.com/rabbitmq/gen-batch-server","last_synced_at":"2026-04-01T17:49:27.797Z","repository":{"id":45048218,"uuid":"151234177","full_name":"rabbitmq/gen-batch-server","owner":"rabbitmq","description":"A generic batching server for Erlang and Elixir","archived":false,"fork":false,"pushed_at":"2026-03-31T13:49:25.000Z","size":176,"stargazers_count":48,"open_issues_count":5,"forks_count":10,"subscribers_count":18,"default_branch":"master","last_synced_at":"2026-03-31T14:16:54.046Z","etag":null,"topics":["batch","elixir","erlang","otp"],"latest_commit_sha":null,"homepage":"","language":"Erlang","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rabbitmq.png","metadata":{"files":{"readme":"README.md","changelog":"ChangeLog.md","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,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2018-10-02T09:57:49.000Z","updated_at":"2026-03-31T13:20:52.000Z","dependencies_parsed_at":"2023-11-22T16:04:18.526Z","dependency_job_id":"94fa97e8-ace0-4b55-b21e-8d53546c74e3","html_url":"https://github.com/rabbitmq/gen-batch-server","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/rabbitmq/gen-batch-server","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rabbitmq%2Fgen-batch-server","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rabbitmq%2Fgen-batch-server/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rabbitmq%2Fgen-batch-server/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rabbitmq%2Fgen-batch-server/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rabbitmq","download_url":"https://codeload.github.com/rabbitmq/gen-batch-server/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rabbitmq%2Fgen-batch-server/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31290622,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T13:12:26.723Z","status":"ssl_error","status_checked_at":"2026-04-01T13:12:25.102Z","response_time":53,"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":["batch","elixir","erlang","otp"],"created_at":"2024-11-15T15:22:12.866Z","updated_at":"2026-04-01T17:49:27.787Z","avatar_url":"https://github.com/rabbitmq.png","language":"Erlang","funding_links":[],"categories":[],"sub_categories":[],"readme":"# gen_batch_server\n\n[![CI](https://github.com/rabbitmq/gen-batch-server/actions/workflows/ci.yaml/badge.svg)](https://github.com/rabbitmq/gen-batch-server/actions/workflows/ci.yaml)\n\n\nA generic batching server for erlang / elixir\n\n\n`gen_batch_server` is a stateful generic server similar to [`gen_server`](https://erlang.org/doc/man/gen_server.html) that instead of processing incoming requests\none by one gathers them into batches before they are passed to the behaviour\nimplementation.\n\nBatches are processed _either_ when the Erlang process mailbox has no further\nmessages to batch _or_\nwhen the number of messages in the current batch reaches the maximum batch size\nlimit.\n`gen_batch_server` tries to trade latency for throughput by automatically growing the max batch\nsize limit when message ingress is high and shrinks it down again as ingress\nreduces.\n\nThis behaviour makes it suitable for use as a data sink, proxy or other kinds of\naggregator that benefit from processing messages in batches. Examples\nwould be a log writer that needs to flush messages to disk using `file:sync/1`\nwithout undue delay or a metrics sink that aggregates metrics from multiple\nprocesses and writes them to an external service. It could also be beneficial\nto use `gen_batch_server` to proxy a bunch of processes that want to update\nsome resource (such as a `dets` table) that doesn't handle casts.\n\n## Usage\n\n#### start_link(Name, Mod, Args) -\u003e Result\n#### start_link(Name, Mod, Args, Opts) -\u003e Result\n\n    Types:\n        Name = {local,Name} | {global,GlobalName} | {via,Module,ViaName}\n        Mod = module()\n        Args = term()\n        Opt = {debug, Dbgs} |\n              {min_batch_size | max_batch_size, non_neg_integer()} |\n              {reversed_batch, boolean()} |\n              {flush_mailbox_on_terminate, false | {true, SummaryCount}} |\n              {batch_size_growth, exponential | {aimd, Step}}\n        SummaryCount = non_neg_integer()\n        Step = pos_integer()\n        Opts = [Opt]\n        Opts = [term()]\n        Result = {ok,Pid} | ignore | {error,Error}\n\nCreates a `gen_batch_server` as part of a supervision tree. The minimum and\nmaximum batch sizes that control the bounds of the batch sizes that are processed\ncan be controlled using the `min_batch_size` (default: 32)\nand `max_batch_size` (default: 8192) options.\n\nThe `reversed_batch` option is an advanced option that where the batch that is\npassed to `handle_batch/2` is in reversed order to the one the messages were\nreceived in. This avoids a `list:reverse/1` all before the batch handling and is\nsomewhat more performant.\n\nThe `flush_mailbox_on_terminate` option controls whether pending mailbox messages\nare drained when the server terminates. By default it is `false` (disabled). When\nset to `{true, SummaryCount}`, up to `SummaryCount` of the pending messages are\ncaptured along with the total mailbox count and written to the process dictionary\nunder the key `mailbox_summary` (as `#{total =\u003e Count, messages =\u003e [...]}`). The\nmailbox is then fully drained. This happens *before* `Module:terminate/2` is called,\nso the callback can inspect or forward the summary. Draining the mailbox prevents\n`proc_lib` crash reports from pretty-printing potentially large messages (e.g. binary\npayloads), which can cause unbounded memory growth.\n\nThe `batch_size_growth` option controls how the batch size grows when the server\nis under load. The default, `exponential`, doubles the batch size each time a full\nbatch is completed (the original behaviour). Setting it to `{aimd, Step}` switches\nto Additive Increase / Multiplicative Decrease: the batch size grows by `Step` on\neach full batch and halves whenever the mailbox is found empty. AIMD produces a\nsmoother, more gradual ramp-up and is useful when large sudden jumps in batch size\nare undesirable.\n\n\n#### cast(ServerRef, Request) -\u003e ok\n\n    Types:\n        ServerRef = pid() | {Name :: atom(), node()} | Name :: atom()\n        Request = term()\n\nSends an asynchronous request to the `gen_batch_server returning` `ok` immediately.\nThe request tuple (`{cast, Request}`) is included in the list of operations passed\nto `Module:handle_batch/2`.\n\n#### cast_batch(ServerRef, Batch) -\u003e ok\n\n    Types:\n        ServerRef = pid() | {Name :: atom(), node()} | Name :: atom()\n        Batch = [term()]\n\nSends an asynchronous batch of requests to the `gen_batch_server returning` `ok`\nimmediately. The batch is appended in order to the current gen_batch_server batch.\n\n#### call(ServerRef, Request) -\u003e Reply\n#### call(ServerRef, Request, Timeout) -\u003e Reply\n\n    Types:\n        ServerRef = pid() | {Name :: atom(), node()} | Name :: atom()\n        Request = term()\n        Reply = term()\n        Timeout = non_neg_integer() | infinity.\n\nMakes a synchronous call to the `gen_batch_server` and waits for the response provided\nby `Module:handle_batch/2`.\nThe timeout is optional and defaults to 5000ms.\n\n#### send_request(ServerRef, Request) -\u003e ReqId\n\n    Types:\n        ServerRef = pid() | {Name :: atom(), node()} | Name :: atom()\n        Request = term()\n        ReqId = request_id()\n\nSends an asynchronous call request to the `gen_batch_server`. Returns a request\nidentifier `ReqId` that can be used with `wait_response/2`, `receive_response/2`,\nor `check_response/2` to collect the reply later. The request appears as a\n`{call, From, Request}` operation in `Module:handle_batch/2`, where `From` is the\nsame opaque alias-based tag as described by `reply_tag()` / `from()` in the public API.\n\n#### send_request(ServerRef, Request, Label, ReqIdCollection) -\u003e NewReqIdCollection\n\n    Types:\n        ServerRef = pid() | {Name :: atom(), node()} | Name :: atom()\n        Request = term()\n        Label = term()\n        ReqIdCollection = request_id_collection()\n        NewReqIdCollection = request_id_collection()\n\nLike `send_request/2`, but stores the request identifier in a collection\nassociated with `Label`. The collection can later be passed to\n`receive_response/3`, `wait_response/3`, or `check_response/3`.\n\n#### wait_response(ReqId, WaitTime) -\u003e Result\n#### receive_response(ReqId, Timeout) -\u003e Result\n\n    Types:\n        ReqId = request_id()\n        WaitTime = Timeout = timeout() | {abs, integer()}\n        Result = {reply, Reply} | {error, {Reason, ServerRef}} | timeout\n\nWait for a response to an async request made with `send_request/2`.\n`receive_response` abandons the request on timeout (late replies are dropped),\nwhile `wait_response` keeps the monitor so you can retry.\n\n#### wait_response(ReqIdCollection, WaitTime, Delete) -\u003e Result\n#### receive_response(ReqIdCollection, Timeout, Delete) -\u003e Result\n\n    Types:\n        ReqIdCollection = request_id_collection()\n        WaitTime = Timeout = timeout() | {abs, integer()}\n        Delete = boolean()\n        Result = {Response, Label, NewReqIdCollection} | no_request | timeout\n        Response = {reply, Reply} | {error, {Reason, ServerRef}}\n\nCollection variants. Returns the response along with the `Label` associated\nwith the request and an updated collection.\n\n#### check_response(Msg, ReqId) -\u003e Result\n\n    Types:\n        Msg = term()\n        ReqId = request_id()\n        Result = {reply, Reply} | {error, {Reason, ServerRef}} | no_reply\n\nCheck whether a received message `Msg` is a response to the request `ReqId`.\nReturns `no_reply` if the message does not correspond to this request.\n\n#### check_response(Msg, ReqIdCollection, Delete) -\u003e Result\n\n    Types:\n        Msg = term()\n        ReqIdCollection = request_id_collection()\n        Delete = boolean()\n        Result = {Response, Label, NewReqIdCollection} | no_request | no_reply\n        Response = {reply, Reply} | {error, {Reason, ServerRef}}\n\nCollection variant of `check_response/2`.\n\n#### reqids_new() -\u003e NewReqIdCollection\n\nCreates a new empty request identifier collection.\n\n#### reqids_add(ReqId, Label, ReqIdCollection) -\u003e NewReqIdCollection\n\nStores `ReqId` with an associated `Label` in the collection.\n\n#### reqids_size(ReqIdCollection) -\u003e non_neg_integer()\n\nReturns the number of request identifiers in the collection.\n\n#### reqids_to_list(ReqIdCollection) -\u003e [{ReqId, Label}]\n\nConverts the collection to a list of `{ReqId, Label}` pairs.\n\n#### Module:init(Args) -\u003e Result\n\n    Types:\n        Args = term()\n        Result = {ok, State} |\n                 {ok, State, {continue, Continue}} |\n                 ignore |\n                 {stop, Reason}\n        State = term()\n        Continue = term()\n        Reason = term()\n\nCalled whenever a `gen_batch_server` is started with the arguments provided\nto `start_link/4`.\n\nReturning `{ok, State, {continue, Continue}}` will cause `handle_continue/2`\nto be called before processing any messages.\n\nReturning `ignore` will cause `start_link` to return `ignore` and the process\nwill exit normally without a crash report.\n\nCan be used in scenarios where a `gen_batch_server`\ndepends on a resource that can be temporarily\nunavailable (e.g. when a Ra member runs out of disk space).\n\n#### Module:handle_batch(Batch, State) -\u003e Result.\n\n    Types:\n        Batch = [Op]\n        UserOp = term(),\n        Op = {cast, UserOp} |\n             {call, from(), UserOp} |\n             {info, UserOp}.\n        Result = {ok, State} |\n                 {ok, State, {continue, Continue}} |\n                 {ok, Actions, State} |\n                 {ok, Actions, State, {continue, Continue}} |\n                 {stop, Reason}\n        State = term()\n        From = {Pid :: pid(), Tag :: reference()}\n        Action = {reply, From, Msg} | garbage_collect\n        Actions = [Action]\n        Reason = term()\n\nCalled whenever a new batch is ready for processing. The implementation can\noptionally return a list of reply actions used to reply to `call` operations.\n\n#### Module:handle_continue(Continue, State) -\u003e Result\n\n    Types:\n        Continue = term()\n        State = term()\n        Result = {ok, NewState} |\n                 {ok, NewState, {continue, NewContinue}} |\n                 {stop, Reason}\n        NewState = term()\n        NewContinue = term()\n        Reason = term()\n\nOptional. Called after `init/1` returns `{ok, State, {continue, Continue}}` or\nafter `handle_batch/2` returns with a `{continue, Continue}` tuple.\n\nMost useful for post-`init/2` work that needs\nthe server to be registered first.\n\n#### Module:terminate(Reason, State) -\u003e Result\n\n    Types:\n        Reason = term()\n        Result = term()\n        State = term(),\n\n\nOptional. Called whenever a `gen_batch_server` is terminating.\n\n#### Module:format_status(State) -\u003e Result\n\n    Types:\n        Result = term()\n        State = term(),\n\n\nOptional. Used to provide a custom formatting of the user state.\n\n\n# Copyright\n\n(c) 2018-2026 Broadcom. All Rights Reserved. The term Broadcom refers to Broadcom Inc. and/or its subsidiaries.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frabbitmq%2Fgen-batch-server","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frabbitmq%2Fgen-batch-server","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frabbitmq%2Fgen-batch-server/lists"}