{"id":13809203,"url":"https://github.com/Hentioe/honeycomb","last_synced_at":"2025-05-14T05:33:28.771Z","repository":{"id":238388240,"uuid":"796448778","full_name":"Hentioe/honeycomb","owner":"Hentioe","description":"Another scheduling system, focusing on the collection of results for one-time tasks, written in Elixir","archived":false,"fork":false,"pushed_at":"2024-05-16T02:30:25.000Z","size":73,"stargazers_count":19,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-05-22T01:19:14.442Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Hentioe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2024-05-06T00:27:03.000Z","updated_at":"2024-08-04T01:10:07.168Z","dependencies_parsed_at":null,"dependency_job_id":"877f1dc2-2a23-466b-87b1-93c9802cd236","html_url":"https://github.com/Hentioe/honeycomb","commit_stats":null,"previous_names":["hentioe/honeycomb"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hentioe%2Fhoneycomb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hentioe%2Fhoneycomb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hentioe%2Fhoneycomb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hentioe%2Fhoneycomb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Hentioe","download_url":"https://codeload.github.com/Hentioe/honeycomb/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225277992,"owners_count":17448801,"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":[],"created_at":"2024-08-04T01:02:08.119Z","updated_at":"2024-11-19T01:30:53.887Z","avatar_url":"https://github.com/Hentioe.png","language":"Elixir","funding_links":[],"categories":["Queue"],"sub_categories":[],"readme":"# Honeycomb 🐝🍯\n\n[![Module Version](https://img.shields.io/hexpm/v/honeycomb.svg)](https://hex.pm/packages/honeycomb)\n[![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/honeycomb/)\n\nHoneycomb is an Elixir library designed for executing asynchronous or background tasks that require persisting results in batches. Its core functionalities include asynchronous/background execution and concurrency control.\n\n[中文教程](https://blog.hentioe.dev/posts/honeycomb-introduction.html)\n\n## Mastery Honeycomb\n\nImagine a honeycomb where you are the commander (you can envision yourself as the queen bee). You assign \"gather honey\" tasks to each bee, and the bees execute them one by one. Once the honey gathering is complete, the bees return to the hive, gradually filling the honeycomb with honey ready for harvesting.\n\nYou can also limit the number of bees leaving the hive simultaneously (concurrency control) or make the bees wait for a moment before departing (delayed execution). This is the fundamental usage logic of the Honeycomb library.\n\nFurthermore, you can create and command the activities of multiple honeycombs without them interfering with each other.\n\n## Installation\n\nAdd Honeycomb to your mix.exs dependencies:\n\n```elixir\ndef deps do\n  [\n    {:honeycomb, \"~\u003e 0.1.1\"},\n  ]\nend\n```\n\n## Tutorial TOC\n\n- [Basic Usage](#basic-usage)\n  - [Adding to the Supervision Tree](#adding-to-the-supervision-tree)\n  - [Executing Tasks](#executing-tasks)\n  - [Harvesting Honey](#harvesting-honey)\n  - [Error Handling](#error-handling)\n  - [Failure Retry](#failure-retry)\n  - [Terminating Tasks](#terminating-tasks)\n  - [Canceling Tasks](#canceling-tasks)\n  - [Stopping Tasks](#stopping-tasks)\n  - [Anonymous Tasks](#anonymous-tasks)\n  - [Synchronous Calls](#synchronous-calls)\n- [Advanced Usage](#advanced-usage)\n  - [Concurrency Control](#concurrency-control)\n  - [Delayed Retry](#delayed-retry)\n  - [Delayed Execution](#delayed-execution)\n  - [Stateless Tasks](#stateless-tasks)\n  - [Multiple Services](#multiple-services)\n  - [Log Metadata](#log-metadata)\n\n## Basic Usage\n\nThis section introduces the basic usage of the Honeycomb library. The examples provided may not necessarily have real-world significance, and specific use cases need to be explored by users themselves.\n\n### Adding to the Supervision Tree\n\nFirst, you need to define your own queen bee:\n\n```elixir\ndefmodule YourProject.Queen do\n  use Honeycomb.Queen, id: :my_honeycomb\nend\n```\n\nCombine the honeycomb and the queen bee, and add them to the supervision tree:\n\n```elixir\nchildren = [\n  # Omit other processes...\n  {Honeycomb, queen: YourProject.Queen}\n]\n\nopts = [strategy: :one_for_one, name: YourProject.Supervisor]\nSupervisor.start_link(children, opts)\n```\n\nNext, we can use the id `:my_honeycomb` as the `queen` to execute all calls. You can also choose not to set an `id`, in which case the module name `YourProject.Queen` will be used as the `queen` parameter.\n\n### Executing Tasks\n\nUse the `gather_*` series of APIs to create tasks. Their first three parameters are `queen`, `name`, and `run`. The `run` parameter is our task, which can be a function that conforms to the `(-\u003e any)` specification or a tuple that conforms to the `{module(), atom(), [any()]}` specification.\n\nLet's execute a task that sleeps for 10 seconds:\n\n```elixir\niex\u003e Honeycomb.gather_honey :my_honeycomb, \"sleep-10\", fn -\u003e :timer.sleep(10 * 1000) end\n```\n\nThe tuple version looks like this:\n\n```elixir\niex\u003e Honeycomb.gather_honey :my_honeycomb, \"sleep-10\", {:timer, :sleep, [10 * 1000]}\n```\n\nHere, the `sleep-10` parameter value is the name we give to the bee, which should be unique.\n\nThe above `Honeycomb.gather_honey/3` call immediately returns the result:\n\n```elixir\n{:ok,\n %Honeycomb.Bee{\n   name: \"sleep-10\",\n   status: :pending,\n   run: #Function\u003c43.105768164/0 in :erl_eval.expr/6\u003e,\n   expect_run_at: ~U[2024-05-07 21:32:15.810149Z],\n   work_start_at: nil,\n   work_end_at: nil,\n   stateless: false,\n   result: nil\n }}\n```\n\nThis is our bee. But at this moment, it's still in the `pending` status and hasn't been executed immediately. Because the entire Honeycomb system is asynchronous, it doesn't synchronously call the task.\n\nIf we immediately call `Honeycomb.bees/1`, we'll see that it's already being executed:\n\n```elixir\niex\u003e Honeycomb.bees :my_honeycomb\n[\n  %Honeycomb.Bee{\n    name: \"sleep-10\",\n    status: :running,\n    run: #Function\u003c43.105768164/0 in :erl_eval.expr/6\u003e,\n    expect_run_at: ~U[2024-05-07 21:35:56.390067Z],\n    work_start_at: ~U[2024-05-07 21:35:56.390377Z],\n    work_end_at: nil,\n    stateless: false,\n    result: nil\n  }\n]\n```\n\nThe `Honeycomb.bees/1` function here returns all the bees (hereinafter referred to as `bee`). The `expect_run_at` field indicates the expected execution time. Since this is not a delayed task, the expected execution time is usually the time when the bee was created. The system will immediately enter the queue to check and execute the task, so the bee will quickly switch to the executing status and update the `work_start_at`, which is the time when the work started.\n\n\u003e [!NOTE]\n\u003e If it's a delayed task, there will be a noticeable gap between these two times. But since this is a non-delayed task, the time difference between them is negligible.\n\nAfter waiting for 10 seconds, let's check again:\n\n```elixir\niex\u003e Honeycomb.bees :my_honeycomb\n[\n  %Honeycomb.Bee{\n    name: \"sleep-10\",\n    status: :done,\n    run: #Function\u003c43.105768164/0 in :erl_eval.expr/6\u003e,\n    expect_run_at: ~U[2024-05-07 21:35:56.390067Z],\n    work_start_at: ~U[2024-05-07 21:35:56.390377Z],\n    work_end_at: ~U[2024-05-07 21:36:06.391425Z],\n    stateless: false,\n    result: :ok\n  }\n]\n```\n\nYou'll find that this bee has finished executing. The `work_end_at` represents the actual end time of execution, with an interval of about 10 seconds from `work_start_at`.\nThe execution result `:ok` (the return value of `:timer.sleep/1`) is stored in the `result` field.\n\n### Harvesting Honey\n\nIn the above example, we used `Honeycomb.bees/1` to view all bees and obtained the task execution result from the `result` field. However, it's not a function specifically designed for retrieving results. We can also use the `Honeycomb.bee/2` function to get the status of a specific bee:\n\n```elixir\niex\u003e Honeycomb.bee :my_honeycomb, \"sleep-10\"\n%Honeycomb.Bee{\n  name: \"sleep-10\",\n  status: :done,\n  run: #Function\u003c43.105768164/0 in :erl_eval.expr/6\u003e,\n  expect_run_at: ~U[2024-05-07 21:35:56.390067Z],\n  work_start_at: ~U[2024-05-07 21:35:56.390377Z],\n  work_end_at: ~U[2024-05-07 21:36:06.391425Z],\n  stateless: false,\n  result: :ok\n}\n```\n\nThe above code directly retrieves the latest status of the corresponding bee using the bee name `sleep-10`, instead of traversing each bee.\n\nIn many cases, we might only care about the result and not the process (not caring about the bee's status). We can call the `Honeycomb.harvest_honey/2` function to directly harvest the result:\n\n```elixir\niex\u003e Honeycomb.harvest_honey :my_honeycomb, \"sleep-10\"\n{:done, :ok}\n```\n\nIt returns `:done` and `:ok`, respectively representing the status after execution is completed and the execution result (the return value of `:timer.sleep/1`). Sometimes, `done` here might be `raised`, indicating that an error occurred during execution.\n\nApart from that, everything else will be in the classic error return structure `{:error, reason}`. The error reasons include:\n\n- Not found (`:not_found`)\n- Not completed (`:undone`)\n\nAdditionally, after a successful call to `Honeycomb.harvest_honey/2`, the bee will be removed. This means that the function with the same parameters can only be called successfully once. After \"harvesting the honey\", both the bee and the honey (result) will cease to exist. This is easy to understand. In many cases, we don't need to keep the results forever. We take them away and use them again, no longer related to the honeycomb:\n\n```elixir\niex\u003e Honeycomb.harvest_honey :my_honeycomb, \"sleep-10\"\n{:error, :not_found} # The \"sleep-10\" bee has been removed because the honey has been harvested.\n```\n\n### Error Handling\n\nFor running tasks, unless you need to deliberately handle certain exceptions, you don't need to wrap the task function with `try/rescue`. This is because Honeycomb has already taken care of this. Let's execute a task that immediately raises an error:\n\n```elixir\niex\u003e Honeycomb.gather_honey :my_honeycomb, \"raise-now\", fn -\u003e raise \"I am an error\" end\n{:ok,\n %Honeycomb.Bee{\n   name: \"raise-now\",\n   status: :pending,\n   run: #Function\u003c43.105768164/0 in :erl_eval.expr/6\u003e,\n   expect_run_at: ~U[2024-05-07 22:09:45.608439Z],\n   work_start_at: nil,\n   work_end_at: nil,\n   stateless: false,\n   result: nil\n }}\n```\n\nThen we check this task:\n\n```elixir\niex\u003e Honeycomb.bee :my_honeycomb, \"raise-now\"\n%Honeycomb.Bee{\n  name: \"raise-now\",\n  status: :raised,\n  run: #Function\u003c43.105768164/0 in :erl_eval.expr/6\u003e,\n  expect_run_at: ~U[2024-05-07 22:09:45.608439Z],\n  work_start_at: ~U[2024-05-07 22:09:45.609694Z],\n  work_end_at: ~U[2024-05-07 22:09:45.609700Z],\n  stateless: false,\n  result: %RuntimeError{message: \"I am an error\"}\n}\n```\n\nWe can see that the bee's status has changed to `raised` instead of `done`. And the `result` field stores the exception structure data that occurred when the `raise` happened. Therefore, regardless of how your task encounters an error, it won't affect the operation of the Honeycomb system.\n\n### Failure Retry\n\nAfter a task execution fails, Honeycomb collects the error result and sets the bee's status to `raised`. Sometimes, we want to automatically retry when certain errors occur, such as network timeouts. This can be easily achieved by configuring the `failure_mode` (failure mode):\n\n```elixir\ndefmodule YourProject.CleanerQueen do\n  alias Honeycomb.FailureMode.Retry\n\n  use Honeycomb.Queen,\n    id: :cleaner,\n    failure_mode: %Retry{max_times: 5, ensure: \u0026ensure/1}\n\n  def ensure(error) do\n    case error do\n      %MatchError{term: {:error, %Telegex.RequestError{reason: :timeout}}} -\u003e\n        # Request timeout, continue retrying\n        :continue\n\n      _ -\u003e\n        :halt\n    end\n  end\nend\n```\n\nWe created a `CleanerQueen` and added the `failure_mode` setting. When a task execution encounters an error, Honeycomb enters the failure mode and performs additional work according to the mode configuration. Here, we set the failure mode to timeout and implemented the `ensure/1` function ourselves. Once the task execution fails, it enters this function to determine whether to retry.\n\n\u003e [!NOTE]\n\u003e Our custom `ensure/1` function decides to retry after a network timeout by examining the error details, and it doesn't retry in other cases. At the same time, we set `max_times`, which limits the maximum number of retries to avoid falling into an infinite retry loop.\n\nThe above is actually a simplified real-world example. It is a wrapper for Telegex functions, giving the APIs the ability to automatically retry. The wrapper is as follows:\n\n```elixir\ndef async_delete_message(chat_id, message_id) do\n  run = fn -\u003e delete_message!(chat_id, message_id) end\n\n  Honeycomb.gather_honey(:cleaner, \"delete-#{chat_id}-#{message_id}\", run, stateless: true)\nend\n\ndef delete_message!(chat_id, message_id) do\n  {:ok, true} = Telegex.delete_message(chat_id, message_id)\nend\n```\n\nWe wrapped the `Telegex.delete_message/2` function without handling any errors and directly pattern-matched the correct return result. If it doesn't match, it means an error occurred, and the failure mode is entered to determine the error details. If it's a network timeout, it automatically retries.\n\nTheoretically, any idempotent call can be wrapped in this way to make the call more stable. There's no need to implement any retry mechanism yourself because Honeycomb does it for us.\n\n\u003e [!CAUTION]\n\u003e Note that the code in `ensure/1` should not contain time-consuming operations because this callback function is executed in Honeycomb's scheduler process, and time-consuming operations will affect the scheduling efficiency. If this callback function encounters an error, the scheduler won't arrange a retry.\n\n### Terminating Tasks\n\nOnce a bee is running, calling `Honeycomb.terminate_bee/2` can terminate it. First, let's create a task that sleeps for 10 seconds, outputs `:hello` to the console, and returns it as the result:\n\n```elixir\niex\u003e Honeycomb.gather_honey :my_honeycomb, \"hello\", fn -\u003e :timer.sleep(10 * 1000); IO.inspect(:hello) end\n{:ok,\n %Honeycomb.Bee{\n   name: \"hello\",\n   status: :pending,\n   run: #Function\u003c43.105768164/0 in :erl_eval.expr/6\u003e,\n   task_pid: nil,\n   create_at: ~U[2024-05-09 01:48:02.609955Z],\n   expect_run_at: ~U[2024-05-09 01:48:02.609955Z],\n   timer: nil,\n   work_start_at: nil,\n   work_end_at: nil,\n   stateless: false,\n   result: nil\n }}\n```\n\nNext, let's terminate it while it's still running within the 10 seconds:\n\n```elixir\niex\u003e Honeycomb.terminate_bee :my_honeycomb, \"hello\"\n{:ok,\n %Honeycomb.Bee{\n   name: \"hello\",\n   status: :terminated,\n   run: #Function\u003c43.105768164/0 in :erl_eval.expr/6\u003e,\n   task_pid: nil,\n   create_at: ~U[2024-05-09 01:48:02.609955Z],\n   expect_run_at: ~U[2024-05-09 01:48:02.609955Z],\n   timer: nil,\n   work_start_at: ~U[2024-05-09 01:48:02.610295Z],\n   work_end_at: nil,\n   stateless: false,\n   result: nil\n }\n```\n\nAfter executing the terminate function, it immediately returns the latest bee, with its `terminated` status indicating that it has been terminated. We wait for 10 seconds and won't see any output in the console. Repeatedly checking this bee won't show any further updates to the `result` because the task has been killed.\n\n\u003e [!CAUTION]\n\u003e Note: The `Honeycomb.terminate_bee/2` function cannot terminate a bee that hasn't started running yet and will return `{:error, bad_status}`. For bees that haven't entered the `running` status yet, you can call `Honeycomb.cancel_bee/2` to cancel them.\n\n### Canceling Tasks\n\nCalling `Honeycomb.cancel_bee/2` can cancel the execution of a bee. A bee can only be canceled when it's in the `pending` status; otherwise, it will return `{:error, bad_staus}`. When cancellation is successful, the bee's status becomes `canceled`. The usage is similar to Terminating Tasks.\n\n### Stopping Tasks\n\nCalling `Honeycomb.stop_bee/2` can stop a bee, which is usually more convenient than \"terminating\" and \"canceling\". This is the third API related to killing tasks, and you might have the following questions:\n\n- Why provide two APIs for \"terminating\" and \"canceling\"?\n\n  \u003e Because the underlying logic and semantics of \"terminating\" and \"canceling\" are completely different, I intentionally exposed two corresponding independent implementations. They have very strict requirements for the bee's status, which can cause trouble. For example, I don't care if the task being killed is running or not.\n\n- What's the difference between the \"stopping\" operation and the other two?\n  \u003e The `Honeycomb.stop_bee/2` function was born to avoid such troubles. It integrates the logic of both \"terminating\" and \"canceling\", compatible with stopping bees in both `:pending` and `:running` states.\n\n\u003e [!NOTE]\n\u003e As mentioned above, \"stopping\" is a reuse of the two operations of \"terminating\" and \"canceling\", so a stopped bee doesn't have a status representing stopped. The bee can be `terminated` (corresponding to being terminated while running) or `canceled` (corresponding to being canceled before running).\n\n### Anonymous Tasks\n\nSometimes our tasks are generated in large quantities, and there are no one-to-one external variables that can be combined to form a name with agreed-upon properties. In this case, you can pass the special atom `anon` to the `name` parameter of the `gather_*` series of functions, and it will generate a name with unique properties when creating the bee, as follows:\n\n```elixir\niex\u003e Honeycomb.gather_honey :my_honeycomb, :anon, fn -\u003e :ok end\n{:ok,\n %Honeycomb.Bee{\n   name: \"-576460752303423485\",\n   status: :pending,\n   caller: nil,\n   run: #Function\u003c43.105768164/0 in :erl_eval.expr/6\u003e,\n   task_pid: nil,\n   retry: 0,\n   create_at: ~U[2024-05-10 09:22:53.888790Z],\n   expect_run_at: ~U[2024-05-10 09:22:53.888790Z],\n   timer: nil,\n   work_start_at: nil,\n   work_end_at: nil,\n   stateless: false,\n   result: nil\n }}\n```\n\n\u003e [!CAUTION]\n\u003e Since `:anon` is not a name value but a generation strategy, never use `:anon` as a name to call functions like `Honeycomb.bee/2` or `Honeycomb.harvest_honey/2`.\n\n### Synchronous Calls\n\nUsing the `Honeycomb.gather_honey_sync/4` function, you can simulate synchronous calls. It blocks the current calling process until it receives the execution result and returns the result as the return value. In plain terms, this function can wait for the task to complete execution and return the task's result, just like synchronous task execution:\n\n```elixir\niex\u003e Honeycomb.gather_honey_sync :my_honeycomb, :anon, fn -\u003e :timer.sleep(2 * 1000); :hello end\n:hello\n```\n\nAfter a 2-second block, the execution result `:hello` is directly obtained. Sometimes, synchronous APIs are very valuable. They can wrap any function that needs a return value, giving the illusion that the underlying system is not asynchronous.\n\n\u003e [!WARNING]\n\u003e It's still important to emphasize that such synchronous APIs only simulate synchronicity, and the task still enters the scheduling system. The task is also affected by other mechanisms, such as concurrency control and failure modes.\n\nPassing the `timeout` option can set a timeout. After the timeout, it returns `{:error, :sync_timeout}`. The call timeout refers to the specified time being reached, but the task still hasn't finished execution. Note: **After a timeout occurs, the task will be immediately terminated**.\n\n\u003e [!NOTE]\n\u003e The tasks generated by synchronous APIs are stateless, which means they are automatically deleted after execution is completed. After all, we have already obtained the result.\n\n## Advanced Usage\n\nFrom the tutorial above, we can simply define Honeycomb as an execution and result collection system for asynchronous tasks. However, its usage is not limited to this. This chapter will cover some more complex usage scenarios.\n\n### Concurrency Control\n\nModify our own `Queen` module and add the `concurrency` parameter:\n\n```elixir\ndefmodule YourProject.Queen do\n  use Honeycomb.Queen, id: :my_honeycomb, concurrency: 10\nend\n```\n\nAt this point, our `:my_honeycomb` honeycomb has the capability of concurrency control. It will always ensure that only 10 tasks are executed simultaneously, and excess tasks will enter the queue in order and wait for execution. Before a waiting bee is run, its status will remain `pending`, and `work_start_at` will always be nil. After the task starts, the waiting duration of the relevant bee can be obtained by calculating the difference between `expect_run_at` and `work_start_at`.\n\n### Delayed Retry\n\nFrom the Failure Retry chapter, we learned about Honeycomb's retry mechanism, but it has further usage methods. In the `ensure/1` callback, you can return a third value `{:continue, delay}`, where delay represents the `delay` time for this retry.\n\nRetries with delays have huge differences in the underlying mechanism compared to immediate retries. When we directly return `:continue`, the scheduling system immediately reallocates a runner to execute again. This process ignores the existing waiting queue, as if all retries are considered as a whole. Unless the retries end, the task is not considered complete. Delayed retries (even with a delay of `0` milliseconds) will re-enqueue the task and participate in scheduling, as if the task was re-added to the system and needs to queue again. Therefore, even returning a delay of `0` milliseconds has practical significance.\nWe can further optimize the retry mechanism for calling [`Telegex`](https://github.com/telegex/telegex) APIs as follows:\n\n```elixir\ndef ensure(error) do\n  case error do\n    %MatchError{term: {:error, %Telegex.RequestError{reason: :timeout}}} -\u003e\n      # Request timed out, performing retry\n      :continue\n\n    %MatchError{\n      term:\n        {:error,\n          %Telegex.Error{\n            description: \u003c\u003c\"Too Many Requests: retry after \" \u003c\u003e second\u003e\u003e,\n            error_code: 429\n          }}\n    } -\u003e\n      # Wait for the number of seconds specified in the error message before retrying.\n      {:continue, String.to_integer(second) * 1000}\n\n    _ -\u003e\n      :halt\n  end\nend\n```\n\nThis `ensure/1` function schedules retry delays according to the waiting time suggested in the API response, ensuring the success rate of API calls.\n\n### Delayed Execution\n\nThe entry point for adding or starting tasks is the `Honeycomb.gather_honey/4` function, and its fourth parameter is a `keyword` for some optional parameters. We pass the `delay` option to delay task execution:\n\n```elixir\niex\u003e Honeycomb.gather_honey :my_honeycomb, \"delay-run\", fn -\u003e :ok end, delay: 10 * 1000\n{:ok,\n %Honeycomb.Bee{\n   name: \"delay-run\",\n   status: :pending,\n   run: #Function\u003c43.105768164/0 in :erl_eval.expr/6\u003e,\n   create_at: ~U[2024-05-07 22:36:35.216562Z],\n   expect_run_at: ~U[2024-05-07 22:36:45.216562Z],\n   work_start_at: nil,\n   work_end_at: nil,\n   stateless: false,\n   result: nil\n }}\n```\n\nWe created a bee named `delay-run,` but our task function doesn't add any blocking calls. From the difference between `create_at` and `expect_run_at`, we can see that the system expects to execute it after 10 seconds. Wait for 10 seconds and check this bee again:\n\n```elixir\niex\u003e Honeycomb.bee :my_honeycomb, \"delay-run\"\n%Honeycomb.Bee{\n  name: \"delay-run\",\n  status: :done,\n  run: #Function\u003c43.105768164/0 in :erl_eval.expr/6\u003e,\n  create_at: ~U[2024-05-07 22:36:35.216562Z],\n  expect_run_at: ~U[2024-05-07 22:36:45.216562Z],\n  work_start_at: ~U[2024-05-07 22:36:45.221104Z],\n  work_end_at: ~U[2024-05-07 22:36:45.221107Z],\n  stateless: false,\n  result: :ok\n}\n```\n\nPerfect, the task was executed on time after 10 seconds. However, if there are concurrency restrictions in the Honeycomb system, it may not be executed on time. You can also call the `Honeycomb.gather_honey_after/5` function, which directly passes a numeric value as the delay time, which is more convenient.\n\n### Stateless Tasks\n\nIf you just want to use Honeycomb's asynchronous execution, concurrency control, and other features, and the result of your task is not important, you can set the task as `stateless`. All stateless tasks clean themselves up after execution, but they still exist before the run ends. Here's an example:\n\n```elixir\niex\u003e Honeycomb.gather_honey :my_honeycomb, \"sleep-5\", fn -\u003e :timer.sleep(5000) end, stateless: true\n{:ok,\n %Honeycomb.Bee{\n   name: \"sleep-5\",\n   status: :pending,\n   run: #Function\u003c43.105768164/0 in :erl_eval.expr/6\u003e,\n   create_at: ~U[2024-05-07 22:43:35.516373Z],\n   expect_run_at: ~U[2024-05-07 22:43:35.516373Z],\n   work_start_at: nil,\n   work_end_at: nil,\n   stateless: true,\n   result: nil\n }}\n```\n\nWithin 5 seconds, we can still query this bee:\n\n```elixir\niex\u003e Honeycomb.bee :my_honeycomb, \"sleep-5\"\n%Honeycomb.Bee{\n  name: \"sleep-5\",\n  status: :running,\n  run: #Function\u003c43.105768164/0 in :erl_eval.expr/6\u003e,\n  create_at: ~U[2024-05-07 22:43:35.516373Z],\n  expect_run_at: ~U[2024-05-07 22:43:35.516373Z],\n  work_start_at: ~U[2024-05-07 22:43:35.517164Z],\n  work_end_at: nil,\n  stateless: true,\n  result: nil\n}\n```\n\nBut after 5 seconds (execution ends), this bee is automatically cleaned up:\n\n```elixir\niex\u003e Honeycomb.bee :my_honeycomb, \"sleep-5\"\nnil\n```\n\nStateless tasks can avoid the hassle of having to call `Honeycomb.harvest_honey/2` to actively clean up each time, providing convenience for tasks that don't care about the result.\n\n### Multiple Services\n\nAs mentioned above, you can create multiple Honeycomb systems and use them independently. For example:\n\n```elixir\ndefmodule YourProject.AnotherQueen do\n  use Honeycomb.Queen, id: :another, concurrency: 10\nend\n```\n\n```elixir\nchildren = [\n  # Omit other processes...\n  {Honeycomb, queen: YourProject.Queen},\n  {Honeycomb, queen: YourProject.AnotherQueen},\n]\n\nopts = [strategy: :one_for_one, name: YourProject.Supervisor]\nSupervisor.start_link(children, opts)\n```\n\nThe above code creates two Honeycomb systems, where `my_honeycomb` does not limit the number of concurrencies, while `another` only allows 10 tasks to be performed simultaneously. They can be used for different occasions in a targeted manner.\n\n### Log Metadata\n\nSometimes you're not sure if the content in the log comes from Honeycomb or which Honeycomb system it comes from. You can add the honeycomb field to the log metadata as follows:\n\n```elixir\nconfig :logger, :console,\n  # Omitting other configurations...\n  metadata: [:honeycomb]\n```\n\nLogs from Honeycomb will include the `id` of the queen, with the effect:\n\n```plaintext\nhoneycomb=my_honeycomb [debug] retry bee: -576460752303423484\nhoneycomb=another [debug] retry bee: -576460752303423487\n```\n\n## Conclusion\n\nThe design of the Honeycomb library is not limited to this. It also has an unfinished [roadmap](https://github.com/Hentioe/honeycomb/issues/1). This library is a personal summary of some experiences in certain scenarios, and it definitely doesn't suit all occasions. However, in limited scenarios, I will also make it as stable and reliable as possible and support more peripheral facilities (such as rate limiting and storage backends).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FHentioe%2Fhoneycomb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FHentioe%2Fhoneycomb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FHentioe%2Fhoneycomb/lists"}