CINXE.COM
GitHub - RichardKnop/machinery: Machinery is an asynchronous task queue/job queue based on distributed message passing.
<!DOCTYPE html> <html lang="en" data-color-mode="auto" data-light-theme="light" data-dark-theme="dark" data-a11y-animated-images="system" data-a11y-link-underlines="true" > <head> <meta charset="utf-8"> <link rel="dns-prefetch" href="https://github.githubassets.com"> <link rel="dns-prefetch" href="https://avatars.githubusercontent.com"> <link rel="dns-prefetch" href="https://github-cloud.s3.amazonaws.com"> <link rel="dns-prefetch" href="https://user-images.githubusercontent.com/"> <link rel="preconnect" href="https://github.githubassets.com" crossorigin> <link rel="preconnect" href="https://avatars.githubusercontent.com"> <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/light-74231a1f3bbb.css" /><link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/dark-8a995f0bacd4.css" /><link data-color-theme="dark_dimmed" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_dimmed-f37fb7684b1f.css" /><link data-color-theme="dark_high_contrast" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_high_contrast-9ac301c3ebe5.css" /><link data-color-theme="dark_colorblind" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_colorblind-cd826e8636dc.css" /><link data-color-theme="light_colorblind" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/light_colorblind-f91b0f603451.css" /><link data-color-theme="light_high_contrast" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/light_high_contrast-83beb16e0ecf.css" /><link data-color-theme="light_tritanopia" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/light_tritanopia-6e122dab64fc.css" /><link data-color-theme="dark_tritanopia" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_tritanopia-18119e682df0.css" /> <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-primitives-225433424a87.css" /> <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-aaa714e5674d.css" /> <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/global-7eaba1d4847c.css" /> <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/github-43ae85d4871b.css" /> <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/repository-4fce88777fa8.css" /> <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/code-0210be90f4d3.css" /> <script type="application/json" id="client-env">{"locale":"en","featureFlags":["a11y_quote_reply_fix","copilot_immersive_issue_preview","copilot_new_references_ui","copilot_chat_repo_custom_instructions_preview","copilot_no_floating_button","copilot_topics_as_references","copilot_read_shared_conversation","copilot_duplicate_thread","copilot_buffered_streaming","dotcom_chat_client_side_skills","experimentation_azure_variant_endpoint","failbot_handle_non_errors","fgpat_form_ui_updates","geojson_azure_maps","ghost_pilot_confidence_truncation_25","ghost_pilot_confidence_truncation_40","github_models_o3_mini_streaming","hovercard_accessibility","insert_before_patch","issues_react_remove_placeholders","issues_react_blur_item_picker_on_close","marketing_pages_search_explore_provider","primer_react_css_modules_ga","react_data_router_pull_requests","remove_child_patch","sample_network_conn_type","swp_enterprise_contact_form","site_proxima_australia_update","viewscreen_sandbox","issues_react_create_milestone","issues_react_cache_fix_workaround","lifecycle_label_name_updates","copilot_task_oriented_assistive_prompts","issues_react_assignee_warning","issue_types_prevent_private_type_creation","refresh_image_video_src","react_router_dispose_on_disconnect","turbo_app_id_restore"]}</script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/wp-runtime-f95ccd92d17e.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_oddbird_popover-polyfill_dist_popover_js-9da652f58479.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_arianotify-polyfill_ariaNotify-polyfill_js-node_modules_github_mi-3abb8f-46b9f4874d95.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/ui_packages_failbot_failbot_ts-75968cfb5298.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/environment-f04cb2a9fc8c.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_primer_behaviors_dist_esm_index_mjs-0dbb79f97f8f.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_selector-observer_dist_index_esm_js-f690fd9ae3d5.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_relative-time-element_dist_index_js-62d275b7ddd9.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_text-expander-element_dist_index_js-78748950cb0c.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_auto-complete-element_dist_index_js-node_modules_github_catalyst_-8e9f78-a90ac05d2469.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_filter-input-element_dist_index_js-node_modules_github_remote-inp-b5f1d7-a1760ffda83d.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_markdown-toolbar-element_dist_index_js-ceef33f593fa.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_file-attachment-element_dist_index_js-node_modules_primer_view-co-c44a69-8c52cf4cd0d3.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/github-elements-394f8eb34f19.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/element-registry-e0a42d158bcc.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_braintree_browser-detection_dist_browser-detection_js-node_modules_githu-2906d7-2a07a295af40.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_lit-html_lit-html_js-be8cb88f481b.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_mini-throttle_dist_index_js-node_modules_morphdom_dist_morphdom-e-7c534c-a4a1922eb55f.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_turbo_dist_turbo_es2017-esm_js-a03ee12d659a.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_remote-form_dist_index_js-node_modules_delegated-events_dist_inde-893f9f-b6294cf703b7.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_color-convert_index_js-e3180fe3bcb3.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_quote-selection_dist_index_js-node_modules_github_session-resume_-947061-e7a6c4a19f98.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/ui_packages_updatable-content_updatable-content_ts-2a55124d5c52.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/app_assets_modules_github_behaviors_task-list_ts-app_assets_modules_github_sso_ts-ui_packages-900dde-768abe60b1f8.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/app_assets_modules_github_sticky-scroll-into-view_ts-3e000c5d31a9.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/app_assets_modules_github_behaviors_ajax-error_ts-app_assets_modules_github_behaviors_include-87a4ae-8be71414579a.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/app_assets_modules_github_behaviors_commenting_edit_ts-app_assets_modules_github_behaviors_ht-83c235-e429cff6ceb1.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/behaviors-7ebb6421bf22.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_delegated-events_dist_index_js-node_modules_github_catalyst_lib_index_js-f6223d90c7ba.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/notifications-global-01e85cd1be94.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_virtualized-list_es_index_js-node_modules_github_template-parts_lib_index_js-94dc7a2157c1.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_remote-form_dist_index_js-node_modules_delegated-events_dist_inde-70450e-4b93df70b903.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/app_assets_modules_github_ref-selector_ts-3e9d848bab5f.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/codespaces-c3bcacfe317c.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_filter-input-element_dist_index_js-node_modules_github_remote-inp-3eebbd-0763620ad7bf.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_mini-throttle_dist_decorators_js-node_modules_delegated-events_di-e161aa-9d41fb1b6c9e.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_file-attachment-element_dist_index_js-node_modules_github_remote--3c9c82-b71ef90fbdc7.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/repositories-7a0dbaa42c57.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_mini-throttle_dist_index_js-node_modules_github_catalyst_lib_inde-dbbea9-26cce2010167.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/code-menu-1c0aedc134b1.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/primer-react-e05a7c4c5398.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/react-core-aaa76995a864.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/react-lib-f1bca44e0926.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/octicons-react-cf2f2ab8dab4.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_emotion_is-prop-valid_dist_emotion-is-prop-valid_esm_js-node_modules_emo-62da9f-2df2f32ec596.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_mini-throttle_dist_index_js-node_modules_stacktrace-parser_dist_s-e7dcdd-9a233856b02c.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_oddbird_popover-polyfill_dist_popover-fn_js-55fea94174bf.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/notifications-subscriptions-menu-58a0c58bfee4.js"></script> <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-react.e0c9f0687c56358ed85e.module.css" /> <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/notifications-subscriptions-menu.1bcff9205c241e99cff2.module.css" /> <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-react.e0c9f0687c56358ed85e.module.css" /> <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/notifications-subscriptions-menu.1bcff9205c241e99cff2.module.css" /> <title>GitHub - RichardKnop/machinery: Machinery is an asynchronous task queue/job queue based on distributed message passing.</title> <meta name="route-pattern" content="/:user_id/:repository" data-turbo-transient> <meta name="route-controller" content="files" data-turbo-transient> <meta name="route-action" content="disambiguate" data-turbo-transient> <meta name="current-catalog-service-hash" content="f3abb0cc802f3d7b95fc8762b94bdcb13bf39634c40c357301c4aa1d67a256fb"> <meta name="request-id" content="9210:127817:8FC34A:A4632C:67E2AF09" data-pjax-transient="true"/><meta name="html-safe-nonce" content="d3e25b2948de91ad18cb8e33f1aa901a0700d94976f965f31b98919213e12cc1" data-pjax-transient="true"/><meta name="visitor-payload" content="eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI5MjEwOjEyNzgxNzo4RkMzNEE6QTQ2MzJDOjY3RTJBRjA5IiwidmlzaXRvcl9pZCI6IjI3MzQzNzk2MzkyNzIyODgwMDkiLCJyZWdpb25fZWRnZSI6InNvdXRoZWFzdGFzaWEiLCJyZWdpb25fcmVuZGVyIjoic291dGhlYXN0YXNpYSJ9" data-pjax-transient="true"/><meta name="visitor-hmac" content="91d3462340f42c3a4bcf5ee52909b45c0adbd6d0309e350d9324611fd5e9a97e" data-pjax-transient="true"/> <meta name="hovercard-subject-tag" content="repository:33452903" data-turbo-transient> <meta name="github-keyboard-shortcuts" content="repository,copilot" data-turbo-transient="true" /> <meta name="selected-link" value="repo_source" data-turbo-transient> <link rel="assets" href="https://github.githubassets.com/"> <meta name="google-site-verification" content="Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I"> <meta name="octolytics-url" content="https://collector.github.com/github/collect" /> <meta name="analytics-location" content="/<user-name>/<repo-name>" data-turbo-transient="true" /> <meta name="user-login" content=""> <meta name="viewport" content="width=device-width"> <meta name="description" content="Machinery is an asynchronous task queue/job queue based on distributed message passing. - RichardKnop/machinery"> <link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="GitHub"> <link rel="fluid-icon" href="https://github.com/fluidicon.png" title="GitHub"> <meta property="fb:app_id" content="1401488693436528"> <meta name="apple-itunes-app" content="app-id=1477376905, app-argument=https://github.com/RichardKnop/machinery" /> <meta name="twitter:image" content="https://opengraph.githubassets.com/8758dcf59b321b3d85f1398dfb464f0580650882a932071f90722201e1518a9a/RichardKnop/machinery" /><meta name="twitter:site" content="@github" /><meta name="twitter:card" content="summary_large_image" /><meta name="twitter:title" content="GitHub - RichardKnop/machinery: Machinery is an asynchronous task queue/job queue based on distributed message passing." /><meta name="twitter:description" content="Machinery is an asynchronous task queue/job queue based on distributed message passing. - RichardKnop/machinery" /> <meta property="og:image" content="https://opengraph.githubassets.com/8758dcf59b321b3d85f1398dfb464f0580650882a932071f90722201e1518a9a/RichardKnop/machinery" /><meta property="og:image:alt" content="Machinery is an asynchronous task queue/job queue based on distributed message passing. - RichardKnop/machinery" /><meta property="og:image:width" content="1200" /><meta property="og:image:height" content="600" /><meta property="og:site_name" content="GitHub" /><meta property="og:type" content="object" /><meta property="og:title" content="GitHub - RichardKnop/machinery: Machinery is an asynchronous task queue/job queue based on distributed message passing." /><meta property="og:url" content="https://github.com/RichardKnop/machinery" /><meta property="og:description" content="Machinery is an asynchronous task queue/job queue based on distributed message passing. - RichardKnop/machinery" /> <meta name="hostname" content="github.com"> <meta name="expected-hostname" content="github.com"> <meta http-equiv="x-pjax-version" content="0373827985631dee8709153c8c7d01da5e66a3572e60e11a31c7c31e992e814c" data-turbo-track="reload"> <meta http-equiv="x-pjax-csp-version" content="77190eb53eb47fc30bd2fcc17a7eefa2dfd8505869fee9299ba911be3a40a9eb" data-turbo-track="reload"> <meta http-equiv="x-pjax-css-version" content="1994cd18701e16e6efa87d97f308447f5b0f15b7ae2b58d73f3d026c94bd5edd" data-turbo-track="reload"> <meta http-equiv="x-pjax-js-version" content="750eb3203f04d40fb8e86ef127b4d0018d41e873b43fe8b65e463c5a0415a53d" data-turbo-track="reload"> <meta name="turbo-cache-control" content="no-preview" data-turbo-transient=""> <meta data-hydrostats="publish"> <meta name="go-import" content="github.com/RichardKnop/machinery git https://github.com/RichardKnop/machinery.git"> <meta name="octolytics-dimension-user_id" content="2325888" /><meta name="octolytics-dimension-user_login" content="RichardKnop" /><meta name="octolytics-dimension-repository_id" content="33452903" /><meta name="octolytics-dimension-repository_nwo" content="RichardKnop/machinery" /><meta name="octolytics-dimension-repository_public" content="true" /><meta name="octolytics-dimension-repository_is_fork" content="false" /><meta name="octolytics-dimension-repository_network_root_id" content="33452903" /><meta name="octolytics-dimension-repository_network_root_nwo" content="RichardKnop/machinery" /> <link rel="canonical" href="https://github.com/RichardKnop/machinery" data-turbo-transient> <meta name="turbo-body-classes" content="logged-out env-production page-responsive"> <meta name="browser-stats-url" content="https://api.github.com/_private/browser/stats"> <meta name="browser-errors-url" content="https://api.github.com/_private/browser/errors"> <meta name="release" content="ab6bc2273251acdc9786a8e36c19b92fcf05ef19"> <link rel="mask-icon" href="https://github.githubassets.com/assets/pinned-octocat-093da3e6fa40.svg" color="#000000"> <link rel="alternate icon" class="js-site-favicon" type="image/png" href="https://github.githubassets.com/favicons/favicon.png"> <link rel="icon" class="js-site-favicon" type="image/svg+xml" href="https://github.githubassets.com/favicons/favicon.svg" data-base-href="https://github.githubassets.com/favicons/favicon"> <meta name="theme-color" content="#1e2327"> <meta name="color-scheme" content="light dark" /> <link rel="manifest" href="/manifest.json" crossOrigin="use-credentials"> </head> <body class="logged-out env-production page-responsive" style="word-wrap: break-word;"> <div data-turbo-body class="logged-out env-production page-responsive" style="word-wrap: break-word;"> <div class="position-relative header-wrapper js-header-wrapper "> <a href="#start-of-content" data-skip-target-assigned="false" class="px-2 py-4 color-bg-accent-emphasis color-fg-on-emphasis show-on-focus js-skip-to-content">Skip to content</a> <span data-view-component="true" class="progress-pjax-loader Progress position-fixed width-full"> <span style="width: 0%;" data-view-component="true" class="Progress-item progress-pjax-loader-bar left-0 top-0 color-bg-accent-emphasis"></span> </span> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/ui_packages_ui-commands_ui-commands_ts-8c874fb594e9.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/keyboard-shortcuts-dialog-33dfb803e078.js"></script> <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-react.e0c9f0687c56358ed85e.module.css" /> <react-partial partial-name="keyboard-shortcuts-dialog" data-ssr="false" data-attempted-ssr="false" > <script type="application/json" data-target="react-partial.embeddedData">{"props":{"docsUrl":"https://docs.github.com/get-started/accessibility/keyboard-shortcuts"}}</script> <div data-target="react-partial.reactRoot"></div> </react-partial> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_github_remote-form_dist_index_js-node_modules_delegated-events_dist_inde-94fd67-4898d1bf4b51.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/sessions-730dca81d0a2.js"></script> <header class="HeaderMktg header-logged-out js-details-container js-header Details f4 py-3" role="banner" data-is-top="true" data-color-mode=light data-light-theme=light data-dark-theme=dark> <h2 class="sr-only">Navigation Menu</h2> <button type="button" class="HeaderMktg-backdrop d-lg-none border-0 position-fixed top-0 left-0 width-full height-full js-details-target" aria-label="Toggle navigation"> <span class="d-none">Toggle navigation</span> </button> <div class="d-flex flex-column flex-lg-row flex-items-center px-3 px-md-4 px-lg-5 height-full position-relative z-1"> <div class="d-flex flex-justify-between flex-items-center width-full width-lg-auto"> <div class="flex-1"> <button aria-label="Toggle navigation" aria-expanded="false" type="button" data-view-component="true" class="js-details-target js-nav-padding-recalculate js-header-menu-toggle Button--link Button--medium Button d-lg-none color-fg-inherit p-1"> <span class="Button-content"> <span class="Button-label"><div class="HeaderMenu-toggle-bar rounded my-1"></div> <div class="HeaderMenu-toggle-bar rounded my-1"></div> <div class="HeaderMenu-toggle-bar rounded my-1"></div></span> </span> </button> </div> <a class="mr-lg-3 color-fg-inherit flex-order-2 js-prevent-focus-on-mobile-nav" href="/" aria-label="Homepage" data-analytics-event="{"category":"Marketing nav","action":"click to go to homepage","label":"ref_page:Marketing;ref_cta:Logomark;ref_loc:Header"}"> <svg height="32" aria-hidden="true" viewBox="0 0 24 24" version="1.1" width="32" data-view-component="true" class="octicon octicon-mark-github"> <path d="M12 1C5.9225 1 1 5.9225 1 12C1 16.8675 4.14875 20.9787 8.52125 22.4362C9.07125 22.5325 9.2775 22.2025 9.2775 21.9137C9.2775 21.6525 9.26375 20.7862 9.26375 19.865C6.5 20.3737 5.785 19.1912 5.565 18.5725C5.44125 18.2562 4.905 17.28 4.4375 17.0187C4.0525 16.8125 3.5025 16.3037 4.42375 16.29C5.29 16.2762 5.90875 17.0875 6.115 17.4175C7.105 19.0812 8.68625 18.6137 9.31875 18.325C9.415 17.61 9.70375 17.1287 10.02 16.8537C7.5725 16.5787 5.015 15.63 5.015 11.4225C5.015 10.2262 5.44125 9.23625 6.1425 8.46625C6.0325 8.19125 5.6475 7.06375 6.2525 5.55125C6.2525 5.55125 7.17375 5.2625 9.2775 6.67875C10.1575 6.43125 11.0925 6.3075 12.0275 6.3075C12.9625 6.3075 13.8975 6.43125 14.7775 6.67875C16.8813 5.24875 17.8025 5.55125 17.8025 5.55125C18.4075 7.06375 18.0225 8.19125 17.9125 8.46625C18.6138 9.23625 19.04 10.2125 19.04 11.4225C19.04 15.6437 16.4688 16.5787 14.0213 16.8537C14.42 17.1975 14.7638 17.8575 14.7638 18.8887C14.7638 20.36 14.75 21.5425 14.75 21.9137C14.75 22.2025 14.9563 22.5462 15.5063 22.4362C19.8513 20.9787 23 16.8537 23 12C23 5.9225 18.0775 1 12 1Z"></path> </svg> </a> <div class="flex-1 flex-order-2 text-right"> <a href="/login?return_to=https%3A%2F%2Fgithub.com%2FRichardKnop%2Fmachinery" class="HeaderMenu-link HeaderMenu-button d-inline-flex d-lg-none flex-order-1 f5 no-underline border color-border-default rounded-2 px-2 py-1 color-fg-inherit js-prevent-focus-on-mobile-nav" data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"site header menu","repository_id":null,"auth_type":"SIGN_UP","originating_url":"https://github.com/RichardKnop/machinery","user_id":null}}" data-hydro-click-hmac="3902138a0d67d5ba78d4829869475c71ae11ce417fdbba76fe96c76244a22e0c" data-analytics-event="{"category":"Marketing nav","action":"click to Sign in","label":"ref_page:Marketing;ref_cta:Sign in;ref_loc:Header"}" > Sign in </a> </div> </div> <div class="HeaderMenu js-header-menu height-fit position-lg-relative d-lg-flex flex-column flex-auto top-0"> <div class="HeaderMenu-wrapper d-flex flex-column flex-self-start flex-lg-row flex-auto rounded rounded-lg-0"> <nav class="HeaderMenu-nav" aria-label="Global"> <ul class="d-lg-flex list-style-none"> <li class="HeaderMenu-item position-relative flex-wrap flex-justify-between flex-items-center d-block d-lg-flex flex-lg-nowrap flex-lg-items-center js-details-container js-header-menu-item"> <button type="button" class="HeaderMenu-link border-0 width-full width-lg-auto px-0 px-lg-2 py-lg-2 no-wrap d-flex flex-items-center flex-justify-between js-details-target" aria-expanded="false"> Product <svg opacity="0.5" aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-chevron-down HeaderMenu-icon ml-1"> <path d="M12.78 5.22a.749.749 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.06 0L3.22 6.28a.749.749 0 1 1 1.06-1.06L8 8.939l3.72-3.719a.749.749 0 0 1 1.06 0Z"></path> </svg> </button> <div class="HeaderMenu-dropdown dropdown-menu rounded m-0 p-0 pt-2 pt-lg-4 position-relative position-lg-absolute left-0 left-lg-n3 pb-2 pb-lg-4 d-lg-flex flex-wrap dropdown-menu-wide"> <div class="HeaderMenu-column px-lg-4 border-lg-right mb-4 mb-lg-0 pr-lg-7"> <div class="border-bottom pb-3 pb-lg-0 border-lg-bottom-0"> <ul class="list-style-none f5" > <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary d-flex flex-items-center Link--has-description pb-lg-3" data-analytics-event="{"location":"navbar","action":"github_copilot","context":"product","tag":"link","label":"github_copilot_link_product_navbar"}" href="https://github.com/features/copilot"> <svg aria-hidden="true" height="24" viewBox="0 0 24 24" version="1.1" width="24" data-view-component="true" class="octicon octicon-copilot color-fg-subtle mr-3"> <path d="M23.922 16.992c-.861 1.495-5.859 5.023-11.922 5.023-6.063 0-11.061-3.528-11.922-5.023A.641.641 0 0 1 0 16.736v-2.869a.841.841 0 0 1 .053-.22c.372-.935 1.347-2.292 2.605-2.656.167-.429.414-1.055.644-1.517a10.195 10.195 0 0 1-.052-1.086c0-1.331.282-2.499 1.132-3.368.397-.406.89-.717 1.474-.952 1.399-1.136 3.392-2.093 6.122-2.093 2.731 0 4.767.957 6.166 2.093.584.235 1.077.546 1.474.952.85.869 1.132 2.037 1.132 3.368 0 .368-.014.733-.052 1.086.23.462.477 1.088.644 1.517 1.258.364 2.233 1.721 2.605 2.656a.832.832 0 0 1 .053.22v2.869a.641.641 0 0 1-.078.256ZM12.172 11h-.344a4.323 4.323 0 0 1-.355.508C10.703 12.455 9.555 13 7.965 13c-1.725 0-2.989-.359-3.782-1.259a2.005 2.005 0 0 1-.085-.104L4 11.741v6.585c1.435.779 4.514 2.179 8 2.179 3.486 0 6.565-1.4 8-2.179v-6.585l-.098-.104s-.033.045-.085.104c-.793.9-2.057 1.259-3.782 1.259-1.59 0-2.738-.545-3.508-1.492a4.323 4.323 0 0 1-.355-.508h-.016.016Zm.641-2.935c.136 1.057.403 1.913.878 2.497.442.544 1.134.938 2.344.938 1.573 0 2.292-.337 2.657-.751.384-.435.558-1.15.558-2.361 0-1.14-.243-1.847-.705-2.319-.477-.488-1.319-.862-2.824-1.025-1.487-.161-2.192.138-2.533.529-.269.307-.437.808-.438 1.578v.021c0 .265.021.562.063.893Zm-1.626 0c.042-.331.063-.628.063-.894v-.02c-.001-.77-.169-1.271-.438-1.578-.341-.391-1.046-.69-2.533-.529-1.505.163-2.347.537-2.824 1.025-.462.472-.705 1.179-.705 2.319 0 1.211.175 1.926.558 2.361.365.414 1.084.751 2.657.751 1.21 0 1.902-.394 2.344-.938.475-.584.742-1.44.878-2.497Z"></path><path d="M14.5 14.25a1 1 0 0 1 1 1v2a1 1 0 0 1-2 0v-2a1 1 0 0 1 1-1Zm-5 0a1 1 0 0 1 1 1v2a1 1 0 0 1-2 0v-2a1 1 0 0 1 1-1Z"></path> </svg> <div> <div class="color-fg-default h4">GitHub Copilot</div> Write better code with AI </div> </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary d-flex flex-items-center Link--has-description pb-lg-3" data-analytics-event="{"location":"navbar","action":"security","context":"product","tag":"link","label":"security_link_product_navbar"}" href="https://github.com/features/security"> <svg aria-hidden="true" height="24" viewBox="0 0 24 24" version="1.1" width="24" data-view-component="true" class="octicon octicon-shield-check color-fg-subtle mr-3"> <path d="M16.53 9.78a.75.75 0 0 0-1.06-1.06L11 13.19l-1.97-1.97a.75.75 0 0 0-1.06 1.06l2.5 2.5a.75.75 0 0 0 1.06 0l5-5Z"></path><path d="m12.54.637 8.25 2.675A1.75 1.75 0 0 1 22 4.976V10c0 6.19-3.771 10.704-9.401 12.83a1.704 1.704 0 0 1-1.198 0C5.77 20.705 2 16.19 2 10V4.976c0-.758.489-1.43 1.21-1.664L11.46.637a1.748 1.748 0 0 1 1.08 0Zm-.617 1.426-8.25 2.676a.249.249 0 0 0-.173.237V10c0 5.46 3.28 9.483 8.43 11.426a.199.199 0 0 0 .14 0C17.22 19.483 20.5 15.461 20.5 10V4.976a.25.25 0 0 0-.173-.237l-8.25-2.676a.253.253 0 0 0-.154 0Z"></path> </svg> <div> <div class="color-fg-default h4">Security</div> Find and fix vulnerabilities </div> </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary d-flex flex-items-center Link--has-description pb-lg-3" data-analytics-event="{"location":"navbar","action":"actions","context":"product","tag":"link","label":"actions_link_product_navbar"}" href="https://github.com/features/actions"> <svg aria-hidden="true" height="24" viewBox="0 0 24 24" version="1.1" width="24" data-view-component="true" class="octicon octicon-workflow color-fg-subtle mr-3"> <path d="M1 3a2 2 0 0 1 2-2h6.5a2 2 0 0 1 2 2v6.5a2 2 0 0 1-2 2H7v4.063C7 16.355 7.644 17 8.438 17H12.5v-2.5a2 2 0 0 1 2-2H21a2 2 0 0 1 2 2V21a2 2 0 0 1-2 2h-6.5a2 2 0 0 1-2-2v-2.5H8.437A2.939 2.939 0 0 1 5.5 15.562V11.5H3a2 2 0 0 1-2-2Zm2-.5a.5.5 0 0 0-.5.5v6.5a.5.5 0 0 0 .5.5h6.5a.5.5 0 0 0 .5-.5V3a.5.5 0 0 0-.5-.5ZM14.5 14a.5.5 0 0 0-.5.5V21a.5.5 0 0 0 .5.5H21a.5.5 0 0 0 .5-.5v-6.5a.5.5 0 0 0-.5-.5Z"></path> </svg> <div> <div class="color-fg-default h4">Actions</div> Automate any workflow </div> </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary d-flex flex-items-center Link--has-description pb-lg-3" data-analytics-event="{"location":"navbar","action":"codespaces","context":"product","tag":"link","label":"codespaces_link_product_navbar"}" href="https://github.com/features/codespaces"> <svg aria-hidden="true" height="24" viewBox="0 0 24 24" version="1.1" width="24" data-view-component="true" class="octicon octicon-codespaces color-fg-subtle mr-3"> <path d="M3.5 3.75C3.5 2.784 4.284 2 5.25 2h13.5c.966 0 1.75.784 1.75 1.75v7.5A1.75 1.75 0 0 1 18.75 13H5.25a1.75 1.75 0 0 1-1.75-1.75Zm-2 12c0-.966.784-1.75 1.75-1.75h17.5c.966 0 1.75.784 1.75 1.75v4a1.75 1.75 0 0 1-1.75 1.75H3.25a1.75 1.75 0 0 1-1.75-1.75ZM5.25 3.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h13.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Zm-2 12a.25.25 0 0 0-.25.25v4c0 .138.112.25.25.25h17.5a.25.25 0 0 0 .25-.25v-4a.25.25 0 0 0-.25-.25Z"></path><path d="M10 17.75a.75.75 0 0 1 .75-.75h6.5a.75.75 0 0 1 0 1.5h-6.5a.75.75 0 0 1-.75-.75Zm-4 0a.75.75 0 0 1 .75-.75h.5a.75.75 0 0 1 0 1.5h-.5a.75.75 0 0 1-.75-.75Z"></path> </svg> <div> <div class="color-fg-default h4">Codespaces</div> Instant dev environments </div> </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary d-flex flex-items-center Link--has-description pb-lg-3" data-analytics-event="{"location":"navbar","action":"issues","context":"product","tag":"link","label":"issues_link_product_navbar"}" href="https://github.com/features/issues"> <svg aria-hidden="true" height="24" viewBox="0 0 24 24" version="1.1" width="24" data-view-component="true" class="octicon octicon-issue-opened color-fg-subtle mr-3"> <path d="M12 1c6.075 0 11 4.925 11 11s-4.925 11-11 11S1 18.075 1 12 5.925 1 12 1ZM2.5 12a9.5 9.5 0 0 0 9.5 9.5 9.5 9.5 0 0 0 9.5-9.5A9.5 9.5 0 0 0 12 2.5 9.5 9.5 0 0 0 2.5 12Zm9.5 2a2 2 0 1 1-.001-3.999A2 2 0 0 1 12 14Z"></path> </svg> <div> <div class="color-fg-default h4">Issues</div> Plan and track work </div> </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary d-flex flex-items-center Link--has-description pb-lg-3" data-analytics-event="{"location":"navbar","action":"code_review","context":"product","tag":"link","label":"code_review_link_product_navbar"}" href="https://github.com/features/code-review"> <svg aria-hidden="true" height="24" viewBox="0 0 24 24" version="1.1" width="24" data-view-component="true" class="octicon octicon-code-review color-fg-subtle mr-3"> <path d="M10.3 6.74a.75.75 0 0 1-.04 1.06l-2.908 2.7 2.908 2.7a.75.75 0 1 1-1.02 1.1l-3.5-3.25a.75.75 0 0 1 0-1.1l3.5-3.25a.75.75 0 0 1 1.06.04Zm3.44 1.06a.75.75 0 1 1 1.02-1.1l3.5 3.25a.75.75 0 0 1 0 1.1l-3.5 3.25a.75.75 0 1 1-1.02-1.1l2.908-2.7-2.908-2.7Z"></path><path d="M1.5 4.25c0-.966.784-1.75 1.75-1.75h17.5c.966 0 1.75.784 1.75 1.75v12.5a1.75 1.75 0 0 1-1.75 1.75h-9.69l-3.573 3.573A1.458 1.458 0 0 1 5 21.043V18.5H3.25a1.75 1.75 0 0 1-1.75-1.75ZM3.25 4a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h2.5a.75.75 0 0 1 .75.75v3.19l3.72-3.72a.749.749 0 0 1 .53-.22h10a.25.25 0 0 0 .25-.25V4.25a.25.25 0 0 0-.25-.25Z"></path> </svg> <div> <div class="color-fg-default h4">Code Review</div> Manage code changes </div> </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary d-flex flex-items-center Link--has-description pb-lg-3" data-analytics-event="{"location":"navbar","action":"discussions","context":"product","tag":"link","label":"discussions_link_product_navbar"}" href="https://github.com/features/discussions"> <svg aria-hidden="true" height="24" viewBox="0 0 24 24" version="1.1" width="24" data-view-component="true" class="octicon octicon-comment-discussion color-fg-subtle mr-3"> <path d="M1.75 1h12.5c.966 0 1.75.784 1.75 1.75v9.5A1.75 1.75 0 0 1 14.25 14H8.061l-2.574 2.573A1.458 1.458 0 0 1 3 15.543V14H1.75A1.75 1.75 0 0 1 0 12.25v-9.5C0 1.784.784 1 1.75 1ZM1.5 2.75v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25H1.75a.25.25 0 0 0-.25.25Z"></path><path d="M22.5 8.75a.25.25 0 0 0-.25-.25h-3.5a.75.75 0 0 1 0-1.5h3.5c.966 0 1.75.784 1.75 1.75v9.5A1.75 1.75 0 0 1 22.25 20H21v1.543a1.457 1.457 0 0 1-2.487 1.03L15.939 20H10.75A1.75 1.75 0 0 1 9 18.25v-1.465a.75.75 0 0 1 1.5 0v1.465c0 .138.112.25.25.25h5.5a.75.75 0 0 1 .53.22l2.72 2.72v-2.19a.75.75 0 0 1 .75-.75h2a.25.25 0 0 0 .25-.25v-9.5Z"></path> </svg> <div> <div class="color-fg-default h4">Discussions</div> Collaborate outside of code </div> </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary d-flex flex-items-center Link--has-description" data-analytics-event="{"location":"navbar","action":"code_search","context":"product","tag":"link","label":"code_search_link_product_navbar"}" href="https://github.com/features/code-search"> <svg aria-hidden="true" height="24" viewBox="0 0 24 24" version="1.1" width="24" data-view-component="true" class="octicon octicon-code-square color-fg-subtle mr-3"> <path d="M10.3 8.24a.75.75 0 0 1-.04 1.06L7.352 12l2.908 2.7a.75.75 0 1 1-1.02 1.1l-3.5-3.25a.75.75 0 0 1 0-1.1l3.5-3.25a.75.75 0 0 1 1.06.04Zm3.44 1.06a.75.75 0 1 1 1.02-1.1l3.5 3.25a.75.75 0 0 1 0 1.1l-3.5 3.25a.75.75 0 1 1-1.02-1.1l2.908-2.7-2.908-2.7Z"></path><path d="M2 3.75C2 2.784 2.784 2 3.75 2h16.5c.966 0 1.75.784 1.75 1.75v16.5A1.75 1.75 0 0 1 20.25 22H3.75A1.75 1.75 0 0 1 2 20.25Zm1.75-.25a.25.25 0 0 0-.25.25v16.5c0 .138.112.25.25.25h16.5a.25.25 0 0 0 .25-.25V3.75a.25.25 0 0 0-.25-.25Z"></path> </svg> <div> <div class="color-fg-default h4">Code Search</div> Find more, search less </div> </a></li> </ul> </div> </div> <div class="HeaderMenu-column px-lg-4"> <div class="border-bottom pb-3 pb-lg-0 border-lg-bottom-0 border-bottom-0"> <span class="d-block h4 color-fg-default my-1" id="product-explore-heading">Explore</span> <ul class="list-style-none f5" aria-labelledby="product-explore-heading"> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"all_features","context":"product","tag":"link","label":"all_features_link_product_navbar"}" href="https://github.com/features"> All features </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary Link--external" target="_blank" data-analytics-event="{"location":"navbar","action":"documentation","context":"product","tag":"link","label":"documentation_link_product_navbar"}" href="https://docs.github.com"> Documentation <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-link-external HeaderMenu-external-icon color-fg-subtle"> <path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path> </svg> </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary Link--external" target="_blank" data-analytics-event="{"location":"navbar","action":"github_skills","context":"product","tag":"link","label":"github_skills_link_product_navbar"}" href="https://skills.github.com"> GitHub Skills <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-link-external HeaderMenu-external-icon color-fg-subtle"> <path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path> </svg> </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary Link--external" target="_blank" data-analytics-event="{"location":"navbar","action":"blog","context":"product","tag":"link","label":"blog_link_product_navbar"}" href="https://github.blog"> Blog <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-link-external HeaderMenu-external-icon color-fg-subtle"> <path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path> </svg> </a></li> </ul> </div> </div> </div> </li> <li class="HeaderMenu-item position-relative flex-wrap flex-justify-between flex-items-center d-block d-lg-flex flex-lg-nowrap flex-lg-items-center js-details-container js-header-menu-item"> <button type="button" class="HeaderMenu-link border-0 width-full width-lg-auto px-0 px-lg-2 py-lg-2 no-wrap d-flex flex-items-center flex-justify-between js-details-target" aria-expanded="false"> Solutions <svg opacity="0.5" aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-chevron-down HeaderMenu-icon ml-1"> <path d="M12.78 5.22a.749.749 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.06 0L3.22 6.28a.749.749 0 1 1 1.06-1.06L8 8.939l3.72-3.719a.749.749 0 0 1 1.06 0Z"></path> </svg> </button> <div class="HeaderMenu-dropdown dropdown-menu rounded m-0 p-0 pt-2 pt-lg-4 position-relative position-lg-absolute left-0 left-lg-n3 d-lg-flex flex-wrap dropdown-menu-wide"> <div class="HeaderMenu-column px-lg-4 border-lg-right mb-4 mb-lg-0 pr-lg-7"> <div class="border-bottom pb-3 pb-lg-0 border-lg-bottom-0 pb-lg-3 mb-3 mb-lg-0"> <span class="d-block h4 color-fg-default my-1" id="solutions-by-company-size-heading">By company size</span> <ul class="list-style-none f5" aria-labelledby="solutions-by-company-size-heading"> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"enterprises","context":"solutions","tag":"link","label":"enterprises_link_solutions_navbar"}" href="https://github.com/enterprise"> Enterprises </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"small_and_medium_teams","context":"solutions","tag":"link","label":"small_and_medium_teams_link_solutions_navbar"}" href="https://github.com/team"> Small and medium teams </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"startups","context":"solutions","tag":"link","label":"startups_link_solutions_navbar"}" href="https://github.com/enterprise/startups"> Startups </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"nonprofits","context":"solutions","tag":"link","label":"nonprofits_link_solutions_navbar"}" href="/solutions/industry/nonprofits"> Nonprofits </a></li> </ul> </div> <div class="border-bottom pb-3 pb-lg-0 border-lg-bottom-0"> <span class="d-block h4 color-fg-default my-1" id="solutions-by-use-case-heading">By use case</span> <ul class="list-style-none f5" aria-labelledby="solutions-by-use-case-heading"> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"devsecops","context":"solutions","tag":"link","label":"devsecops_link_solutions_navbar"}" href="/solutions/use-case/devsecops"> DevSecOps </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"devops","context":"solutions","tag":"link","label":"devops_link_solutions_navbar"}" href="/solutions/use-case/devops"> DevOps </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"ci_cd","context":"solutions","tag":"link","label":"ci_cd_link_solutions_navbar"}" href="/solutions/use-case/ci-cd"> CI/CD </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"view_all_use_cases","context":"solutions","tag":"link","label":"view_all_use_cases_link_solutions_navbar"}" href="/solutions/use-case"> View all use cases </a></li> </ul> </div> </div> <div class="HeaderMenu-column px-lg-4"> <div class="border-bottom pb-3 pb-lg-0 border-lg-bottom-0"> <span class="d-block h4 color-fg-default my-1" id="solutions-by-industry-heading">By industry</span> <ul class="list-style-none f5" aria-labelledby="solutions-by-industry-heading"> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"healthcare","context":"solutions","tag":"link","label":"healthcare_link_solutions_navbar"}" href="/solutions/industry/healthcare"> Healthcare </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"financial_services","context":"solutions","tag":"link","label":"financial_services_link_solutions_navbar"}" href="/solutions/industry/financial-services"> Financial services </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"manufacturing","context":"solutions","tag":"link","label":"manufacturing_link_solutions_navbar"}" href="/solutions/industry/manufacturing"> Manufacturing </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"government","context":"solutions","tag":"link","label":"government_link_solutions_navbar"}" href="/solutions/industry/government"> Government </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"view_all_industries","context":"solutions","tag":"link","label":"view_all_industries_link_solutions_navbar"}" href="/solutions/industry"> View all industries </a></li> </ul> </div> </div> <div class="HeaderMenu-trailing-link rounded-bottom-2 flex-shrink-0 mt-lg-4 px-lg-4 py-4 py-lg-3 f5 text-semibold"> <a href="/solutions"> View all solutions <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-chevron-right HeaderMenu-trailing-link-icon"> <path d="M6.22 3.22a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L9.94 8 6.22 4.28a.75.75 0 0 1 0-1.06Z"></path> </svg> </a> </div> </div> </li> <li class="HeaderMenu-item position-relative flex-wrap flex-justify-between flex-items-center d-block d-lg-flex flex-lg-nowrap flex-lg-items-center js-details-container js-header-menu-item"> <button type="button" class="HeaderMenu-link border-0 width-full width-lg-auto px-0 px-lg-2 py-lg-2 no-wrap d-flex flex-items-center flex-justify-between js-details-target" aria-expanded="false"> Resources <svg opacity="0.5" aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-chevron-down HeaderMenu-icon ml-1"> <path d="M12.78 5.22a.749.749 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.06 0L3.22 6.28a.749.749 0 1 1 1.06-1.06L8 8.939l3.72-3.719a.749.749 0 0 1 1.06 0Z"></path> </svg> </button> <div class="HeaderMenu-dropdown dropdown-menu rounded m-0 p-0 pt-2 pt-lg-4 position-relative position-lg-absolute left-0 left-lg-n3 pb-2 pb-lg-4 d-lg-flex flex-wrap dropdown-menu-wide"> <div class="HeaderMenu-column px-lg-4 border-lg-right mb-4 mb-lg-0 pr-lg-7"> <div class="border-bottom pb-3 pb-lg-0 border-lg-bottom-0"> <span class="d-block h4 color-fg-default my-1" id="resources-topics-heading">Topics</span> <ul class="list-style-none f5" aria-labelledby="resources-topics-heading"> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"ai","context":"resources","tag":"link","label":"ai_link_resources_navbar"}" href="/resources/articles/ai"> AI </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"devops","context":"resources","tag":"link","label":"devops_link_resources_navbar"}" href="/resources/articles/devops"> DevOps </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"security","context":"resources","tag":"link","label":"security_link_resources_navbar"}" href="/resources/articles/security"> Security </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"software_development","context":"resources","tag":"link","label":"software_development_link_resources_navbar"}" href="/resources/articles/software-development"> Software Development </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"view_all","context":"resources","tag":"link","label":"view_all_link_resources_navbar"}" href="/resources/articles"> View all </a></li> </ul> </div> </div> <div class="HeaderMenu-column px-lg-4"> <div class="border-bottom pb-3 pb-lg-0 border-lg-bottom-0 border-bottom-0"> <span class="d-block h4 color-fg-default my-1" id="resources-explore-heading">Explore</span> <ul class="list-style-none f5" aria-labelledby="resources-explore-heading"> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary Link--external" target="_blank" data-analytics-event="{"location":"navbar","action":"learning_pathways","context":"resources","tag":"link","label":"learning_pathways_link_resources_navbar"}" href="https://resources.github.com/learn/pathways"> Learning Pathways <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-link-external HeaderMenu-external-icon color-fg-subtle"> <path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path> </svg> </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary Link--external" target="_blank" data-analytics-event="{"location":"navbar","action":"events_amp_webinars","context":"resources","tag":"link","label":"events_amp_webinars_link_resources_navbar"}" href="https://resources.github.com"> Events & Webinars <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-link-external HeaderMenu-external-icon color-fg-subtle"> <path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path> </svg> </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"ebooks_amp_whitepapers","context":"resources","tag":"link","label":"ebooks_amp_whitepapers_link_resources_navbar"}" href="https://github.com/resources/whitepapers"> Ebooks & Whitepapers </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"customer_stories","context":"resources","tag":"link","label":"customer_stories_link_resources_navbar"}" href="https://github.com/customer-stories"> Customer Stories </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary Link--external" target="_blank" data-analytics-event="{"location":"navbar","action":"partners","context":"resources","tag":"link","label":"partners_link_resources_navbar"}" href="https://partner.github.com"> Partners <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-link-external HeaderMenu-external-icon color-fg-subtle"> <path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path> </svg> </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"executive_insights","context":"resources","tag":"link","label":"executive_insights_link_resources_navbar"}" href="https://github.com/solutions/executive-insights"> Executive Insights </a></li> </ul> </div> </div> </div> </li> <li class="HeaderMenu-item position-relative flex-wrap flex-justify-between flex-items-center d-block d-lg-flex flex-lg-nowrap flex-lg-items-center js-details-container js-header-menu-item"> <button type="button" class="HeaderMenu-link border-0 width-full width-lg-auto px-0 px-lg-2 py-lg-2 no-wrap d-flex flex-items-center flex-justify-between js-details-target" aria-expanded="false"> Open Source <svg opacity="0.5" aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-chevron-down HeaderMenu-icon ml-1"> <path d="M12.78 5.22a.749.749 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.06 0L3.22 6.28a.749.749 0 1 1 1.06-1.06L8 8.939l3.72-3.719a.749.749 0 0 1 1.06 0Z"></path> </svg> </button> <div class="HeaderMenu-dropdown dropdown-menu rounded m-0 p-0 pt-2 pt-lg-4 position-relative position-lg-absolute left-0 left-lg-n3 pb-2 pb-lg-4 px-lg-4"> <div class="HeaderMenu-column"> <div class="border-bottom pb-3 pb-lg-0 pb-lg-3 mb-3 mb-lg-0 mb-lg-3"> <ul class="list-style-none f5" > <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary d-flex flex-items-center Link--has-description" data-analytics-event="{"location":"navbar","action":"github_sponsors","context":"open_source","tag":"link","label":"github_sponsors_link_open_source_navbar"}" href="/sponsors"> <div> <div class="color-fg-default h4">GitHub Sponsors</div> Fund open source developers </div> </a></li> </ul> </div> <div class="border-bottom pb-3 pb-lg-0 pb-lg-3 mb-3 mb-lg-0 mb-lg-3"> <ul class="list-style-none f5" > <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary d-flex flex-items-center Link--has-description" data-analytics-event="{"location":"navbar","action":"the_readme_project","context":"open_source","tag":"link","label":"the_readme_project_link_open_source_navbar"}" href="https://github.com/readme"> <div> <div class="color-fg-default h4">The ReadME Project</div> GitHub community articles </div> </a></li> </ul> </div> <div class="border-bottom pb-3 pb-lg-0 border-bottom-0"> <span class="d-block h4 color-fg-default my-1" id="open-source-repositories-heading">Repositories</span> <ul class="list-style-none f5" aria-labelledby="open-source-repositories-heading"> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"topics","context":"open_source","tag":"link","label":"topics_link_open_source_navbar"}" href="https://github.com/topics"> Topics </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"trending","context":"open_source","tag":"link","label":"trending_link_open_source_navbar"}" href="https://github.com/trending"> Trending </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary" data-analytics-event="{"location":"navbar","action":"collections","context":"open_source","tag":"link","label":"collections_link_open_source_navbar"}" href="https://github.com/collections"> Collections </a></li> </ul> </div> </div> </div> </li> <li class="HeaderMenu-item position-relative flex-wrap flex-justify-between flex-items-center d-block d-lg-flex flex-lg-nowrap flex-lg-items-center js-details-container js-header-menu-item"> <button type="button" class="HeaderMenu-link border-0 width-full width-lg-auto px-0 px-lg-2 py-lg-2 no-wrap d-flex flex-items-center flex-justify-between js-details-target" aria-expanded="false"> Enterprise <svg opacity="0.5" aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-chevron-down HeaderMenu-icon ml-1"> <path d="M12.78 5.22a.749.749 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.06 0L3.22 6.28a.749.749 0 1 1 1.06-1.06L8 8.939l3.72-3.719a.749.749 0 0 1 1.06 0Z"></path> </svg> </button> <div class="HeaderMenu-dropdown dropdown-menu rounded m-0 p-0 pt-2 pt-lg-4 position-relative position-lg-absolute left-0 left-lg-n3 pb-2 pb-lg-4 px-lg-4"> <div class="HeaderMenu-column"> <div class="border-bottom pb-3 pb-lg-0 pb-lg-3 mb-3 mb-lg-0 mb-lg-3"> <ul class="list-style-none f5" > <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary d-flex flex-items-center Link--has-description" data-analytics-event="{"location":"navbar","action":"enterprise_platform","context":"enterprise","tag":"link","label":"enterprise_platform_link_enterprise_navbar"}" href="/enterprise"> <svg aria-hidden="true" height="24" viewBox="0 0 24 24" version="1.1" width="24" data-view-component="true" class="octicon octicon-stack color-fg-subtle mr-3"> <path d="M11.063 1.456a1.749 1.749 0 0 1 1.874 0l8.383 5.316a1.751 1.751 0 0 1 0 2.956l-8.383 5.316a1.749 1.749 0 0 1-1.874 0L2.68 9.728a1.751 1.751 0 0 1 0-2.956Zm1.071 1.267a.25.25 0 0 0-.268 0L3.483 8.039a.25.25 0 0 0 0 .422l8.383 5.316a.25.25 0 0 0 .268 0l8.383-5.316a.25.25 0 0 0 0-.422Z"></path><path d="M1.867 12.324a.75.75 0 0 1 1.035-.232l8.964 5.685a.25.25 0 0 0 .268 0l8.964-5.685a.75.75 0 0 1 .804 1.267l-8.965 5.685a1.749 1.749 0 0 1-1.874 0l-8.965-5.685a.75.75 0 0 1-.231-1.035Z"></path><path d="M1.867 16.324a.75.75 0 0 1 1.035-.232l8.964 5.685a.25.25 0 0 0 .268 0l8.964-5.685a.75.75 0 0 1 .804 1.267l-8.965 5.685a1.749 1.749 0 0 1-1.874 0l-8.965-5.685a.75.75 0 0 1-.231-1.035Z"></path> </svg> <div> <div class="color-fg-default h4">Enterprise platform</div> AI-powered developer platform </div> </a></li> </ul> </div> <div class="border-bottom pb-3 pb-lg-0 border-bottom-0"> <span class="d-block h4 color-fg-default my-1" id="enterprise-available-add-ons-heading">Available add-ons</span> <ul class="list-style-none f5" aria-labelledby="enterprise-available-add-ons-heading"> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary d-flex flex-items-center Link--has-description pb-lg-3" data-analytics-event="{"location":"navbar","action":"advanced_security","context":"enterprise","tag":"link","label":"advanced_security_link_enterprise_navbar"}" href="https://github.com/enterprise/advanced-security"> <svg aria-hidden="true" height="24" viewBox="0 0 24 24" version="1.1" width="24" data-view-component="true" class="octicon octicon-shield-check color-fg-subtle mr-3"> <path d="M16.53 9.78a.75.75 0 0 0-1.06-1.06L11 13.19l-1.97-1.97a.75.75 0 0 0-1.06 1.06l2.5 2.5a.75.75 0 0 0 1.06 0l5-5Z"></path><path d="m12.54.637 8.25 2.675A1.75 1.75 0 0 1 22 4.976V10c0 6.19-3.771 10.704-9.401 12.83a1.704 1.704 0 0 1-1.198 0C5.77 20.705 2 16.19 2 10V4.976c0-.758.489-1.43 1.21-1.664L11.46.637a1.748 1.748 0 0 1 1.08 0Zm-.617 1.426-8.25 2.676a.249.249 0 0 0-.173.237V10c0 5.46 3.28 9.483 8.43 11.426a.199.199 0 0 0 .14 0C17.22 19.483 20.5 15.461 20.5 10V4.976a.25.25 0 0 0-.173-.237l-8.25-2.676a.253.253 0 0 0-.154 0Z"></path> </svg> <div> <div class="color-fg-default h4">Advanced Security</div> Enterprise-grade security features </div> </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary d-flex flex-items-center Link--has-description pb-lg-3" data-analytics-event="{"location":"navbar","action":"copilot_for_business","context":"enterprise","tag":"link","label":"copilot_for_business_link_enterprise_navbar"}" href="/features/copilot/copilot-business"> <svg aria-hidden="true" height="24" viewBox="0 0 24 24" version="1.1" width="24" data-view-component="true" class="octicon octicon-copilot color-fg-subtle mr-3"> <path d="M23.922 16.992c-.861 1.495-5.859 5.023-11.922 5.023-6.063 0-11.061-3.528-11.922-5.023A.641.641 0 0 1 0 16.736v-2.869a.841.841 0 0 1 .053-.22c.372-.935 1.347-2.292 2.605-2.656.167-.429.414-1.055.644-1.517a10.195 10.195 0 0 1-.052-1.086c0-1.331.282-2.499 1.132-3.368.397-.406.89-.717 1.474-.952 1.399-1.136 3.392-2.093 6.122-2.093 2.731 0 4.767.957 6.166 2.093.584.235 1.077.546 1.474.952.85.869 1.132 2.037 1.132 3.368 0 .368-.014.733-.052 1.086.23.462.477 1.088.644 1.517 1.258.364 2.233 1.721 2.605 2.656a.832.832 0 0 1 .053.22v2.869a.641.641 0 0 1-.078.256ZM12.172 11h-.344a4.323 4.323 0 0 1-.355.508C10.703 12.455 9.555 13 7.965 13c-1.725 0-2.989-.359-3.782-1.259a2.005 2.005 0 0 1-.085-.104L4 11.741v6.585c1.435.779 4.514 2.179 8 2.179 3.486 0 6.565-1.4 8-2.179v-6.585l-.098-.104s-.033.045-.085.104c-.793.9-2.057 1.259-3.782 1.259-1.59 0-2.738-.545-3.508-1.492a4.323 4.323 0 0 1-.355-.508h-.016.016Zm.641-2.935c.136 1.057.403 1.913.878 2.497.442.544 1.134.938 2.344.938 1.573 0 2.292-.337 2.657-.751.384-.435.558-1.15.558-2.361 0-1.14-.243-1.847-.705-2.319-.477-.488-1.319-.862-2.824-1.025-1.487-.161-2.192.138-2.533.529-.269.307-.437.808-.438 1.578v.021c0 .265.021.562.063.893Zm-1.626 0c.042-.331.063-.628.063-.894v-.02c-.001-.77-.169-1.271-.438-1.578-.341-.391-1.046-.69-2.533-.529-1.505.163-2.347.537-2.824 1.025-.462.472-.705 1.179-.705 2.319 0 1.211.175 1.926.558 2.361.365.414 1.084.751 2.657.751 1.21 0 1.902-.394 2.344-.938.475-.584.742-1.44.878-2.497Z"></path><path d="M14.5 14.25a1 1 0 0 1 1 1v2a1 1 0 0 1-2 0v-2a1 1 0 0 1 1-1Zm-5 0a1 1 0 0 1 1 1v2a1 1 0 0 1-2 0v-2a1 1 0 0 1 1-1Z"></path> </svg> <div> <div class="color-fg-default h4">Copilot for business</div> Enterprise-grade AI features </div> </a></li> <li> <a class="HeaderMenu-dropdown-link d-block no-underline position-relative py-2 Link--secondary d-flex flex-items-center Link--has-description" data-analytics-event="{"location":"navbar","action":"premium_support","context":"enterprise","tag":"link","label":"premium_support_link_enterprise_navbar"}" href="/premium-support"> <svg aria-hidden="true" height="24" viewBox="0 0 24 24" version="1.1" width="24" data-view-component="true" class="octicon octicon-comment-discussion color-fg-subtle mr-3"> <path d="M1.75 1h12.5c.966 0 1.75.784 1.75 1.75v9.5A1.75 1.75 0 0 1 14.25 14H8.061l-2.574 2.573A1.458 1.458 0 0 1 3 15.543V14H1.75A1.75 1.75 0 0 1 0 12.25v-9.5C0 1.784.784 1 1.75 1ZM1.5 2.75v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25H1.75a.25.25 0 0 0-.25.25Z"></path><path d="M22.5 8.75a.25.25 0 0 0-.25-.25h-3.5a.75.75 0 0 1 0-1.5h3.5c.966 0 1.75.784 1.75 1.75v9.5A1.75 1.75 0 0 1 22.25 20H21v1.543a1.457 1.457 0 0 1-2.487 1.03L15.939 20H10.75A1.75 1.75 0 0 1 9 18.25v-1.465a.75.75 0 0 1 1.5 0v1.465c0 .138.112.25.25.25h5.5a.75.75 0 0 1 .53.22l2.72 2.72v-2.19a.75.75 0 0 1 .75-.75h2a.25.25 0 0 0 .25-.25v-9.5Z"></path> </svg> <div> <div class="color-fg-default h4">Premium Support</div> Enterprise-grade 24/7 support </div> </a></li> </ul> </div> </div> </div> </li> <li class="HeaderMenu-item position-relative flex-wrap flex-justify-between flex-items-center d-block d-lg-flex flex-lg-nowrap flex-lg-items-center js-details-container js-header-menu-item"> <a class="HeaderMenu-link no-underline px-0 px-lg-2 py-3 py-lg-2 d-block d-lg-inline-block" data-analytics-event="{"location":"navbar","action":"pricing","context":"global","tag":"link","label":"pricing_link_global_navbar"}" href="https://github.com/pricing">Pricing</a> </li> </ul> </nav> <div class="d-flex flex-column flex-lg-row width-full flex-justify-end flex-lg-items-center text-center mt-3 mt-lg-0 text-lg-left ml-lg-3"> <qbsearch-input class="search-input" data-scope="repo:RichardKnop/machinery" data-custom-scopes-path="/search/custom_scopes" data-delete-custom-scopes-csrf="5-iGW6f9MB0NzefZvdt4Q6_n1FGNsUg176RVykUi1w7Ll9Hvq9IgDXr5FhAx81tt2Pxdoefqk1khGvGVZ8EzHw" data-max-custom-scopes="10" data-header-redesign-enabled="false" data-initial-value="" data-blackbird-suggestions-path="/search/suggestions" data-jump-to-suggestions-path="/_graphql/GetSuggestedNavigationDestinations" data-current-repository="RichardKnop/machinery" data-current-org="" data-current-owner="RichardKnop" data-logged-in="false" data-copilot-chat-enabled="false" data-nl-search-enabled="false" data-retain-scroll-position="true"> <div class="search-input-container search-with-dialog position-relative d-flex flex-row flex-items-center mr-4 rounded" data-action="click:qbsearch-input#searchInputContainerClicked" > <button type="button" class="header-search-button placeholder input-button form-control d-flex flex-1 flex-self-stretch flex-items-center no-wrap width-full py-0 pl-2 pr-0 text-left border-0 box-shadow-none" data-target="qbsearch-input.inputButton" aria-label="Search or jump to…" aria-haspopup="dialog" placeholder="Search or jump to..." data-hotkey=s,/ autocapitalize="off" data-analytics-event="{"location":"navbar","action":"searchbar","context":"global","tag":"input","label":"searchbar_input_global_navbar"}" data-action="click:qbsearch-input#handleExpand" > <div class="mr-2 color-fg-muted"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-search"> <path d="M10.68 11.74a6 6 0 0 1-7.922-8.982 6 6 0 0 1 8.982 7.922l3.04 3.04a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215ZM11.5 7a4.499 4.499 0 1 0-8.997 0A4.499 4.499 0 0 0 11.5 7Z"></path> </svg> </div> <span class="flex-1" data-target="qbsearch-input.inputButtonText">Search or jump to...</span> <div class="d-flex" data-target="qbsearch-input.hotkeyIndicator"> <svg xmlns="http://www.w3.org/2000/svg" width="22" height="20" aria-hidden="true" class="mr-1"><path fill="none" stroke="#979A9C" opacity=".4" d="M3.5.5h12c1.7 0 3 1.3 3 3v13c0 1.7-1.3 3-3 3h-12c-1.7 0-3-1.3-3-3v-13c0-1.7 1.3-3 3-3z"></path><path fill="#979A9C" d="M11.8 6L8 15.1h-.9L10.8 6h1z"></path></svg> </div> </button> <input type="hidden" name="type" class="js-site-search-type-field"> <div class="Overlay--hidden " data-modal-dialog-overlay> <modal-dialog data-action="close:qbsearch-input#handleClose cancel:qbsearch-input#handleClose" data-target="qbsearch-input.searchSuggestionsDialog" role="dialog" id="search-suggestions-dialog" aria-modal="true" aria-labelledby="search-suggestions-dialog-header" data-view-component="true" class="Overlay Overlay--width-large Overlay--height-auto"> <h1 id="search-suggestions-dialog-header" class="sr-only">Search code, repositories, users, issues, pull requests...</h1> <div class="Overlay-body Overlay-body--paddingNone"> <div data-view-component="true"> <div class="search-suggestions position-fixed width-full color-shadow-large border color-fg-default color-bg-default overflow-hidden d-flex flex-column query-builder-container" style="border-radius: 12px;" data-target="qbsearch-input.queryBuilderContainer" hidden > <!-- '"` --><!-- </textarea></xmp> --></option></form><form id="query-builder-test-form" action="" accept-charset="UTF-8" method="get"> <query-builder data-target="qbsearch-input.queryBuilder" id="query-builder-query-builder-test" data-filter-key=":" data-view-component="true" class="QueryBuilder search-query-builder"> <div class="FormControl FormControl--fullWidth"> <label id="query-builder-test-label" for="query-builder-test" class="FormControl-label sr-only"> Search </label> <div class="QueryBuilder-StyledInput width-fit " data-target="query-builder.styledInput" > <span id="query-builder-test-leadingvisual-wrap" class="FormControl-input-leadingVisualWrap QueryBuilder-leadingVisualWrap"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-search FormControl-input-leadingVisual"> <path d="M10.68 11.74a6 6 0 0 1-7.922-8.982 6 6 0 0 1 8.982 7.922l3.04 3.04a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215ZM11.5 7a4.499 4.499 0 1 0-8.997 0A4.499 4.499 0 0 0 11.5 7Z"></path> </svg> </span> <div data-target="query-builder.styledInputContainer" class="QueryBuilder-StyledInputContainer"> <div aria-hidden="true" class="QueryBuilder-StyledInputContent" data-target="query-builder.styledInputContent" ></div> <div class="QueryBuilder-InputWrapper"> <div aria-hidden="true" class="QueryBuilder-Sizer" data-target="query-builder.sizer"></div> <input id="query-builder-test" name="query-builder-test" value="" autocomplete="off" type="text" role="combobox" spellcheck="false" aria-expanded="false" aria-describedby="validation-2adbdc26-2a28-4ee9-81de-407b5792f0df" data-target="query-builder.input" data-action=" input:query-builder#inputChange blur:query-builder#inputBlur keydown:query-builder#inputKeydown focus:query-builder#inputFocus " data-view-component="true" class="FormControl-input QueryBuilder-Input FormControl-medium" /> </div> </div> <span class="sr-only" id="query-builder-test-clear">Clear</span> <button role="button" id="query-builder-test-clear-button" aria-labelledby="query-builder-test-clear query-builder-test-label" data-target="query-builder.clearButton" data-action=" click:query-builder#clear focus:query-builder#clearButtonFocus blur:query-builder#clearButtonBlur " variant="small" hidden="hidden" type="button" data-view-component="true" class="Button Button--iconOnly Button--invisible Button--medium mr-1 px-2 py-0 d-flex flex-items-center rounded-1 color-fg-muted"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x-circle-fill Button-visual"> <path d="M2.343 13.657A8 8 0 1 1 13.658 2.343 8 8 0 0 1 2.343 13.657ZM6.03 4.97a.751.751 0 0 0-1.042.018.751.751 0 0 0-.018 1.042L6.94 8 4.97 9.97a.749.749 0 0 0 .326 1.275.749.749 0 0 0 .734-.215L8 9.06l1.97 1.97a.749.749 0 0 0 1.275-.326.749.749 0 0 0-.215-.734L9.06 8l1.97-1.97a.749.749 0 0 0-.326-1.275.749.749 0 0 0-.734.215L8 6.94Z"></path> </svg> </button> </div> <template id="search-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-search"> <path d="M10.68 11.74a6 6 0 0 1-7.922-8.982 6 6 0 0 1 8.982 7.922l3.04 3.04a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215ZM11.5 7a4.499 4.499 0 1 0-8.997 0A4.499 4.499 0 0 0 11.5 7Z"></path> </svg> </template> <template id="code-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-code"> <path d="m11.28 3.22 4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L13.94 8l-3.72-3.72a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215Zm-6.56 0a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042L2.06 8l3.72 3.72a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L.47 8.53a.75.75 0 0 1 0-1.06Z"></path> </svg> </template> <template id="file-code-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-file-code"> <path d="M4 1.75C4 .784 4.784 0 5.75 0h5.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v8.586A1.75 1.75 0 0 1 14.25 15h-9a.75.75 0 0 1 0-1.5h9a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 10 4.25V1.5H5.75a.25.25 0 0 0-.25.25v2.5a.75.75 0 0 1-1.5 0Zm1.72 4.97a.75.75 0 0 1 1.06 0l2 2a.75.75 0 0 1 0 1.06l-2 2a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734l1.47-1.47-1.47-1.47a.75.75 0 0 1 0-1.06ZM3.28 7.78 1.81 9.25l1.47 1.47a.751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018l-2-2a.75.75 0 0 1 0-1.06l2-2a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Zm8.22-6.218V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path> </svg> </template> <template id="history-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-history"> <path d="m.427 1.927 1.215 1.215a8.002 8.002 0 1 1-1.6 5.685.75.75 0 1 1 1.493-.154 6.5 6.5 0 1 0 1.18-4.458l1.358 1.358A.25.25 0 0 1 3.896 6H.25A.25.25 0 0 1 0 5.75V2.104a.25.25 0 0 1 .427-.177ZM7.75 4a.75.75 0 0 1 .75.75v2.992l2.028.812a.75.75 0 0 1-.557 1.392l-2.5-1A.751.751 0 0 1 7 8.25v-3.5A.75.75 0 0 1 7.75 4Z"></path> </svg> </template> <template id="repo-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-repo"> <path d="M2 2.5A2.5 2.5 0 0 1 4.5 0h8.75a.75.75 0 0 1 .75.75v12.5a.75.75 0 0 1-.75.75h-2.5a.75.75 0 0 1 0-1.5h1.75v-2h-8a1 1 0 0 0-.714 1.7.75.75 0 1 1-1.072 1.05A2.495 2.495 0 0 1 2 11.5Zm10.5-1h-8a1 1 0 0 0-1 1v6.708A2.486 2.486 0 0 1 4.5 9h8ZM5 12.25a.25.25 0 0 1 .25-.25h3.5a.25.25 0 0 1 .25.25v3.25a.25.25 0 0 1-.4.2l-1.45-1.087a.249.249 0 0 0-.3 0L5.4 15.7a.25.25 0 0 1-.4-.2Z"></path> </svg> </template> <template id="bookmark-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-bookmark"> <path d="M3 2.75C3 1.784 3.784 1 4.75 1h6.5c.966 0 1.75.784 1.75 1.75v11.5a.75.75 0 0 1-1.227.579L8 11.722l-3.773 3.107A.751.751 0 0 1 3 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.91l3.023-2.489a.75.75 0 0 1 .954 0l3.023 2.49V2.75a.25.25 0 0 0-.25-.25Z"></path> </svg> </template> <template id="plus-circle-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-plus-circle"> <path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm7.25-3.25v2.5h2.5a.75.75 0 0 1 0 1.5h-2.5v2.5a.75.75 0 0 1-1.5 0v-2.5h-2.5a.75.75 0 0 1 0-1.5h2.5v-2.5a.75.75 0 0 1 1.5 0Z"></path> </svg> </template> <template id="circle-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-dot-fill"> <path d="M8 4a4 4 0 1 1 0 8 4 4 0 0 1 0-8Z"></path> </svg> </template> <template id="trash-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-trash"> <path d="M11 1.75V3h2.25a.75.75 0 0 1 0 1.5H2.75a.75.75 0 0 1 0-1.5H5V1.75C5 .784 5.784 0 6.75 0h2.5C10.216 0 11 .784 11 1.75ZM4.496 6.675l.66 6.6a.25.25 0 0 0 .249.225h5.19a.25.25 0 0 0 .249-.225l.66-6.6a.75.75 0 0 1 1.492.149l-.66 6.6A1.748 1.748 0 0 1 10.595 15h-5.19a1.75 1.75 0 0 1-1.741-1.575l-.66-6.6a.75.75 0 1 1 1.492-.15ZM6.5 1.75V3h3V1.75a.25.25 0 0 0-.25-.25h-2.5a.25.25 0 0 0-.25.25Z"></path> </svg> </template> <template id="team-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-people"> <path d="M2 5.5a3.5 3.5 0 1 1 5.898 2.549 5.508 5.508 0 0 1 3.034 4.084.75.75 0 1 1-1.482.235 4 4 0 0 0-7.9 0 .75.75 0 0 1-1.482-.236A5.507 5.507 0 0 1 3.102 8.05 3.493 3.493 0 0 1 2 5.5ZM11 4a3.001 3.001 0 0 1 2.22 5.018 5.01 5.01 0 0 1 2.56 3.012.749.749 0 0 1-.885.954.752.752 0 0 1-.549-.514 3.507 3.507 0 0 0-2.522-2.372.75.75 0 0 1-.574-.73v-.352a.75.75 0 0 1 .416-.672A1.5 1.5 0 0 0 11 5.5.75.75 0 0 1 11 4Zm-5.5-.5a2 2 0 1 0-.001 3.999A2 2 0 0 0 5.5 3.5Z"></path> </svg> </template> <template id="project-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-project"> <path d="M1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25V1.75C0 .784.784 0 1.75 0ZM1.5 1.75v12.5c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25V1.75a.25.25 0 0 0-.25-.25H1.75a.25.25 0 0 0-.25.25ZM11.75 3a.75.75 0 0 1 .75.75v7.5a.75.75 0 0 1-1.5 0v-7.5a.75.75 0 0 1 .75-.75Zm-8.25.75a.75.75 0 0 1 1.5 0v5.5a.75.75 0 0 1-1.5 0ZM8 3a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 3Z"></path> </svg> </template> <template id="pencil-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-pencil"> <path d="M11.013 1.427a1.75 1.75 0 0 1 2.474 0l1.086 1.086a1.75 1.75 0 0 1 0 2.474l-8.61 8.61c-.21.21-.47.364-.756.445l-3.251.93a.75.75 0 0 1-.927-.928l.929-3.25c.081-.286.235-.547.445-.758l8.61-8.61Zm.176 4.823L9.75 4.81l-6.286 6.287a.253.253 0 0 0-.064.108l-.558 1.953 1.953-.558a.253.253 0 0 0 .108-.064Zm1.238-3.763a.25.25 0 0 0-.354 0L10.811 3.75l1.439 1.44 1.263-1.263a.25.25 0 0 0 0-.354Z"></path> </svg> </template> <template id="copilot-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-copilot"> <path d="M7.998 15.035c-4.562 0-7.873-2.914-7.998-3.749V9.338c.085-.628.677-1.686 1.588-2.065.013-.07.024-.143.036-.218.029-.183.06-.384.126-.612-.201-.508-.254-1.084-.254-1.656 0-.87.128-1.769.693-2.484.579-.733 1.494-1.124 2.724-1.261 1.206-.134 2.262.034 2.944.765.05.053.096.108.139.165.044-.057.094-.112.143-.165.682-.731 1.738-.899 2.944-.765 1.23.137 2.145.528 2.724 1.261.566.715.693 1.614.693 2.484 0 .572-.053 1.148-.254 1.656.066.228.098.429.126.612.012.076.024.148.037.218.924.385 1.522 1.471 1.591 2.095v1.872c0 .766-3.351 3.795-8.002 3.795Zm0-1.485c2.28 0 4.584-1.11 5.002-1.433V7.862l-.023-.116c-.49.21-1.075.291-1.727.291-1.146 0-2.059-.327-2.71-.991A3.222 3.222 0 0 1 8 6.303a3.24 3.24 0 0 1-.544.743c-.65.664-1.563.991-2.71.991-.652 0-1.236-.081-1.727-.291l-.023.116v4.255c.419.323 2.722 1.433 5.002 1.433ZM6.762 2.83c-.193-.206-.637-.413-1.682-.297-1.019.113-1.479.404-1.713.7-.247.312-.369.789-.369 1.554 0 .793.129 1.171.308 1.371.162.181.519.379 1.442.379.853 0 1.339-.235 1.638-.54.315-.322.527-.827.617-1.553.117-.935-.037-1.395-.241-1.614Zm4.155-.297c-1.044-.116-1.488.091-1.681.297-.204.219-.359.679-.242 1.614.091.726.303 1.231.618 1.553.299.305.784.54 1.638.54.922 0 1.28-.198 1.442-.379.179-.2.308-.578.308-1.371 0-.765-.123-1.242-.37-1.554-.233-.296-.693-.587-1.713-.7Z"></path><path d="M6.25 9.037a.75.75 0 0 1 .75.75v1.501a.75.75 0 0 1-1.5 0V9.787a.75.75 0 0 1 .75-.75Zm4.25.75v1.501a.75.75 0 0 1-1.5 0V9.787a.75.75 0 0 1 1.5 0Z"></path> </svg> </template> <template id="copilot-error-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-copilot-error"> <path d="M16 11.24c0 .112-.072.274-.21.467L13 9.688V7.862l-.023-.116c-.49.21-1.075.291-1.727.291-.198 0-.388-.009-.571-.029L6.833 5.226a4.01 4.01 0 0 0 .17-.782c.117-.935-.037-1.395-.241-1.614-.193-.206-.637-.413-1.682-.297-.683.076-1.115.231-1.395.415l-1.257-.91c.579-.564 1.413-.877 2.485-.996 1.206-.134 2.262.034 2.944.765.05.053.096.108.139.165.044-.057.094-.112.143-.165.682-.731 1.738-.899 2.944-.765 1.23.137 2.145.528 2.724 1.261.566.715.693 1.614.693 2.484 0 .572-.053 1.148-.254 1.656.066.228.098.429.126.612.012.076.024.148.037.218.924.385 1.522 1.471 1.591 2.095Zm-5.083-8.707c-1.044-.116-1.488.091-1.681.297-.204.219-.359.679-.242 1.614.091.726.303 1.231.618 1.553.299.305.784.54 1.638.54.922 0 1.28-.198 1.442-.379.179-.2.308-.578.308-1.371 0-.765-.123-1.242-.37-1.554-.233-.296-.693-.587-1.713-.7Zm2.511 11.074c-1.393.776-3.272 1.428-5.43 1.428-4.562 0-7.873-2.914-7.998-3.749V9.338c.085-.628.677-1.686 1.588-2.065.013-.07.024-.143.036-.218.029-.183.06-.384.126-.612-.18-.455-.241-.963-.252-1.475L.31 4.107A.747.747 0 0 1 0 3.509V3.49a.748.748 0 0 1 .625-.73c.156-.026.306.047.435.139l14.667 10.578a.592.592 0 0 1 .227.264.752.752 0 0 1 .046.249v.022a.75.75 0 0 1-1.19.596Zm-1.367-.991L5.635 7.964a5.128 5.128 0 0 1-.889.073c-.652 0-1.236-.081-1.727-.291l-.023.116v4.255c.419.323 2.722 1.433 5.002 1.433 1.539 0 3.089-.505 4.063-.934Z"></path> </svg> </template> <template id="workflow-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-workflow"> <path d="M0 1.75C0 .784.784 0 1.75 0h3.5C6.216 0 7 .784 7 1.75v3.5A1.75 1.75 0 0 1 5.25 7H4v4a1 1 0 0 0 1 1h4v-1.25C9 9.784 9.784 9 10.75 9h3.5c.966 0 1.75.784 1.75 1.75v3.5A1.75 1.75 0 0 1 14.25 16h-3.5A1.75 1.75 0 0 1 9 14.25v-.75H5A2.5 2.5 0 0 1 2.5 11V7h-.75A1.75 1.75 0 0 1 0 5.25Zm1.75-.25a.25.25 0 0 0-.25.25v3.5c0 .138.112.25.25.25h3.5a.25.25 0 0 0 .25-.25v-3.5a.25.25 0 0 0-.25-.25Zm9 9a.25.25 0 0 0-.25.25v3.5c0 .138.112.25.25.25h3.5a.25.25 0 0 0 .25-.25v-3.5a.25.25 0 0 0-.25-.25Z"></path> </svg> </template> <template id="book-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-book"> <path d="M0 1.75A.75.75 0 0 1 .75 1h4.253c1.227 0 2.317.59 3 1.501A3.743 3.743 0 0 1 11.006 1h4.245a.75.75 0 0 1 .75.75v10.5a.75.75 0 0 1-.75.75h-4.507a2.25 2.25 0 0 0-1.591.659l-.622.621a.75.75 0 0 1-1.06 0l-.622-.621A2.25 2.25 0 0 0 5.258 13H.75a.75.75 0 0 1-.75-.75Zm7.251 10.324.004-5.073-.002-2.253A2.25 2.25 0 0 0 5.003 2.5H1.5v9h3.757a3.75 3.75 0 0 1 1.994.574ZM8.755 4.75l-.004 7.322a3.752 3.752 0 0 1 1.992-.572H14.5v-9h-3.495a2.25 2.25 0 0 0-2.25 2.25Z"></path> </svg> </template> <template id="code-review-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-code-review"> <path d="M1.75 1h12.5c.966 0 1.75.784 1.75 1.75v8.5A1.75 1.75 0 0 1 14.25 13H8.061l-2.574 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25v-8.5C0 1.784.784 1 1.75 1ZM1.5 2.75v8.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-8.5a.25.25 0 0 0-.25-.25H1.75a.25.25 0 0 0-.25.25Zm5.28 1.72a.75.75 0 0 1 0 1.06L5.31 7l1.47 1.47a.751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018l-2-2a.75.75 0 0 1 0-1.06l2-2a.75.75 0 0 1 1.06 0Zm2.44 0a.75.75 0 0 1 1.06 0l2 2a.75.75 0 0 1 0 1.06l-2 2a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L10.69 7 9.22 5.53a.75.75 0 0 1 0-1.06Z"></path> </svg> </template> <template id="codespaces-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-codespaces"> <path d="M0 11.25c0-.966.784-1.75 1.75-1.75h12.5c.966 0 1.75.784 1.75 1.75v3A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25Zm2-9.5C2 .784 2.784 0 3.75 0h8.5C13.216 0 14 .784 14 1.75v5a1.75 1.75 0 0 1-1.75 1.75h-8.5A1.75 1.75 0 0 1 2 6.75Zm1.75-.25a.25.25 0 0 0-.25.25v5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-5a.25.25 0 0 0-.25-.25Zm-2 9.5a.25.25 0 0 0-.25.25v3c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25v-3a.25.25 0 0 0-.25-.25Z"></path><path d="M7 12.75a.75.75 0 0 1 .75-.75h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1-.75-.75Zm-4 0a.75.75 0 0 1 .75-.75h.5a.75.75 0 0 1 0 1.5h-.5a.75.75 0 0 1-.75-.75Z"></path> </svg> </template> <template id="comment-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-comment"> <path d="M1 2.75C1 1.784 1.784 1 2.75 1h10.5c.966 0 1.75.784 1.75 1.75v7.5A1.75 1.75 0 0 1 13.25 12H9.06l-2.573 2.573A1.458 1.458 0 0 1 4 13.543V12H2.75A1.75 1.75 0 0 1 1 10.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h4.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path> </svg> </template> <template id="comment-discussion-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-comment-discussion"> <path d="M1.75 1h8.5c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0 1 10.25 10H7.061l-2.574 2.573A1.458 1.458 0 0 1 2 11.543V10h-.25A1.75 1.75 0 0 1 0 8.25v-5.5C0 1.784.784 1 1.75 1ZM1.5 2.75v5.5c0 .138.112.25.25.25h1a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h3.5a.25.25 0 0 0 .25-.25v-5.5a.25.25 0 0 0-.25-.25h-8.5a.25.25 0 0 0-.25.25Zm13 2a.25.25 0 0 0-.25-.25h-.5a.75.75 0 0 1 0-1.5h.5c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0 1 14.25 12H14v1.543a1.458 1.458 0 0 1-2.487 1.03L9.22 12.28a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215l2.22 2.22v-2.19a.75.75 0 0 1 .75-.75h1a.25.25 0 0 0 .25-.25Z"></path> </svg> </template> <template id="organization-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-organization"> <path d="M1.75 16A1.75 1.75 0 0 1 0 14.25V1.75C0 .784.784 0 1.75 0h8.5C11.216 0 12 .784 12 1.75v12.5c0 .085-.006.168-.018.25h2.268a.25.25 0 0 0 .25-.25V8.285a.25.25 0 0 0-.111-.208l-1.055-.703a.749.749 0 1 1 .832-1.248l1.055.703c.487.325.779.871.779 1.456v5.965A1.75 1.75 0 0 1 14.25 16h-3.5a.766.766 0 0 1-.197-.026c-.099.017-.2.026-.303.026h-3a.75.75 0 0 1-.75-.75V14h-1v1.25a.75.75 0 0 1-.75.75Zm-.25-1.75c0 .138.112.25.25.25H4v-1.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 .75.75v1.25h2.25a.25.25 0 0 0 .25-.25V1.75a.25.25 0 0 0-.25-.25h-8.5a.25.25 0 0 0-.25.25ZM3.75 6h.5a.75.75 0 0 1 0 1.5h-.5a.75.75 0 0 1 0-1.5ZM3 3.75A.75.75 0 0 1 3.75 3h.5a.75.75 0 0 1 0 1.5h-.5A.75.75 0 0 1 3 3.75Zm4 3A.75.75 0 0 1 7.75 6h.5a.75.75 0 0 1 0 1.5h-.5A.75.75 0 0 1 7 6.75ZM7.75 3h.5a.75.75 0 0 1 0 1.5h-.5a.75.75 0 0 1 0-1.5ZM3 9.75A.75.75 0 0 1 3.75 9h.5a.75.75 0 0 1 0 1.5h-.5A.75.75 0 0 1 3 9.75ZM7.75 9h.5a.75.75 0 0 1 0 1.5h-.5a.75.75 0 0 1 0-1.5Z"></path> </svg> </template> <template id="rocket-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-rocket"> <path d="M14.064 0h.186C15.216 0 16 .784 16 1.75v.186a8.752 8.752 0 0 1-2.564 6.186l-.458.459c-.314.314-.641.616-.979.904v3.207c0 .608-.315 1.172-.833 1.49l-2.774 1.707a.749.749 0 0 1-1.11-.418l-.954-3.102a1.214 1.214 0 0 1-.145-.125L3.754 9.816a1.218 1.218 0 0 1-.124-.145L.528 8.717a.749.749 0 0 1-.418-1.11l1.71-2.774A1.748 1.748 0 0 1 3.31 4h3.204c.288-.338.59-.665.904-.979l.459-.458A8.749 8.749 0 0 1 14.064 0ZM8.938 3.623h-.002l-.458.458c-.76.76-1.437 1.598-2.02 2.5l-1.5 2.317 2.143 2.143 2.317-1.5c.902-.583 1.74-1.26 2.499-2.02l.459-.458a7.25 7.25 0 0 0 2.123-5.127V1.75a.25.25 0 0 0-.25-.25h-.186a7.249 7.249 0 0 0-5.125 2.123ZM3.56 14.56c-.732.732-2.334 1.045-3.005 1.148a.234.234 0 0 1-.201-.064.234.234 0 0 1-.064-.201c.103-.671.416-2.273 1.15-3.003a1.502 1.502 0 1 1 2.12 2.12Zm6.94-3.935c-.088.06-.177.118-.266.175l-2.35 1.521.548 1.783 1.949-1.2a.25.25 0 0 0 .119-.213ZM3.678 8.116 5.2 5.766c.058-.09.117-.178.176-.266H3.309a.25.25 0 0 0-.213.119l-1.2 1.95ZM12 5a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path> </svg> </template> <template id="shield-check-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-shield-check"> <path d="m8.533.133 5.25 1.68A1.75 1.75 0 0 1 15 3.48V7c0 1.566-.32 3.182-1.303 4.682-.983 1.498-2.585 2.813-5.032 3.855a1.697 1.697 0 0 1-1.33 0c-2.447-1.042-4.049-2.357-5.032-3.855C1.32 10.182 1 8.566 1 7V3.48a1.75 1.75 0 0 1 1.217-1.667l5.25-1.68a1.748 1.748 0 0 1 1.066 0Zm-.61 1.429.001.001-5.25 1.68a.251.251 0 0 0-.174.237V7c0 1.36.275 2.666 1.057 3.859.784 1.194 2.121 2.342 4.366 3.298a.196.196 0 0 0 .154 0c2.245-.957 3.582-2.103 4.366-3.297C13.225 9.666 13.5 8.358 13.5 7V3.48a.25.25 0 0 0-.174-.238l-5.25-1.68a.25.25 0 0 0-.153 0ZM11.28 6.28l-3.5 3.5a.75.75 0 0 1-1.06 0l-1.5-1.5a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215l.97.97 2.97-2.97a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"></path> </svg> </template> <template id="heart-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-heart"> <path d="m8 14.25.345.666a.75.75 0 0 1-.69 0l-.008-.004-.018-.01a7.152 7.152 0 0 1-.31-.17 22.055 22.055 0 0 1-3.434-2.414C2.045 10.731 0 8.35 0 5.5 0 2.836 2.086 1 4.25 1 5.797 1 7.153 1.802 8 3.02 8.847 1.802 10.203 1 11.75 1 13.914 1 16 2.836 16 5.5c0 2.85-2.045 5.231-3.885 6.818a22.066 22.066 0 0 1-3.744 2.584l-.018.01-.006.003h-.002ZM4.25 2.5c-1.336 0-2.75 1.164-2.75 3 0 2.15 1.58 4.144 3.365 5.682A20.58 20.58 0 0 0 8 13.393a20.58 20.58 0 0 0 3.135-2.211C12.92 9.644 14.5 7.65 14.5 5.5c0-1.836-1.414-3-2.75-3-1.373 0-2.609.986-3.029 2.456a.749.749 0 0 1-1.442 0C6.859 3.486 5.623 2.5 4.25 2.5Z"></path> </svg> </template> <template id="server-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-server"> <path d="M1.75 1h12.5c.966 0 1.75.784 1.75 1.75v4c0 .372-.116.717-.314 1 .198.283.314.628.314 1v4a1.75 1.75 0 0 1-1.75 1.75H1.75A1.75 1.75 0 0 1 0 12.75v-4c0-.358.109-.707.314-1a1.739 1.739 0 0 1-.314-1v-4C0 1.784.784 1 1.75 1ZM1.5 2.75v4c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25v-4a.25.25 0 0 0-.25-.25H1.75a.25.25 0 0 0-.25.25Zm.25 5.75a.25.25 0 0 0-.25.25v4c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25v-4a.25.25 0 0 0-.25-.25ZM7 4.75A.75.75 0 0 1 7.75 4h4.5a.75.75 0 0 1 0 1.5h-4.5A.75.75 0 0 1 7 4.75ZM7.75 10h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM3 4.75A.75.75 0 0 1 3.75 4h.5a.75.75 0 0 1 0 1.5h-.5A.75.75 0 0 1 3 4.75ZM3.75 10h.5a.75.75 0 0 1 0 1.5h-.5a.75.75 0 0 1 0-1.5Z"></path> </svg> </template> <template id="globe-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-globe"> <path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM5.78 8.75a9.64 9.64 0 0 0 1.363 4.177c.255.426.542.832.857 1.215.245-.296.551-.705.857-1.215A9.64 9.64 0 0 0 10.22 8.75Zm4.44-1.5a9.64 9.64 0 0 0-1.363-4.177c-.307-.51-.612-.919-.857-1.215a9.927 9.927 0 0 0-.857 1.215A9.64 9.64 0 0 0 5.78 7.25Zm-5.944 1.5H1.543a6.507 6.507 0 0 0 4.666 5.5c-.123-.181-.24-.365-.352-.552-.715-1.192-1.437-2.874-1.581-4.948Zm-2.733-1.5h2.733c.144-2.074.866-3.756 1.58-4.948.12-.197.237-.381.353-.552a6.507 6.507 0 0 0-4.666 5.5Zm10.181 1.5c-.144 2.074-.866 3.756-1.58 4.948-.12.197-.237.381-.353.552a6.507 6.507 0 0 0 4.666-5.5Zm2.733-1.5a6.507 6.507 0 0 0-4.666-5.5c.123.181.24.365.353.552.714 1.192 1.436 2.874 1.58 4.948Z"></path> </svg> </template> <template id="issue-opened-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-issue-opened"> <path d="M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"></path><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Z"></path> </svg> </template> <template id="device-mobile-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-device-mobile"> <path d="M3.75 0h8.5C13.216 0 14 .784 14 1.75v12.5A1.75 1.75 0 0 1 12.25 16h-8.5A1.75 1.75 0 0 1 2 14.25V1.75C2 .784 2.784 0 3.75 0ZM3.5 1.75v12.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25V1.75a.25.25 0 0 0-.25-.25h-8.5a.25.25 0 0 0-.25.25ZM8 13a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path> </svg> </template> <template id="package-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-package"> <path d="m8.878.392 5.25 3.045c.54.314.872.89.872 1.514v6.098a1.75 1.75 0 0 1-.872 1.514l-5.25 3.045a1.75 1.75 0 0 1-1.756 0l-5.25-3.045A1.75 1.75 0 0 1 1 11.049V4.951c0-.624.332-1.201.872-1.514L7.122.392a1.75 1.75 0 0 1 1.756 0ZM7.875 1.69l-4.63 2.685L8 7.133l4.755-2.758-4.63-2.685a.248.248 0 0 0-.25 0ZM2.5 5.677v5.372c0 .09.047.171.125.216l4.625 2.683V8.432Zm6.25 8.271 4.625-2.683a.25.25 0 0 0 .125-.216V5.677L8.75 8.432Z"></path> </svg> </template> <template id="credit-card-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-credit-card"> <path d="M10.75 9a.75.75 0 0 0 0 1.5h1.5a.75.75 0 0 0 0-1.5h-1.5Z"></path><path d="M0 3.75C0 2.784.784 2 1.75 2h12.5c.966 0 1.75.784 1.75 1.75v8.5A1.75 1.75 0 0 1 14.25 14H1.75A1.75 1.75 0 0 1 0 12.25ZM14.5 6.5h-13v5.75c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25Zm0-2.75a.25.25 0 0 0-.25-.25H1.75a.25.25 0 0 0-.25.25V5h13Z"></path> </svg> </template> <template id="play-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-play"> <path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm4.879-2.773 4.264 2.559a.25.25 0 0 1 0 .428l-4.264 2.559A.25.25 0 0 1 6 10.559V5.442a.25.25 0 0 1 .379-.215Z"></path> </svg> </template> <template id="gift-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-gift"> <path d="M2 2.75A2.75 2.75 0 0 1 4.75 0c.983 0 1.873.42 2.57 1.232.268.318.497.668.68 1.042.183-.375.411-.725.68-1.044C9.376.42 10.266 0 11.25 0a2.75 2.75 0 0 1 2.45 4h.55c.966 0 1.75.784 1.75 1.75v2c0 .698-.409 1.301-1 1.582v4.918A1.75 1.75 0 0 1 13.25 16H2.75A1.75 1.75 0 0 1 1 14.25V9.332C.409 9.05 0 8.448 0 7.75v-2C0 4.784.784 4 1.75 4h.55c-.192-.375-.3-.8-.3-1.25ZM7.25 9.5H2.5v4.75c0 .138.112.25.25.25h4.5Zm1.5 0v5h4.5a.25.25 0 0 0 .25-.25V9.5Zm0-4V8h5.5a.25.25 0 0 0 .25-.25v-2a.25.25 0 0 0-.25-.25Zm-7 0a.25.25 0 0 0-.25.25v2c0 .138.112.25.25.25h5.5V5.5h-5.5Zm3-4a1.25 1.25 0 0 0 0 2.5h2.309c-.233-.818-.542-1.401-.878-1.793-.43-.502-.915-.707-1.431-.707ZM8.941 4h2.309a1.25 1.25 0 0 0 0-2.5c-.516 0-1 .205-1.43.707-.337.392-.646.975-.879 1.793Z"></path> </svg> </template> <template id="code-square-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-code-square"> <path d="M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25V1.75a.25.25 0 0 0-.25-.25Zm7.47 3.97a.75.75 0 0 1 1.06 0l2 2a.75.75 0 0 1 0 1.06l-2 2a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L10.69 8 9.22 6.53a.75.75 0 0 1 0-1.06ZM6.78 6.53 5.31 8l1.47 1.47a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215l-2-2a.75.75 0 0 1 0-1.06l2-2a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"></path> </svg> </template> <template id="device-desktop-icon"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-device-desktop"> <path d="M14.25 1c.966 0 1.75.784 1.75 1.75v7.5A1.75 1.75 0 0 1 14.25 12h-3.727c.099 1.041.52 1.872 1.292 2.757A.752.752 0 0 1 11.25 16h-6.5a.75.75 0 0 1-.565-1.243c.772-.885 1.192-1.716 1.292-2.757H1.75A1.75 1.75 0 0 1 0 10.25v-7.5C0 1.784.784 1 1.75 1ZM1.75 2.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25ZM9.018 12H6.982a5.72 5.72 0 0 1-.765 2.5h3.566a5.72 5.72 0 0 1-.765-2.5Z"></path> </svg> </template> <div class="position-relative"> <ul role="listbox" class="ActionListWrap QueryBuilder-ListWrap" aria-label="Suggestions" data-action=" combobox-commit:query-builder#comboboxCommit mousedown:query-builder#resultsMousedown " data-target="query-builder.resultsList" data-persist-list=false id="query-builder-test-results" ></ul> </div> <div class="FormControl-inlineValidation" id="validation-2adbdc26-2a28-4ee9-81de-407b5792f0df" hidden="hidden"> <span class="FormControl-inlineValidation--visual"> <svg aria-hidden="true" height="12" viewBox="0 0 12 12" version="1.1" width="12" data-view-component="true" class="octicon octicon-alert-fill"> <path d="M4.855.708c.5-.896 1.79-.896 2.29 0l4.675 8.351a1.312 1.312 0 0 1-1.146 1.954H1.33A1.313 1.313 0 0 1 .183 9.058ZM7 7V3H5v4Zm-1 3a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z"></path> </svg> </span> <span></span> </div> </div> <div data-target="query-builder.screenReaderFeedback" aria-live="polite" aria-atomic="true" class="sr-only"></div> </query-builder></form> <div class="d-flex flex-row color-fg-muted px-3 text-small color-bg-default search-feedback-prompt"> <a target="_blank" href="https://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax" data-view-component="true" class="Link color-fg-accent text-normal ml-2">Search syntax tips</a> <div class="d-flex flex-1"></div> </div> </div> </div> </div> </modal-dialog></div> </div> <div data-action="click:qbsearch-input#retract" class="dark-backdrop position-fixed" hidden data-target="qbsearch-input.darkBackdrop"></div> <div class="color-fg-default"> <dialog-helper> <dialog data-target="qbsearch-input.feedbackDialog" data-action="close:qbsearch-input#handleDialogClose cancel:qbsearch-input#handleDialogClose" id="feedback-dialog" aria-modal="true" aria-labelledby="feedback-dialog-title" aria-describedby="feedback-dialog-description" data-view-component="true" class="Overlay Overlay-whenNarrow Overlay--size-medium Overlay--motion-scaleFade Overlay--disableScroll"> <div data-view-component="true" class="Overlay-header"> <div class="Overlay-headerContentWrap"> <div class="Overlay-titleWrap"> <h1 class="Overlay-title " id="feedback-dialog-title"> Provide feedback </h1> </div> <div class="Overlay-actionWrap"> <button data-close-dialog-id="feedback-dialog" aria-label="Close" type="button" data-view-component="true" class="close-button Overlay-closeButton"><svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x"> <path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"></path> </svg></button> </div> </div> </div> <scrollable-region data-labelled-by="feedback-dialog-title"> <div data-view-component="true" class="Overlay-body"> <!-- '"` --><!-- </textarea></xmp> --></option></form><form id="code-search-feedback-form" data-turbo="false" action="/search/feedback" accept-charset="UTF-8" method="post"><input type="hidden" data-csrf="true" name="authenticity_token" value="FX9LO6mP1dQJupTDE6vf2evE0k0gfBNmbg5RnXAUnksgz+JnlnKRlUyZDDAKiUW7g4H9uJ7AbLaEdybdFOCL/A==" /> <p>We read every piece of feedback, and take your input very seriously.</p> <textarea name="feedback" class="form-control width-full mb-2" style="height: 120px" id="feedback"></textarea> <input name="include_email" id="include_email" aria-label="Include my email address so I can be contacted" class="form-control mr-2" type="checkbox"> <label for="include_email" style="font-weight: normal">Include my email address so I can be contacted</label> </form></div> </scrollable-region> <div data-view-component="true" class="Overlay-footer Overlay-footer--alignEnd"> <button data-close-dialog-id="feedback-dialog" type="button" data-view-component="true" class="btn"> Cancel </button> <button form="code-search-feedback-form" data-action="click:qbsearch-input#submitFeedback" type="submit" data-view-component="true" class="btn-primary btn"> Submit feedback </button> </div> </dialog></dialog-helper> <custom-scopes data-target="qbsearch-input.customScopesManager"> <dialog-helper> <dialog data-target="custom-scopes.customScopesModalDialog" data-action="close:qbsearch-input#handleDialogClose cancel:qbsearch-input#handleDialogClose" id="custom-scopes-dialog" aria-modal="true" aria-labelledby="custom-scopes-dialog-title" aria-describedby="custom-scopes-dialog-description" data-view-component="true" class="Overlay Overlay-whenNarrow Overlay--size-medium Overlay--motion-scaleFade Overlay--disableScroll"> <div data-view-component="true" class="Overlay-header Overlay-header--divided"> <div class="Overlay-headerContentWrap"> <div class="Overlay-titleWrap"> <h1 class="Overlay-title " id="custom-scopes-dialog-title"> Saved searches </h1> <h2 id="custom-scopes-dialog-description" class="Overlay-description">Use saved searches to filter your results more quickly</h2> </div> <div class="Overlay-actionWrap"> <button data-close-dialog-id="custom-scopes-dialog" aria-label="Close" type="button" data-view-component="true" class="close-button Overlay-closeButton"><svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x"> <path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"></path> </svg></button> </div> </div> </div> <scrollable-region data-labelled-by="custom-scopes-dialog-title"> <div data-view-component="true" class="Overlay-body"> <div data-target="custom-scopes.customScopesModalDialogFlash"></div> <div hidden class="create-custom-scope-form" data-target="custom-scopes.createCustomScopeForm"> <!-- '"` --><!-- </textarea></xmp> --></option></form><form id="custom-scopes-dialog-form" data-turbo="false" action="/search/custom_scopes" accept-charset="UTF-8" method="post"><input type="hidden" data-csrf="true" name="authenticity_token" value="i/w7SCKf+Q3ghMkesKs9DrSDcuFLSh20GQgKE/lskeEMkPp7rWyPrBn0yyZYv4Xd6LYAl0+BeqwPxTWQZ/Jq0g==" /> <div data-target="custom-scopes.customScopesModalDialogFlash"></div> <input type="hidden" id="custom_scope_id" name="custom_scope_id" data-target="custom-scopes.customScopesIdField"> <div class="form-group"> <label for="custom_scope_name">Name</label> <auto-check src="/search/custom_scopes/check_name" required only-validate-on-blur="false"> <input type="text" name="custom_scope_name" id="custom_scope_name" data-target="custom-scopes.customScopesNameField" class="form-control" autocomplete="off" placeholder="github-ruby" required maxlength="50"> <input type="hidden" data-csrf="true" value="hbrTgJ2cELwz1GU71W9fRIYLvEnlQmf5KyHdAwh8GBWS+p0vcHzDnB+4GRzRZ8Ls76U0JGlwVl6/oJjIdOWj+A==" /> </auto-check> </div> <div class="form-group"> <label for="custom_scope_query">Query</label> <input type="text" name="custom_scope_query" id="custom_scope_query" data-target="custom-scopes.customScopesQueryField" class="form-control" autocomplete="off" placeholder="(repo:mona/a OR repo:mona/b) AND lang:python" required maxlength="500"> </div> <p class="text-small color-fg-muted"> To see all available qualifiers, see our <a class="Link--inTextBlock" href="https://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax">documentation</a>. </p> </form> </div> <div data-target="custom-scopes.manageCustomScopesForm"> <div data-target="custom-scopes.list"></div> </div> </div> </scrollable-region> <div data-view-component="true" class="Overlay-footer Overlay-footer--alignEnd Overlay-footer--divided"> <button data-action="click:custom-scopes#customScopesCancel" type="button" data-view-component="true" class="btn"> Cancel </button> <button form="custom-scopes-dialog-form" data-action="click:custom-scopes#customScopesSubmit" data-target="custom-scopes.customScopesSubmitButton" type="submit" data-view-component="true" class="btn-primary btn"> Create saved search </button> </div> </dialog></dialog-helper> </custom-scopes> </div> </qbsearch-input> <div class="position-relative HeaderMenu-link-wrap d-lg-inline-block"> <a href="/login?return_to=https%3A%2F%2Fgithub.com%2FRichardKnop%2Fmachinery" class="HeaderMenu-link HeaderMenu-link--sign-in HeaderMenu-button flex-shrink-0 no-underline d-none d-lg-inline-flex border border-lg-0 rounded rounded-lg-0 px-2 py-1" style="margin-left: 12px;" data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"site header menu","repository_id":null,"auth_type":"SIGN_UP","originating_url":"https://github.com/RichardKnop/machinery","user_id":null}}" data-hydro-click-hmac="3902138a0d67d5ba78d4829869475c71ae11ce417fdbba76fe96c76244a22e0c" data-analytics-event="{"category":"Marketing nav","action":"click to go to homepage","label":"ref_page:Marketing;ref_cta:Sign in;ref_loc:Header"}" > Sign in </a> </div> <a href="/signup?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E&source=header-repo&source_repo=RichardKnop%2Fmachinery" class="HeaderMenu-link HeaderMenu-link--sign-up HeaderMenu-button flex-shrink-0 d-flex d-lg-inline-flex no-underline border color-border-default rounded px-2 py-1" data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"site header menu","repository_id":null,"auth_type":"SIGN_UP","originating_url":"https://github.com/RichardKnop/machinery","user_id":null}}" data-hydro-click-hmac="3902138a0d67d5ba78d4829869475c71ae11ce417fdbba76fe96c76244a22e0c" data-analytics-event="{"category":"Sign up","action":"click to sign up for account","label":"ref_page:/<user-name>/<repo-name>;ref_cta:Sign up;ref_loc:header logged out"}" > Sign up </a> <button type="button" class="sr-only js-header-menu-focus-trap d-block d-lg-none">Reseting focus</button> </div> </div> </div> </div> </header> <div hidden="hidden" data-view-component="true" class="js-stale-session-flash stale-session-flash flash flash-warn flash-full"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-alert"> <path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path> </svg> <span class="js-stale-session-flash-signed-in" hidden>You signed in with another tab or window. <a class="Link--inTextBlock" href="">Reload</a> to refresh your session.</span> <span class="js-stale-session-flash-signed-out" hidden>You signed out in another tab or window. <a class="Link--inTextBlock" href="">Reload</a> to refresh your session.</span> <span class="js-stale-session-flash-switched" hidden>You switched accounts on another tab or window. <a class="Link--inTextBlock" href="">Reload</a> to refresh your session.</span> <button id="icon-button-f56cc7f2-e6f6-4b76-903c-5088babb2ef8" aria-labelledby="tooltip-39710c64-e50d-4208-a3af-26d3b412b981" type="button" data-view-component="true" class="Button Button--iconOnly Button--invisible Button--medium flash-close js-flash-close"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x Button-visual"> <path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"></path> </svg> </button><tool-tip id="tooltip-39710c64-e50d-4208-a3af-26d3b412b981" for="icon-button-f56cc7f2-e6f6-4b76-903c-5088babb2ef8" popover="manual" data-direction="s" data-type="label" data-view-component="true" class="sr-only position-absolute">Dismiss alert</tool-tip> </div> </div> <div id="start-of-content" class="show-on-focus"></div> <div id="js-flash-container" class="flash-container" data-turbo-replace> <template class="js-flash-template"> <div class="flash flash-full {{ className }}"> <div > <button autofocus class="flash-close js-flash-close" type="button" aria-label="Dismiss this message"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x"> <path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"></path> </svg> </button> <div aria-atomic="true" role="alert" class="js-flash-alert"> <div>{{ message }}</div> </div> </div> </div> </template> </div> <div class="application-main " data-commit-hovercards-enabled data-discussion-hovercards-enabled data-issue-and-pr-hovercards-enabled data-project-hovercards-enabled > <div itemscope itemtype="http://schema.org/SoftwareSourceCode" class=""> <main id="js-repo-pjax-container" > <div id="repository-container-header" class="pt-3 hide-full-screen" style="background-color: var(--page-header-bgColor, var(--color-page-header-bg));" data-turbo-replace> <div class="d-flex flex-nowrap flex-justify-end mb-3 px-3 px-lg-5" style="gap: 1rem;"> <div class="flex-auto min-width-0 width-fit"> <div class=" d-flex flex-wrap flex-items-center wb-break-word f3 text-normal"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-repo color-fg-muted mr-2"> <path d="M2 2.5A2.5 2.5 0 0 1 4.5 0h8.75a.75.75 0 0 1 .75.75v12.5a.75.75 0 0 1-.75.75h-2.5a.75.75 0 0 1 0-1.5h1.75v-2h-8a1 1 0 0 0-.714 1.7.75.75 0 1 1-1.072 1.05A2.495 2.495 0 0 1 2 11.5Zm10.5-1h-8a1 1 0 0 0-1 1v6.708A2.486 2.486 0 0 1 4.5 9h8ZM5 12.25a.25.25 0 0 1 .25-.25h3.5a.25.25 0 0 1 .25.25v3.25a.25.25 0 0 1-.4.2l-1.45-1.087a.249.249 0 0 0-.3 0L5.4 15.7a.25.25 0 0 1-.4-.2Z"></path> </svg> <span class="author flex-self-stretch" itemprop="author"> <a class="url fn" rel="author" data-hovercard-type="user" data-hovercard-url="/users/RichardKnop/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="/RichardKnop"> RichardKnop </a> </span> <span class="mx-1 flex-self-stretch color-fg-muted">/</span> <strong itemprop="name" class="mr-2 flex-self-stretch"> <a data-pjax="#repo-content-pjax-container" data-turbo-frame="repo-content-turbo-frame" href="/RichardKnop/machinery">machinery</a> </strong> <span></span><span class="Label Label--secondary v-align-middle mr-1">Public</span> </div> </div> <div id="repository-details-container" class="flex-shrink-0" data-turbo-replace style="max-width: 70%;"> <ul class="pagehead-actions flex-shrink-0 d-none d-md-inline" style="padding: 2px 0;"> <li> <a href="/login?return_to=%2FRichardKnop%2Fmachinery" rel="nofollow" id="repository-details-watch-button" data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"notification subscription menu watch","repository_id":null,"auth_type":"LOG_IN","originating_url":"https://github.com/RichardKnop/machinery","user_id":null}}" data-hydro-click-hmac="c36b15a3c89aba40c051867cda9dc3a7dbcf68ca02f6b5217c5353bb34485862" aria-label="You must be signed in to change notification settings" data-view-component="true" class="btn-sm btn"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-bell mr-2"> <path d="M8 16a2 2 0 0 0 1.985-1.75c.017-.137-.097-.25-.235-.25h-3.5c-.138 0-.252.113-.235.25A2 2 0 0 0 8 16ZM3 5a5 5 0 0 1 10 0v2.947c0 .05.015.098.042.139l1.703 2.555A1.519 1.519 0 0 1 13.482 13H2.518a1.516 1.516 0 0 1-1.263-2.36l1.703-2.554A.255.255 0 0 0 3 7.947Zm5-3.5A3.5 3.5 0 0 0 4.5 5v2.947c0 .346-.102.683-.294.97l-1.703 2.556a.017.017 0 0 0-.003.01l.001.006c0 .002.002.004.004.006l.006.004.007.001h10.964l.007-.001.006-.004.004-.006.001-.007a.017.017 0 0 0-.003-.01l-1.703-2.554a1.745 1.745 0 0 1-.294-.97V5A3.5 3.5 0 0 0 8 1.5Z"></path> </svg>Notifications </a> <tool-tip id="tooltip-3fab035d-2839-4134-ac82-2a2d7d9844d8" for="repository-details-watch-button" popover="manual" data-direction="s" data-type="description" data-view-component="true" class="sr-only position-absolute">You must be signed in to change notification settings</tool-tip> </li> <li> <a icon="repo-forked" id="fork-button" href="/login?return_to=%2FRichardKnop%2Fmachinery" rel="nofollow" data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"repo details fork button","repository_id":33452903,"auth_type":"LOG_IN","originating_url":"https://github.com/RichardKnop/machinery","user_id":null}}" data-hydro-click-hmac="668ecb11d7ca404735830e5dcb23a56176903c48164985cd03bcfdefb92c265b" data-view-component="true" class="btn-sm btn"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-repo-forked mr-2"> <path d="M5 5.372v.878c0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75v-.878a2.25 2.25 0 1 1 1.5 0v.878a2.25 2.25 0 0 1-2.25 2.25h-1.5v2.128a2.251 2.251 0 1 1-1.5 0V8.5h-1.5A2.25 2.25 0 0 1 3.5 6.25v-.878a2.25 2.25 0 1 1 1.5 0ZM5 3.25a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Zm6.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm-3 8.75a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Z"></path> </svg>Fork <span id="repo-network-counter" data-pjax-replace="true" data-turbo-replace="true" title="929" data-view-component="true" class="Counter">929</span> </a> </li> <li> <div data-view-component="true" class="BtnGroup d-flex"> <a href="/login?return_to=%2FRichardKnop%2Fmachinery" rel="nofollow" data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"star button","repository_id":33452903,"auth_type":"LOG_IN","originating_url":"https://github.com/RichardKnop/machinery","user_id":null}}" data-hydro-click-hmac="6d205b5a8a1eb90bbf4e0bf636ebc0f4bdf7fe5a78f4a7139ab71e7fa2f9b83f" aria-label="You must be signed in to star a repository" data-view-component="true" class="tooltipped tooltipped-sw btn-sm btn"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-star v-align-text-bottom d-inline-block mr-2"> <path d="M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Zm0 2.445L6.615 5.5a.75.75 0 0 1-.564.41l-3.097.45 2.24 2.184a.75.75 0 0 1 .216.664l-.528 3.084 2.769-1.456a.75.75 0 0 1 .698 0l2.77 1.456-.53-3.084a.75.75 0 0 1 .216-.664l2.24-2.183-3.096-.45a.75.75 0 0 1-.564-.41L8 2.694Z"></path> </svg><span data-view-component="true" class="d-inline"> Star </span> <span id="repo-stars-counter-star" aria-label="7685 users starred this repository" data-singular-suffix="user starred this repository" data-plural-suffix="users starred this repository" data-turbo-replace="true" title="7,685" data-view-component="true" class="Counter js-social-count">7.7k</span> </a></div> </li> </ul> </div> </div> <div id="responsive-meta-container" data-turbo-replace> <div class="d-block d-md-none mb-2 px-3 px-md-4 px-lg-5"> <p class="f4 mb-3 "> Machinery is an asynchronous task queue/job queue based on distributed message passing. </p> <h3 class="sr-only">License</h3> <div class="mb-2"> <a href="/RichardKnop/machinery/blob/master/LICENSE" class="Link--muted" data-analytics-event="{"category":"Repository Overview","action":"click","label":"location:sidebar;file:license"}" > <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-law mr-2"> <path d="M8.75.75V2h.985c.304 0 .603.08.867.231l1.29.736c.038.022.08.033.124.033h2.234a.75.75 0 0 1 0 1.5h-.427l2.111 4.692a.75.75 0 0 1-.154.838l-.53-.53.529.531-.001.002-.002.002-.006.006-.006.005-.01.01-.045.04c-.21.176-.441.327-.686.45C14.556 10.78 13.88 11 13 11a4.498 4.498 0 0 1-2.023-.454 3.544 3.544 0 0 1-.686-.45l-.045-.04-.016-.015-.006-.006-.004-.004v-.001a.75.75 0 0 1-.154-.838L12.178 4.5h-.162c-.305 0-.604-.079-.868-.231l-1.29-.736a.245.245 0 0 0-.124-.033H8.75V13h2.5a.75.75 0 0 1 0 1.5h-6.5a.75.75 0 0 1 0-1.5h2.5V3.5h-.984a.245.245 0 0 0-.124.033l-1.289.737c-.265.15-.564.23-.869.23h-.162l2.112 4.692a.75.75 0 0 1-.154.838l-.53-.53.529.531-.001.002-.002.002-.006.006-.016.015-.045.04c-.21.176-.441.327-.686.45C4.556 10.78 3.88 11 3 11a4.498 4.498 0 0 1-2.023-.454 3.544 3.544 0 0 1-.686-.45l-.045-.04-.016-.015-.006-.006-.004-.004v-.001a.75.75 0 0 1-.154-.838L2.178 4.5H1.75a.75.75 0 0 1 0-1.5h2.234a.249.249 0 0 0 .125-.033l1.288-.737c.265-.15.564-.23.869-.23h.984V.75a.75.75 0 0 1 1.5 0Zm2.945 8.477c.285.135.718.273 1.305.273s1.02-.138 1.305-.273L13 6.327Zm-10 0c.285.135.718.273 1.305.273s1.02-.138 1.305-.273L3 6.327Z"></path> </svg> MPL-2.0 license </a> </div> <div class="mb-3"> <a class="Link--secondary no-underline mr-3" href="/RichardKnop/machinery/stargazers"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-star mr-1"> <path d="M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Zm0 2.445L6.615 5.5a.75.75 0 0 1-.564.41l-3.097.45 2.24 2.184a.75.75 0 0 1 .216.664l-.528 3.084 2.769-1.456a.75.75 0 0 1 .698 0l2.77 1.456-.53-3.084a.75.75 0 0 1 .216-.664l2.24-2.183-3.096-.45a.75.75 0 0 1-.564-.41L8 2.694Z"></path> </svg> <span class="text-bold">7.7k</span> stars </a> <a class="Link--secondary no-underline mr-3" href="/RichardKnop/machinery/forks"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-repo-forked mr-1"> <path d="M5 5.372v.878c0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75v-.878a2.25 2.25 0 1 1 1.5 0v.878a2.25 2.25 0 0 1-2.25 2.25h-1.5v2.128a2.251 2.251 0 1 1-1.5 0V8.5h-1.5A2.25 2.25 0 0 1 3.5 6.25v-.878a2.25 2.25 0 1 1 1.5 0ZM5 3.25a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Zm6.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm-3 8.75a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Z"></path> </svg> <span class="text-bold">929</span> forks </a> <a class="Link--secondary no-underline mr-3 d-inline-block" href="/RichardKnop/machinery/branches"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-git-branch mr-1"> <path d="M9.5 3.25a2.25 2.25 0 1 1 3 2.122V6A2.5 2.5 0 0 1 10 8.5H6a1 1 0 0 0-1 1v1.128a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0v1.836A2.493 2.493 0 0 1 6 7h4a1 1 0 0 0 1-1v-.628A2.25 2.25 0 0 1 9.5 3.25Zm-6 0a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Zm8.25-.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM4.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z"></path> </svg> <span>Branches</span> </a> <a class="Link--secondary no-underline d-inline-block" href="/RichardKnop/machinery/tags"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-tag mr-1"> <path d="M1 7.775V2.75C1 1.784 1.784 1 2.75 1h5.025c.464 0 .91.184 1.238.513l6.25 6.25a1.75 1.75 0 0 1 0 2.474l-5.026 5.026a1.75 1.75 0 0 1-2.474 0l-6.25-6.25A1.752 1.752 0 0 1 1 7.775Zm1.5 0c0 .066.026.13.073.177l6.25 6.25a.25.25 0 0 0 .354 0l5.025-5.025a.25.25 0 0 0 0-.354l-6.25-6.25a.25.25 0 0 0-.177-.073H2.75a.25.25 0 0 0-.25.25ZM6 5a1 1 0 1 1 0 2 1 1 0 0 1 0-2Z"></path> </svg> <span>Tags</span> </a> <a class="Link--secondary no-underline d-inline-block" href="/RichardKnop/machinery/activity"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-pulse mr-1"> <path d="M6 2c.306 0 .582.187.696.471L10 10.731l1.304-3.26A.751.751 0 0 1 12 7h3.25a.75.75 0 0 1 0 1.5h-2.742l-1.812 4.528a.751.751 0 0 1-1.392 0L6 4.77 4.696 8.03A.75.75 0 0 1 4 8.5H.75a.75.75 0 0 1 0-1.5h2.742l1.812-4.529A.751.751 0 0 1 6 2Z"></path> </svg> <span>Activity</span> </a> </div> <div class="d-flex flex-wrap gap-2"> <div class="flex-1"> <div data-view-component="true" class="BtnGroup d-flex"> <a href="/login?return_to=%2FRichardKnop%2Fmachinery" rel="nofollow" data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"star button","repository_id":33452903,"auth_type":"LOG_IN","originating_url":"https://github.com/RichardKnop/machinery","user_id":null}}" data-hydro-click-hmac="6d205b5a8a1eb90bbf4e0bf636ebc0f4bdf7fe5a78f4a7139ab71e7fa2f9b83f" aria-label="You must be signed in to star a repository" data-view-component="true" class="tooltipped tooltipped-sw btn-sm btn btn-block"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-star v-align-text-bottom d-inline-block mr-2"> <path d="M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Zm0 2.445L6.615 5.5a.75.75 0 0 1-.564.41l-3.097.45 2.24 2.184a.75.75 0 0 1 .216.664l-.528 3.084 2.769-1.456a.75.75 0 0 1 .698 0l2.77 1.456-.53-3.084a.75.75 0 0 1 .216-.664l2.24-2.183-3.096-.45a.75.75 0 0 1-.564-.41L8 2.694Z"></path> </svg><span data-view-component="true" class="d-inline"> Star </span> </a></div> </div> <div class="flex-1"> <a href="/login?return_to=%2FRichardKnop%2Fmachinery" rel="nofollow" id="files-overview-watch-button" data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"notification subscription menu watch","repository_id":null,"auth_type":"LOG_IN","originating_url":"https://github.com/RichardKnop/machinery","user_id":null}}" data-hydro-click-hmac="c36b15a3c89aba40c051867cda9dc3a7dbcf68ca02f6b5217c5353bb34485862" aria-label="You must be signed in to change notification settings" data-view-component="true" class="btn-sm btn btn-block"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-bell mr-2"> <path d="M8 16a2 2 0 0 0 1.985-1.75c.017-.137-.097-.25-.235-.25h-3.5c-.138 0-.252.113-.235.25A2 2 0 0 0 8 16ZM3 5a5 5 0 0 1 10 0v2.947c0 .05.015.098.042.139l1.703 2.555A1.519 1.519 0 0 1 13.482 13H2.518a1.516 1.516 0 0 1-1.263-2.36l1.703-2.554A.255.255 0 0 0 3 7.947Zm5-3.5A3.5 3.5 0 0 0 4.5 5v2.947c0 .346-.102.683-.294.97l-1.703 2.556a.017.017 0 0 0-.003.01l.001.006c0 .002.002.004.004.006l.006.004.007.001h10.964l.007-.001.006-.004.004-.006.001-.007a.017.017 0 0 0-.003-.01l-1.703-2.554a1.745 1.745 0 0 1-.294-.97V5A3.5 3.5 0 0 0 8 1.5Z"></path> </svg>Notifications </a> <tool-tip id="tooltip-f7d42621-4e71-493f-9f95-ab9025029335" for="files-overview-watch-button" popover="manual" data-direction="s" data-type="description" data-view-component="true" class="sr-only position-absolute">You must be signed in to change notification settings</tool-tip> </div> <span> </span> </div> </div> </div> <nav data-pjax="#js-repo-pjax-container" aria-label="Repository" data-view-component="true" class="js-repo-nav js-sidenav-container-pjax js-responsive-underlinenav overflow-hidden UnderlineNav px-3 px-md-4 px-lg-5"> <ul data-view-component="true" class="UnderlineNav-body list-style-none"> <li data-view-component="true" class="d-inline-flex"> <a id="code-tab" href="/RichardKnop/machinery" data-tab-item="i0code-tab" data-selected-links="repo_source repo_downloads repo_commits repo_releases repo_tags repo_branches repo_packages repo_deployments repo_attestations /RichardKnop/machinery" data-pjax="#repo-content-pjax-container" data-turbo-frame="repo-content-turbo-frame" data-hotkey="g c" data-analytics-event="{"category":"Underline navbar","action":"Click tab","label":"Code","target":"UNDERLINE_NAV.TAB"}" aria-current="page" data-view-component="true" class="UnderlineNav-item no-wrap js-responsive-underlinenav-item js-selected-navigation-item selected"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-code UnderlineNav-octicon d-none d-sm-inline"> <path d="m11.28 3.22 4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L13.94 8l-3.72-3.72a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215Zm-6.56 0a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042L2.06 8l3.72 3.72a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L.47 8.53a.75.75 0 0 1 0-1.06Z"></path> </svg> <span data-content="Code">Code</span> <span id="code-repo-tab-count" data-pjax-replace="" data-turbo-replace="" title="Not available" data-view-component="true" class="Counter"></span> </a></li> <li data-view-component="true" class="d-inline-flex"> <a id="issues-tab" href="/RichardKnop/machinery/issues" data-tab-item="i1issues-tab" data-selected-links="repo_issues repo_labels repo_milestones /RichardKnop/machinery/issues" data-pjax="#repo-content-pjax-container" data-turbo-frame="repo-content-turbo-frame" data-hotkey="g i" data-analytics-event="{"category":"Underline navbar","action":"Click tab","label":"Issues","target":"UNDERLINE_NAV.TAB"}" data-view-component="true" class="UnderlineNav-item no-wrap js-responsive-underlinenav-item js-selected-navigation-item"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-issue-opened UnderlineNav-octicon d-none d-sm-inline"> <path d="M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"></path><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Z"></path> </svg> <span data-content="Issues">Issues</span> <span id="issues-repo-tab-count" data-pjax-replace="" data-turbo-replace="" title="212" data-view-component="true" class="Counter">212</span> </a></li> <li data-view-component="true" class="d-inline-flex"> <a id="pull-requests-tab" href="/RichardKnop/machinery/pulls" data-tab-item="i2pull-requests-tab" data-selected-links="repo_pulls checks /RichardKnop/machinery/pulls" data-pjax="#repo-content-pjax-container" data-turbo-frame="repo-content-turbo-frame" data-hotkey="g p" data-analytics-event="{"category":"Underline navbar","action":"Click tab","label":"Pull requests","target":"UNDERLINE_NAV.TAB"}" data-view-component="true" class="UnderlineNav-item no-wrap js-responsive-underlinenav-item js-selected-navigation-item"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-git-pull-request UnderlineNav-octicon d-none d-sm-inline"> <path d="M1.5 3.25a2.25 2.25 0 1 1 3 2.122v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.25 2.25 0 0 1 1.5 3.25Zm5.677-.177L9.573.677A.25.25 0 0 1 10 .854V2.5h1A2.5 2.5 0 0 1 13.5 5v5.628a2.251 2.251 0 1 1-1.5 0V5a1 1 0 0 0-1-1h-1v1.646a.25.25 0 0 1-.427.177L7.177 3.427a.25.25 0 0 1 0-.354ZM3.75 2.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm0 9.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm8.25.75a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Z"></path> </svg> <span data-content="Pull requests">Pull requests</span> <span id="pull-requests-repo-tab-count" data-pjax-replace="" data-turbo-replace="" title="30" data-view-component="true" class="Counter">30</span> </a></li> <li data-view-component="true" class="d-inline-flex"> <a id="actions-tab" href="/RichardKnop/machinery/actions" data-tab-item="i3actions-tab" data-selected-links="repo_actions /RichardKnop/machinery/actions" data-pjax="#repo-content-pjax-container" data-turbo-frame="repo-content-turbo-frame" data-hotkey="g a" data-analytics-event="{"category":"Underline navbar","action":"Click tab","label":"Actions","target":"UNDERLINE_NAV.TAB"}" data-view-component="true" class="UnderlineNav-item no-wrap js-responsive-underlinenav-item js-selected-navigation-item"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-play UnderlineNav-octicon d-none d-sm-inline"> <path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm4.879-2.773 4.264 2.559a.25.25 0 0 1 0 .428l-4.264 2.559A.25.25 0 0 1 6 10.559V5.442a.25.25 0 0 1 .379-.215Z"></path> </svg> <span data-content="Actions">Actions</span> <span id="actions-repo-tab-count" data-pjax-replace="" data-turbo-replace="" title="Not available" data-view-component="true" class="Counter"></span> </a></li> <li data-view-component="true" class="d-inline-flex"> <a id="projects-tab" href="/RichardKnop/machinery/projects" data-tab-item="i4projects-tab" data-selected-links="repo_projects new_repo_project repo_project /RichardKnop/machinery/projects" data-pjax="#repo-content-pjax-container" data-turbo-frame="repo-content-turbo-frame" data-hotkey="g b" data-analytics-event="{"category":"Underline navbar","action":"Click tab","label":"Projects","target":"UNDERLINE_NAV.TAB"}" data-view-component="true" class="UnderlineNav-item no-wrap js-responsive-underlinenav-item js-selected-navigation-item"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-table UnderlineNav-octicon d-none d-sm-inline"> <path d="M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25ZM6.5 6.5v8h7.75a.25.25 0 0 0 .25-.25V6.5Zm8-1.5V1.75a.25.25 0 0 0-.25-.25H6.5V5Zm-13 1.5v7.75c0 .138.112.25.25.25H5v-8ZM5 5V1.5H1.75a.25.25 0 0 0-.25.25V5Z"></path> </svg> <span data-content="Projects">Projects</span> <span id="projects-repo-tab-count" data-pjax-replace="" data-turbo-replace="" title="0" hidden="hidden" data-view-component="true" class="Counter">0</span> </a></li> <li data-view-component="true" class="d-inline-flex"> <a id="wiki-tab" href="/RichardKnop/machinery/wiki" data-tab-item="i5wiki-tab" data-selected-links="repo_wiki /RichardKnop/machinery/wiki" data-pjax="#repo-content-pjax-container" data-turbo-frame="repo-content-turbo-frame" data-hotkey="g w" data-analytics-event="{"category":"Underline navbar","action":"Click tab","label":"Wiki","target":"UNDERLINE_NAV.TAB"}" data-view-component="true" class="UnderlineNav-item no-wrap js-responsive-underlinenav-item js-selected-navigation-item"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-book UnderlineNav-octicon d-none d-sm-inline"> <path d="M0 1.75A.75.75 0 0 1 .75 1h4.253c1.227 0 2.317.59 3 1.501A3.743 3.743 0 0 1 11.006 1h4.245a.75.75 0 0 1 .75.75v10.5a.75.75 0 0 1-.75.75h-4.507a2.25 2.25 0 0 0-1.591.659l-.622.621a.75.75 0 0 1-1.06 0l-.622-.621A2.25 2.25 0 0 0 5.258 13H.75a.75.75 0 0 1-.75-.75Zm7.251 10.324.004-5.073-.002-2.253A2.25 2.25 0 0 0 5.003 2.5H1.5v9h3.757a3.75 3.75 0 0 1 1.994.574ZM8.755 4.75l-.004 7.322a3.752 3.752 0 0 1 1.992-.572H14.5v-9h-3.495a2.25 2.25 0 0 0-2.25 2.25Z"></path> </svg> <span data-content="Wiki">Wiki</span> <span id="wiki-repo-tab-count" data-pjax-replace="" data-turbo-replace="" title="Not available" data-view-component="true" class="Counter"></span> </a></li> <li data-view-component="true" class="d-inline-flex"> <a id="security-tab" href="/RichardKnop/machinery/security" data-tab-item="i6security-tab" data-selected-links="security overview alerts policy token_scanning code_scanning /RichardKnop/machinery/security" data-pjax="#repo-content-pjax-container" data-turbo-frame="repo-content-turbo-frame" data-hotkey="g s" data-analytics-event="{"category":"Underline navbar","action":"Click tab","label":"Security","target":"UNDERLINE_NAV.TAB"}" data-view-component="true" class="UnderlineNav-item no-wrap js-responsive-underlinenav-item js-selected-navigation-item"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-shield UnderlineNav-octicon d-none d-sm-inline"> <path d="M7.467.133a1.748 1.748 0 0 1 1.066 0l5.25 1.68A1.75 1.75 0 0 1 15 3.48V7c0 1.566-.32 3.182-1.303 4.682-.983 1.498-2.585 2.813-5.032 3.855a1.697 1.697 0 0 1-1.33 0c-2.447-1.042-4.049-2.357-5.032-3.855C1.32 10.182 1 8.566 1 7V3.48a1.75 1.75 0 0 1 1.217-1.667Zm.61 1.429a.25.25 0 0 0-.153 0l-5.25 1.68a.25.25 0 0 0-.174.238V7c0 1.358.275 2.666 1.057 3.86.784 1.194 2.121 2.34 4.366 3.297a.196.196 0 0 0 .154 0c2.245-.956 3.582-2.104 4.366-3.298C13.225 9.666 13.5 8.36 13.5 7V3.48a.251.251 0 0 0-.174-.237l-5.25-1.68ZM8.75 4.75v3a.75.75 0 0 1-1.5 0v-3a.75.75 0 0 1 1.5 0ZM9 10.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path> </svg> <span data-content="Security">Security</span> <include-fragment src="/RichardKnop/machinery/security/overall-count" accept="text/fragment+html"></include-fragment> </a></li> <li data-view-component="true" class="d-inline-flex"> <a id="insights-tab" href="/RichardKnop/machinery/pulse" data-tab-item="i7insights-tab" data-selected-links="repo_graphs repo_contributors dependency_graph dependabot_updates pulse people community /RichardKnop/machinery/pulse" data-pjax="#repo-content-pjax-container" data-turbo-frame="repo-content-turbo-frame" data-analytics-event="{"category":"Underline navbar","action":"Click tab","label":"Insights","target":"UNDERLINE_NAV.TAB"}" data-view-component="true" class="UnderlineNav-item no-wrap js-responsive-underlinenav-item js-selected-navigation-item"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-graph UnderlineNav-octicon d-none d-sm-inline"> <path d="M1.5 1.75V13.5h13.75a.75.75 0 0 1 0 1.5H.75a.75.75 0 0 1-.75-.75V1.75a.75.75 0 0 1 1.5 0Zm14.28 2.53-5.25 5.25a.75.75 0 0 1-1.06 0L7 7.06 4.28 9.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.25-3.25a.75.75 0 0 1 1.06 0L10 7.94l4.72-4.72a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"></path> </svg> <span data-content="Insights">Insights</span> <span id="insights-repo-tab-count" data-pjax-replace="" data-turbo-replace="" title="Not available" data-view-component="true" class="Counter"></span> </a></li> </ul> <div style="visibility:hidden;" data-view-component="true" class="UnderlineNav-actions js-responsive-underlinenav-overflow position-absolute pr-3 pr-md-4 pr-lg-5 right-0"> <action-menu data-select-variant="none" data-view-component="true"> <focus-group direction="vertical" mnemonics retain> <button id="action-menu-b29a9895-71b3-49d0-928c-5134636c5810-button" popovertarget="action-menu-b29a9895-71b3-49d0-928c-5134636c5810-overlay" aria-controls="action-menu-b29a9895-71b3-49d0-928c-5134636c5810-list" aria-haspopup="true" aria-labelledby="tooltip-fa8df02a-2f1c-4057-a354-2dd3d74b4f7d" type="button" data-view-component="true" class="Button Button--iconOnly Button--secondary Button--medium UnderlineNav-item"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-kebab-horizontal Button-visual"> <path d="M8 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM1.5 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Zm13 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"></path> </svg> </button><tool-tip id="tooltip-fa8df02a-2f1c-4057-a354-2dd3d74b4f7d" for="action-menu-b29a9895-71b3-49d0-928c-5134636c5810-button" popover="manual" data-direction="s" data-type="label" data-view-component="true" class="sr-only position-absolute">Additional navigation options</tool-tip> <anchored-position data-target="action-menu.overlay" id="action-menu-b29a9895-71b3-49d0-928c-5134636c5810-overlay" anchor="action-menu-b29a9895-71b3-49d0-928c-5134636c5810-button" align="start" side="outside-bottom" anchor-offset="normal" popover="auto" data-view-component="true"> <div data-view-component="true" class="Overlay Overlay--size-auto"> <div data-view-component="true" class="Overlay-body Overlay-body--paddingNone"> <action-list> <div data-view-component="true"> <ul aria-labelledby="action-menu-b29a9895-71b3-49d0-928c-5134636c5810-button" id="action-menu-b29a9895-71b3-49d0-928c-5134636c5810-list" role="menu" data-view-component="true" class="ActionListWrap--inset ActionListWrap"> <li hidden="hidden" data-menu-item="i0code-tab" data-targets="action-list.items" role="none" data-view-component="true" class="ActionListItem"> <a tabindex="-1" id="item-13d09021-0c9b-482b-ad1e-4e5805e9c56c" href="/RichardKnop/machinery" role="menuitem" data-view-component="true" class="ActionListContent ActionListContent--visual16"> <span class="ActionListItem-visual ActionListItem-visual--leading"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-code"> <path d="m11.28 3.22 4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L13.94 8l-3.72-3.72a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215Zm-6.56 0a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042L2.06 8l3.72 3.72a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L.47 8.53a.75.75 0 0 1 0-1.06Z"></path> </svg> </span> <span data-view-component="true" class="ActionListItem-label"> Code </span> </a> </li> <li hidden="hidden" data-menu-item="i1issues-tab" data-targets="action-list.items" role="none" data-view-component="true" class="ActionListItem"> <a tabindex="-1" id="item-194b0a0d-30dd-4787-adc3-f69989de4a8e" href="/RichardKnop/machinery/issues" role="menuitem" data-view-component="true" class="ActionListContent ActionListContent--visual16"> <span class="ActionListItem-visual ActionListItem-visual--leading"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-issue-opened"> <path d="M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"></path><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Z"></path> </svg> </span> <span data-view-component="true" class="ActionListItem-label"> Issues </span> </a> </li> <li hidden="hidden" data-menu-item="i2pull-requests-tab" data-targets="action-list.items" role="none" data-view-component="true" class="ActionListItem"> <a tabindex="-1" id="item-7556bc6f-a845-415c-beb1-393508522e12" href="/RichardKnop/machinery/pulls" role="menuitem" data-view-component="true" class="ActionListContent ActionListContent--visual16"> <span class="ActionListItem-visual ActionListItem-visual--leading"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-git-pull-request"> <path d="M1.5 3.25a2.25 2.25 0 1 1 3 2.122v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.25 2.25 0 0 1 1.5 3.25Zm5.677-.177L9.573.677A.25.25 0 0 1 10 .854V2.5h1A2.5 2.5 0 0 1 13.5 5v5.628a2.251 2.251 0 1 1-1.5 0V5a1 1 0 0 0-1-1h-1v1.646a.25.25 0 0 1-.427.177L7.177 3.427a.25.25 0 0 1 0-.354ZM3.75 2.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm0 9.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm8.25.75a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Z"></path> </svg> </span> <span data-view-component="true" class="ActionListItem-label"> Pull requests </span> </a> </li> <li hidden="hidden" data-menu-item="i3actions-tab" data-targets="action-list.items" role="none" data-view-component="true" class="ActionListItem"> <a tabindex="-1" id="item-e6536ecd-9ec2-4c0c-9ed1-a478fa058c50" href="/RichardKnop/machinery/actions" role="menuitem" data-view-component="true" class="ActionListContent ActionListContent--visual16"> <span class="ActionListItem-visual ActionListItem-visual--leading"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-play"> <path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm4.879-2.773 4.264 2.559a.25.25 0 0 1 0 .428l-4.264 2.559A.25.25 0 0 1 6 10.559V5.442a.25.25 0 0 1 .379-.215Z"></path> </svg> </span> <span data-view-component="true" class="ActionListItem-label"> Actions </span> </a> </li> <li hidden="hidden" data-menu-item="i4projects-tab" data-targets="action-list.items" role="none" data-view-component="true" class="ActionListItem"> <a tabindex="-1" id="item-2422f98f-5d4e-436b-b43f-2d74c095af26" href="/RichardKnop/machinery/projects" role="menuitem" data-view-component="true" class="ActionListContent ActionListContent--visual16"> <span class="ActionListItem-visual ActionListItem-visual--leading"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-table"> <path d="M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25ZM6.5 6.5v8h7.75a.25.25 0 0 0 .25-.25V6.5Zm8-1.5V1.75a.25.25 0 0 0-.25-.25H6.5V5Zm-13 1.5v7.75c0 .138.112.25.25.25H5v-8ZM5 5V1.5H1.75a.25.25 0 0 0-.25.25V5Z"></path> </svg> </span> <span data-view-component="true" class="ActionListItem-label"> Projects </span> </a> </li> <li hidden="hidden" data-menu-item="i5wiki-tab" data-targets="action-list.items" role="none" data-view-component="true" class="ActionListItem"> <a tabindex="-1" id="item-0b6caaf6-3bbc-49cf-9d5a-d5ee9cb80286" href="/RichardKnop/machinery/wiki" role="menuitem" data-view-component="true" class="ActionListContent ActionListContent--visual16"> <span class="ActionListItem-visual ActionListItem-visual--leading"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-book"> <path d="M0 1.75A.75.75 0 0 1 .75 1h4.253c1.227 0 2.317.59 3 1.501A3.743 3.743 0 0 1 11.006 1h4.245a.75.75 0 0 1 .75.75v10.5a.75.75 0 0 1-.75.75h-4.507a2.25 2.25 0 0 0-1.591.659l-.622.621a.75.75 0 0 1-1.06 0l-.622-.621A2.25 2.25 0 0 0 5.258 13H.75a.75.75 0 0 1-.75-.75Zm7.251 10.324.004-5.073-.002-2.253A2.25 2.25 0 0 0 5.003 2.5H1.5v9h3.757a3.75 3.75 0 0 1 1.994.574ZM8.755 4.75l-.004 7.322a3.752 3.752 0 0 1 1.992-.572H14.5v-9h-3.495a2.25 2.25 0 0 0-2.25 2.25Z"></path> </svg> </span> <span data-view-component="true" class="ActionListItem-label"> Wiki </span> </a> </li> <li hidden="hidden" data-menu-item="i6security-tab" data-targets="action-list.items" role="none" data-view-component="true" class="ActionListItem"> <a tabindex="-1" id="item-c692349b-ceec-4d74-9afa-b5fab18b8566" href="/RichardKnop/machinery/security" role="menuitem" data-view-component="true" class="ActionListContent ActionListContent--visual16"> <span class="ActionListItem-visual ActionListItem-visual--leading"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-shield"> <path d="M7.467.133a1.748 1.748 0 0 1 1.066 0l5.25 1.68A1.75 1.75 0 0 1 15 3.48V7c0 1.566-.32 3.182-1.303 4.682-.983 1.498-2.585 2.813-5.032 3.855a1.697 1.697 0 0 1-1.33 0c-2.447-1.042-4.049-2.357-5.032-3.855C1.32 10.182 1 8.566 1 7V3.48a1.75 1.75 0 0 1 1.217-1.667Zm.61 1.429a.25.25 0 0 0-.153 0l-5.25 1.68a.25.25 0 0 0-.174.238V7c0 1.358.275 2.666 1.057 3.86.784 1.194 2.121 2.34 4.366 3.297a.196.196 0 0 0 .154 0c2.245-.956 3.582-2.104 4.366-3.298C13.225 9.666 13.5 8.36 13.5 7V3.48a.251.251 0 0 0-.174-.237l-5.25-1.68ZM8.75 4.75v3a.75.75 0 0 1-1.5 0v-3a.75.75 0 0 1 1.5 0ZM9 10.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path> </svg> </span> <span data-view-component="true" class="ActionListItem-label"> Security </span> </a> </li> <li hidden="hidden" data-menu-item="i7insights-tab" data-targets="action-list.items" role="none" data-view-component="true" class="ActionListItem"> <a tabindex="-1" id="item-724b1efb-4028-4291-acf3-b8091033da70" href="/RichardKnop/machinery/pulse" role="menuitem" data-view-component="true" class="ActionListContent ActionListContent--visual16"> <span class="ActionListItem-visual ActionListItem-visual--leading"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-graph"> <path d="M1.5 1.75V13.5h13.75a.75.75 0 0 1 0 1.5H.75a.75.75 0 0 1-.75-.75V1.75a.75.75 0 0 1 1.5 0Zm14.28 2.53-5.25 5.25a.75.75 0 0 1-1.06 0L7 7.06 4.28 9.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.25-3.25a.75.75 0 0 1 1.06 0L10 7.94l4.72-4.72a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"></path> </svg> </span> <span data-view-component="true" class="ActionListItem-label"> Insights </span> </a> </li> </ul> </div></action-list> </div> </div></anchored-position> </focus-group> </action-menu></div> </nav> </div> <turbo-frame id="repo-content-turbo-frame" target="_top" data-turbo-action="advance" class=""> <div id="repo-content-pjax-container" class="repository-content " > <h1 class='sr-only'>RichardKnop/machinery</h1> <div class="clearfix container-xl px-md-4 px-lg-5 px-3"> <div> <div style="max-width: 100%" data-view-component="true" class="Layout Layout--flowRow-until-md react-repos-overview-margin Layout--sidebarPosition-end Layout--sidebarPosition-flowRow-end"> <div data-view-component="true" class="Layout-main"> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_dompurify_dist_purify_es_mjs-dd1d3ea6a436.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/vendors-node_modules_tanstack_query-core_build_modern_queryObserver_js-node_modules_tanstack_-defd52-843b41414e0e.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/ui_packages_aria-live_aria-live_ts-ui_packages_promise-with-resolvers-polyfill_promise-with-r-17c672-34345cb18aac.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/ui_packages_paths_index_ts-e019c54eb886.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/ui_packages_ref-selector_RefSelector_tsx-7496afc3784d.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/ui_packages_commit-attribution_index_ts-ui_packages_commit-checks-status_index_ts-ui_packages-7094d4-15017f02e61c.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/ui_packages_code-view-shared_hooks_shortcuts_ts-ui_packages_code-view-shared_utilities_styles-0dc246-f8753c5db08d.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/ui_packages_code-view-shared_hooks_use-canonical-object_ts-ui_packages_code-view-shared_hooks-a83ec0-5ee2b562b57f.js"></script> <script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/repos-overview-ca785c0ab4fa.js"></script> <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-react.e0c9f0687c56358ed85e.module.css" /> <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/repos-overview.0ee7cac3ab511a65d9f9.module.css" /> <react-partial partial-name="repos-overview" data-ssr="true" data-attempted-ssr="true" > <script type="application/json" data-target="react-partial.embeddedData">{"props":{"initialPayload":{"allShortcutsEnabled":false,"path":"/","repo":{"id":33452903,"defaultBranch":"master","name":"machinery","ownerLogin":"RichardKnop","currentUserCanPush":false,"isFork":false,"isEmpty":false,"createdAt":"2015-04-05T19:46:34.000Z","ownerAvatar":"https://avatars.githubusercontent.com/u/2325888?v=4","public":true,"private":false,"isOrgOwned":false},"currentUser":null,"refInfo":{"name":"master","listCacheKey":"v0:1710841990.0","canEdit":false,"refType":"branch","currentOid":"ef4a2e724ade48874185ce0ca7542355fe4aa9be"},"tree":{"items":[{"name":"example","path":"example","contentType":"directory"},{"name":"instruction-notes","path":"instruction-notes","contentType":"directory"},{"name":"integration-tests","path":"integration-tests","contentType":"directory"},{"name":"v1","path":"v1","contentType":"directory"},{"name":"v2","path":"v2","contentType":"directory"},{"name":".deepsource.toml","path":".deepsource.toml","contentType":"file"},{"name":".gitignore","path":".gitignore","contentType":"file"},{"name":".travis.yml","path":".travis.yml","contentType":"file"},{"name":"Dockerfile.gcppubsub","path":"Dockerfile.gcppubsub","contentType":"file"},{"name":"Dockerfile.test","path":"Dockerfile.test","contentType":"file"},{"name":"LICENSE","path":"LICENSE","contentType":"file"},{"name":"Makefile","path":"Makefile","contentType":"file"},{"name":"README.md","path":"README.md","contentType":"file"},{"name":"docker-compose.test.yml","path":"docker-compose.test.yml","contentType":"file"},{"name":"go.mod","path":"go.mod","contentType":"file"},{"name":"go.sum","path":"go.sum","contentType":"file"},{"name":"wait-for-it.sh","path":"wait-for-it.sh","contentType":"file"}],"templateDirectorySuggestionUrl":null,"readme":null,"totalCount":17,"showBranchInfobar":false},"fileTree":null,"fileTreeProcessingTime":null,"foldersToFetch":[],"treeExpanded":false,"symbolsExpanded":false,"isOverview":true,"overview":{"banners":{"shouldRecommendReadme":false,"isPersonalRepo":false,"showUseActionBanner":false,"actionSlug":null,"actionId":null,"showProtectBranchBanner":false,"publishBannersInfo":{"dismissActionNoticePath":"/settings/dismiss-notice/publish_action_from_repo","releasePath":"/RichardKnop/machinery/releases/new?marketplace=true","showPublishActionBanner":false},"interactionLimitBanner":null,"showInvitationBanner":false,"inviterName":null,"actionsMigrationBannerInfo":{"releaseTags":[],"showImmutableActionsMigrationBanner":false,"initialMigrationStatus":null}},"codeButton":{"contactPath":"/contact","isEnterprise":false,"local":{"protocolInfo":{"httpAvailable":true,"sshAvailable":null,"httpUrl":"https://github.com/RichardKnop/machinery.git","showCloneWarning":null,"sshUrl":null,"sshCertificatesRequired":null,"sshCertificatesAvailable":null,"ghCliUrl":"gh repo clone RichardKnop/machinery","defaultProtocol":"http","newSshKeyUrl":"/settings/ssh/new","setProtocolPath":"/users/set_protocol"},"platformInfo":{"cloneUrl":"https://desktop.github.com","showVisualStudioCloneButton":false,"visualStudioCloneUrl":"https://windows.github.com","showXcodeCloneButton":false,"xcodeCloneUrl":"xcode://clone?repo=https%3A%2F%2Fgithub.com%2FRichardKnop%2Fmachinery","zipballUrl":"/RichardKnop/machinery/archive/refs/heads/master.zip"}},"newCodespacePath":"/codespaces/new?hide_repo_select=true\u0026repo=33452903"},"popovers":{"rename":null,"renamedParentRepo":null},"commitCount":"1,063","overviewFiles":[{"displayName":"README.md","repoName":"machinery","refName":"master","path":"README.md","preferredFileType":"readme","tabName":"README","richText":"\u003carticle class=\"markdown-body entry-content container-lg\" itemprop=\"text\"\u003e\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eMachinery\u003c/h2\u003e\u003ca id=\"user-content-machinery\" class=\"anchor\" aria-label=\"Permalink: Machinery\" href=\"#machinery\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eMachinery is an asynchronous task queue/job queue based on distributed message passing.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ca href=\"https://travis-ci.org/RichardKnop/machinery\" rel=\"nofollow\"\u003e\u003cimg src=\"https://camo.githubusercontent.com/40b0c030de54012daf268a5c2ada1027708c41206550ec7c64ca1aed732eca5b/68747470733a2f2f7472617669732d63692e6f72672f526963686172644b6e6f702f6d616368696e6572792e7376673f6272616e63683d6d6173746572266c6162656c3d6c696e75782b6275696c64\" alt=\"Travis Status for RichardKnop/machinery\" data-canonical-src=\"https://travis-ci.org/RichardKnop/machinery.svg?branch=master\u0026amp;label=linux+build\" style=\"max-width: 100%;\"\u003e\u003c/a\u003e\n\u003ca href=\"http://godoc.org/github.com/RichardKnop/machinery/v1\" rel=\"nofollow\"\u003e\u003cimg src=\"https://camo.githubusercontent.com/ba8e1fdfc10c2e72e21e374db1726077ffce854e7506ae647f981fa384a4f2c3/68747470733a2f2f676f646f632e6f72672f6769746875622e636f6d2f6e617468616e792f6c6f6f7065723f7374617475732e737667\" alt=\"godoc for RichardKnop/machinery\" data-canonical-src=\"https://godoc.org/github.com/nathany/looper?status.svg\" style=\"max-width: 100%;\"\u003e\u003c/a\u003e\n\u003ca href=\"https://codecov.io/gh/RichardKnop/machinery\" rel=\"nofollow\"\u003e\u003cimg src=\"https://camo.githubusercontent.com/74e2b5bb7ee0fa2e7684d5003a119e5105e05de749d217f7a4dc57f0dc5c92e3/68747470733a2f2f636f6465636f762e696f2f67682f526963686172644b6e6f702f6d616368696e6572792f6272616e63682f6d61737465722f67726170682f62616467652e737667\" alt=\"codecov for RichardKnop/machinery\" data-canonical-src=\"https://codecov.io/gh/RichardKnop/machinery/branch/master/graph/badge.svg\" style=\"max-width: 100%;\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ca href=\"https://goreportcard.com/report/github.com/RichardKnop/machinery\" rel=\"nofollow\"\u003e\u003cimg src=\"https://camo.githubusercontent.com/fe06941ff762bfeaa21a01a4b7205a176363bb8ed6f404d96897631f82d04227/68747470733a2f2f676f7265706f7274636172642e636f6d2f62616467652f6769746875622e636f6d2f526963686172644b6e6f702f6d616368696e657279\" alt=\"Go Report Card\" data-canonical-src=\"https://goreportcard.com/badge/github.com/RichardKnop/machinery\" style=\"max-width: 100%;\"\u003e\u003c/a\u003e\n\u003ca href=\"https://golangci.com\" rel=\"nofollow\"\u003e\u003cimg src=\"https://camo.githubusercontent.com/324eed5ff78053caa5b7b25c59540c253f2824585dd6bbe7e7f2e2f64d223136/68747470733a2f2f676f6c616e6763692e636f6d2f6261646765732f6769746875622e636f6d2f526963686172644b6e6f702f6d616368696e6572792e737667\" alt=\"GolangCI\" data-canonical-src=\"https://golangci.com/badges/github.com/RichardKnop/machinery.svg\" style=\"max-width: 100%;\"\u003e\u003c/a\u003e\n\u003ca href=\"http://opentracing.io\" rel=\"nofollow\"\u003e\u003cimg src=\"https://camo.githubusercontent.com/ad854b5cd661412d83ad272bdedc460f22784bb13327cfc9a5110d5375130665/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4f70656e54726163696e672d656e61626c65642d626c75652e737667\" alt=\"OpenTracing Badge\" data-canonical-src=\"https://img.shields.io/badge/OpenTracing-enabled-blue.svg\" style=\"max-width: 100%;\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ca href=\"https://sourcegraph.com/github.com/RichardKnop/machinery?badge\" rel=\"nofollow\"\u003e\u003cimg src=\"https://camo.githubusercontent.com/25e031010279447153e701ddc01762052b1ea58032103332178f10224cb52f4f/68747470733a2f2f736f7572636567726170682e636f6d2f6769746875622e636f6d2f526963686172644b6e6f702f6d616368696e6572792f2d2f62616467652e737667\" alt=\"Sourcegraph for RichardKnop/machinery\" data-canonical-src=\"https://sourcegraph.com/github.com/RichardKnop/machinery/-/badge.svg\" style=\"max-width: 100%;\"\u003e\u003c/a\u003e\n\u003ca href=\"https://richardknop.github.io/donate/\" rel=\"nofollow\"\u003e\u003cimg src=\"https://camo.githubusercontent.com/6f06e72688e64d1f15fef24e157b92611ce29c54f94fb1ee84784a857d7db920/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f646f6e6174652d626974636f696e2d6f72616e67652e737667\" alt=\"Donate Bitcoin\" data-canonical-src=\"https://img.shields.io/badge/donate-bitcoin-orange.svg\" style=\"max-width: 100%;\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003chr\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ca href=\"#v2-experiment\"\u003eV2 Experiment\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#first-steps\"\u003eFirst Steps\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#configuration\"\u003eConfiguration\u003c/a\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ca href=\"#lock\"\u003eLock\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#broker\"\u003eBroker\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#defaultqueue\"\u003eDefaultQueue\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#resultbackend\"\u003eResultBackend\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#resultsexpirein\"\u003eResultsExpireIn\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#amqp-2\"\u003eAMQP\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#dynamodb\"\u003eDynamoDB\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#redis-2\"\u003eRedis\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#gcppubsub\"\u003eGCPPubSub\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#custom-logger\"\u003eCustom Logger\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#server\"\u003eServer\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#workers\"\u003eWorkers\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#tasks\"\u003eTasks\u003c/a\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ca href=\"#registering-tasks\"\u003eRegistering Tasks\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#signatures\"\u003eSignatures\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#supported-types\"\u003eSupported Types\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#sending-tasks\"\u003eSending Tasks\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#delayed-tasks\"\u003eDelayed Tasks\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#retry-tasks\"\u003eRetry Tasks\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#get-pending-tasks\"\u003eGet Pending Tasks\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#keeping-results\"\u003eKeeping Results\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#workflows\"\u003eWorkflows\u003c/a\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ca href=\"#groups\"\u003eGroups\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#chords\"\u003eChords\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#chains\"\u003eChains\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#periodic-tasks--workflows\"\u003ePeriodic Tasks \u0026amp; Workflows\u003c/a\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ca href=\"#periodic-tasks\"\u003ePeriodic Tasks\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#periodic-groups\"\u003ePeriodic Groups\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#periodic-chains\"\u003ePeriodic Chains\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#periodic-chords\"\u003ePeriodic Chords\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#development\"\u003eDevelopment\u003c/a\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ca href=\"#requirements\"\u003eRequirements\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#dependencies\"\u003eDependencies\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#testing\"\u003eTesting\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eV2\u003c/h3\u003e\u003ca id=\"user-content-v2\" class=\"anchor\" aria-label=\"Permalink: V2\" href=\"#v2\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eI recommend using V2 in order to avoid having to import all dependencies for brokers and backends you are not using.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eInstead of factory, you will need to inject broker and backend objects to the server constructor:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"import (\n \u0026quot;github.com/RichardKnop/machinery/v2\u0026quot;\n backendsiface \u0026quot;github.com/RichardKnop/machinery/v2/backends/iface\u0026quot;\n brokersiface \u0026quot;github.com/RichardKnop/machinery/v2/brokers/iface\u0026quot;\n locksiface \u0026quot;github.com/RichardKnop/machinery/v2/locks/iface\u0026quot;\n)\n\nvar broker brokersiface.Broker\nvar backend backendsiface.Backend\nvar lock locksiface.Lock\nserver := machinery.NewServer(cnf, broker, backend, lock)\n// server.NewWorker(\u0026quot;machinery\u0026quot;, 10)\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e (\n \u003cspan class=\"pl-s\"\u003e\"github.com/RichardKnop/machinery/v2\"\u003c/span\u003e\n backendsiface \u003cspan class=\"pl-s\"\u003e\"github.com/RichardKnop/machinery/v2/backends/iface\"\u003c/span\u003e\n brokersiface \u003cspan class=\"pl-s\"\u003e\"github.com/RichardKnop/machinery/v2/brokers/iface\"\u003c/span\u003e\n locksiface \u003cspan class=\"pl-s\"\u003e\"github.com/RichardKnop/machinery/v2/locks/iface\"\u003c/span\u003e\n)\n\n\u003cspan class=\"pl-k\"\u003evar\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ebroker\u003c/span\u003e brokersiface.\u003cspan class=\"pl-smi\"\u003eBroker\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003evar\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ebackend\u003c/span\u003e backendsiface.\u003cspan class=\"pl-smi\"\u003eBackend\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003evar\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003elock\u003c/span\u003e locksiface.\u003cspan class=\"pl-smi\"\u003eLock\u003c/span\u003e\n\u003cspan class=\"pl-s1\"\u003eserver\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003emachinery\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eNewServer\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003ecnf\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003ebroker\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003ebackend\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003elock\u003c/span\u003e)\n\u003cspan class=\"pl-c\"\u003e// server.NewWorker(\"machinery\", 10)\u003c/span\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eFirst Steps\u003c/h3\u003e\u003ca id=\"user-content-first-steps\" class=\"anchor\" aria-label=\"Permalink: First Steps\" href=\"#first-steps\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eTo install recommended v2 release:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"go get github.com/RichardKnop/machinery/v2\"\u003e\u003cpre\u003ego get github.com/RichardKnop/machinery/v2\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eIf you want to use legacy v1 version, you still can:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"go get github.com/RichardKnop/machinery\"\u003e\u003cpre\u003ego get github.com/RichardKnop/machinery\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eFirst, you will need to define some tasks. Look at sample tasks in \u003ccode\u003ev2/example/tasks/tasks.go\u003c/code\u003e to see a few examples.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eSecond, you will need to launch a worker process with one of these commands (v2 is recommended since it doesn't import dependencies for all brokers / backends, only those you actually need):\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"cd v2/\ngo run example/amqp/main.go worker\ngo run example/redigo/main.go worker // Redis with redigo driver\ngo run example/go-redis/main.go worker // Redis with Go Redis driver\n\ngo run example/amqp/main.go worker\ngo run example/redis/main.go worker\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c1\"\u003ecd\u003c/span\u003e v2/\ngo run example/amqp/main.go worker\ngo run example/redigo/main.go worker // Redis with redigo driver\ngo run example/go-redis/main.go worker // Redis with Go Redis driver\n\ngo run example/amqp/main.go worker\ngo run example/redis/main.go worker\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e\u003ca target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://raw.githubusercontent.com/RichardKnop/assets/master/machinery/example_worker.png\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/RichardKnop/assets/master/machinery/example_worker.png\" alt=\"Example worker\" style=\"max-width: 100%;\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eFinally, once you have a worker running and waiting for tasks to consume, send some tasks with one of these commands (v2 is recommended since it doesn't import dependencies for all brokers / backends, only those you actually need):\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"cd v2\ngo run v2/example/amqp/main.go send\ngo run v2/example/redigo/main.go send // Redis with redigo driver\ngo run v2/example/go-redis/main.go send // Redis with Go Redis driver\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c1\"\u003ecd\u003c/span\u003e v2\ngo run v2/example/amqp/main.go send\ngo run v2/example/redigo/main.go send // Redis with redigo driver\ngo run v2/example/go-redis/main.go send // Redis with Go Redis driver\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eYou will be able to see the tasks being processed asynchronously by the worker:\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ca target=\"_blank\" rel=\"noopener noreferrer nofollow\" href=\"https://raw.githubusercontent.com/RichardKnop/assets/master/machinery/example_worker_receives_tasks.png\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/RichardKnop/assets/master/machinery/example_worker_receives_tasks.png\" alt=\"Example worker receives tasks\" style=\"max-width: 100%;\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eConfiguration\u003c/h3\u003e\u003ca id=\"user-content-configuration\" class=\"anchor\" aria-label=\"Permalink: Configuration\" href=\"#configuration\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eThe \u003ca href=\"/RichardKnop/machinery/blob/master/v2/config/config.go\"\u003econfig\u003c/a\u003e package has convenience methods for loading configuration from environment variables or a YAML file. For example, load configuration from environment variables:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"cnf, err := config.NewFromEnvironment()\"\u003e\u003cpre\u003e\u003cspan class=\"pl-s1\"\u003ecnf\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003econfig\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eNewFromEnvironment\u003c/span\u003e()\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eOr load from YAML file:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"cnf, err := config.NewFromYaml(\u0026quot;config.yml\u0026quot;, true)\"\u003e\u003cpre\u003e\u003cspan class=\"pl-s1\"\u003ecnf\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003econfig\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eNewFromYaml\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"config.yml\"\u003c/span\u003e, \u003cspan class=\"pl-c1\"\u003etrue\u003c/span\u003e)\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eSecond boolean flag enables live reloading of configuration every 10 seconds. Use \u003ccode\u003efalse\u003c/code\u003e to disable live reloading.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eMachinery configuration is encapsulated by a \u003ccode\u003eConfig\u003c/code\u003e struct and injected as a dependency to objects that need it.\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eLock\u003c/h4\u003e\u003ca id=\"user-content-lock\" class=\"anchor\" aria-label=\"Permalink: Lock\" href=\"#lock\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch5 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eRedis\u003c/h5\u003e\u003ca id=\"user-content-redis\" class=\"anchor\" aria-label=\"Permalink: Redis\" href=\"#redis\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eUse Redis URL in one of these formats:\u003c/p\u003e\n\u003cdiv class=\"snippet-clipboard-content notranslate position-relative overflow-auto\" data-snippet-clipboard-copy-content=\"redis://[password@]host[port][/db_num]\"\u003e\u003cpre class=\"notranslate\"\u003e\u003ccode\u003eredis://[password@]host[port][/db_num]\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eFor example:\u003c/p\u003e\n\u003col dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003eredis://localhost:6379\u003c/code\u003e, or with password \u003ccode\u003eredis://password@localhost:6379\u003c/code\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eBroker\u003c/h4\u003e\u003ca id=\"user-content-broker\" class=\"anchor\" aria-label=\"Permalink: Broker\" href=\"#broker\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eA message broker. Currently supported brokers are:\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch5 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eAMQP\u003c/h5\u003e\u003ca id=\"user-content-amqp\" class=\"anchor\" aria-label=\"Permalink: AMQP\" href=\"#amqp\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eUse AMQP URL in the format:\u003c/p\u003e\n\u003cdiv class=\"snippet-clipboard-content notranslate position-relative overflow-auto\" data-snippet-clipboard-copy-content=\"amqp://[username:password@]@host[:port]\"\u003e\u003cpre class=\"notranslate\"\u003e\u003ccode\u003eamqp://[username:password@]@host[:port]\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eFor example:\u003c/p\u003e\n\u003col dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003eamqp://guest:guest@localhost:5672\u003c/code\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp dir=\"auto\"\u003eAMQP also supports multiples brokers urls. You need to specify the URL separator in the \u003ccode\u003eMultipleBrokerSeparator\u003c/code\u003e field.\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch5 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eRedis\u003c/h5\u003e\u003ca id=\"user-content-redis-1\" class=\"anchor\" aria-label=\"Permalink: Redis\" href=\"#redis-1\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eUse Redis URL in one of these formats:\u003c/p\u003e\n\u003cdiv class=\"snippet-clipboard-content notranslate position-relative overflow-auto\" data-snippet-clipboard-copy-content=\"redis://[password@]host[port][/db_num]\nredis+socket://[password@]/path/to/file.sock[:/db_num]\"\u003e\u003cpre class=\"notranslate\"\u003e\u003ccode\u003eredis://[password@]host[port][/db_num]\nredis+socket://[password@]/path/to/file.sock[:/db_num]\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eFor example:\u003c/p\u003e\n\u003col dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003eredis://localhost:6379\u003c/code\u003e, or with password \u003ccode\u003eredis://password@localhost:6379\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eredis+socket://password@/path/to/file.sock:/0\u003c/code\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch5 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eAWS SQS\u003c/h5\u003e\u003ca id=\"user-content-aws-sqs\" class=\"anchor\" aria-label=\"Permalink: AWS SQS\" href=\"#aws-sqs\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eUse AWS SQS URL in the format:\u003c/p\u003e\n\u003cdiv class=\"snippet-clipboard-content notranslate position-relative overflow-auto\" data-snippet-clipboard-copy-content=\"https://sqs.us-east-2.amazonaws.com/123456789012\"\u003e\u003cpre class=\"notranslate\"\u003e\u003ccode\u003ehttps://sqs.us-east-2.amazonaws.com/123456789012\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eSee \u003ca href=\"https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html\" rel=\"nofollow\"\u003eAWS SQS docs\u003c/a\u003e for more information.\nAlso, configuring \u003ccode\u003eAWS_REGION\u003c/code\u003e is required, or an error would be thrown.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eTo use a manually configured SQS Client:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"var sqsClient = sqs.New(session.Must(session.NewSession(\u0026amp;aws.Config{\n Region: aws.String(\u0026quot;YOUR_AWS_REGION\u0026quot;),\n Credentials: credentials.NewStaticCredentials(\u0026quot;YOUR_AWS_ACCESS_KEY\u0026quot;, \u0026quot;YOUR_AWS_ACCESS_SECRET\u0026quot;, \u0026quot;\u0026quot;),\n HTTPClient: \u0026amp;http.Client{\n Timeout: time.Second * 120,\n },\n})))\nvar visibilityTimeout = 20\nvar cnf = \u0026amp;config.Config{\n Broker: \u0026quot;YOUR_SQS_URL\u0026quot;\n DefaultQueue: \u0026quot;machinery_tasks\u0026quot;,\n ResultBackend: \u0026quot;YOUR_BACKEND_URL\u0026quot;,\n SQS: \u0026amp;config.SQSConfig{\n Client: sqsClient,\n // if VisibilityTimeout is nil default to the overall visibility timeout setting for the queue\n // https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-visibility-timeout.html\n VisibilityTimeout: \u0026amp;visibilityTimeout,\n WaitTimeSeconds: 30,\n },\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003evar\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003esqsClient\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003esqs\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eNew\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003esession\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eMust\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003esession\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eNewSession\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e\u0026amp;\u003c/span\u003eaws.\u003cspan class=\"pl-smi\"\u003eConfig\u003c/span\u003e{\n \u003cspan class=\"pl-s1\"\u003eRegion\u003c/span\u003e: \u003cspan class=\"pl-s1\"\u003eaws\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eString\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"YOUR_AWS_REGION\"\u003c/span\u003e),\n \u003cspan class=\"pl-s1\"\u003eCredentials\u003c/span\u003e: \u003cspan class=\"pl-s1\"\u003ecredentials\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eNewStaticCredentials\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"YOUR_AWS_ACCESS_KEY\"\u003c/span\u003e, \u003cspan class=\"pl-s\"\u003e\"YOUR_AWS_ACCESS_SECRET\"\u003c/span\u003e, \u003cspan class=\"pl-s\"\u003e\"\"\u003c/span\u003e),\n \u003cspan class=\"pl-s1\"\u003eHTTPClient\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e\u0026amp;\u003c/span\u003ehttp.\u003cspan class=\"pl-smi\"\u003eClient\u003c/span\u003e{\n \u003cspan class=\"pl-s1\"\u003eTimeout\u003c/span\u003e: \u003cspan class=\"pl-s1\"\u003etime\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eSecond\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e*\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e120\u003c/span\u003e,\n },\n})))\n\u003cspan class=\"pl-k\"\u003evar\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003evisibilityTimeout\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e=\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e20\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003evar\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ecnf\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e=\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e\u0026amp;\u003c/span\u003econfig.\u003cspan class=\"pl-smi\"\u003eConfig\u003c/span\u003e{\n \u003cspan class=\"pl-s1\"\u003eBroker\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"YOUR_SQS_URL\"\u003c/span\u003e\n \u003cspan class=\"pl-s1\"\u003eDefaultQueue\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"machinery_tasks\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eResultBackend\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"YOUR_BACKEND_URL\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eSQS\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e\u0026amp;\u003c/span\u003econfig.\u003cspan class=\"pl-smi\"\u003eSQSConfig\u003c/span\u003e{\n \u003cspan class=\"pl-s1\"\u003eClient\u003c/span\u003e: \u003cspan class=\"pl-s1\"\u003esqsClient\u003c/span\u003e,\n \u003cspan class=\"pl-c\"\u003e// if VisibilityTimeout is nil default to the overall visibility timeout setting for the queue\u003c/span\u003e\n \u003cspan class=\"pl-c\"\u003e// https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-visibility-timeout.html\u003c/span\u003e\n \u003cspan class=\"pl-s1\"\u003eVisibilityTimeout\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"pl-s1\"\u003evisibilityTimeout\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eWaitTimeSeconds\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e30\u003c/span\u003e,\n },\n}\u003c/pre\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch5 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eGCP Pub/Sub\u003c/h5\u003e\u003ca id=\"user-content-gcp-pubsub\" class=\"anchor\" aria-label=\"Permalink: GCP Pub/Sub\" href=\"#gcp-pubsub\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eUse GCP Pub/Sub URL in the format:\u003c/p\u003e\n\u003cdiv class=\"snippet-clipboard-content notranslate position-relative overflow-auto\" data-snippet-clipboard-copy-content=\"gcppubsub://YOUR_GCP_PROJECT_ID/YOUR_PUBSUB_SUBSCRIPTION_NAME\"\u003e\u003cpre class=\"notranslate\"\u003e\u003ccode\u003egcppubsub://YOUR_GCP_PROJECT_ID/YOUR_PUBSUB_SUBSCRIPTION_NAME\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eTo use a manually configured Pub/Sub Client:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"pubsubClient, err := pubsub.NewClient(\n context.Background(),\n \u0026quot;YOUR_GCP_PROJECT_ID\u0026quot;,\n option.WithServiceAccountFile(\u0026quot;YOUR_GCP_SERVICE_ACCOUNT_FILE\u0026quot;),\n)\n\ncnf := \u0026amp;config.Config{\n Broker: \u0026quot;gcppubsub://YOUR_GCP_PROJECT_ID/YOUR_PUBSUB_SUBSCRIPTION_NAME\u0026quot;\n DefaultQueue: \u0026quot;YOUR_PUBSUB_TOPIC_NAME\u0026quot;,\n ResultBackend: \u0026quot;YOUR_BACKEND_URL\u0026quot;,\n GCPPubSub: config.GCPPubSubConfig{\n Client: pubsubClient,\n },\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-s1\"\u003epubsubClient\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003epubsub\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eNewClient\u003c/span\u003e(\n \u003cspan class=\"pl-s1\"\u003econtext\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eBackground\u003c/span\u003e(),\n \u003cspan class=\"pl-s\"\u003e\"YOUR_GCP_PROJECT_ID\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eoption\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eWithServiceAccountFile\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"YOUR_GCP_SERVICE_ACCOUNT_FILE\"\u003c/span\u003e),\n)\n\n\u003cspan class=\"pl-s1\"\u003ecnf\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e\u0026amp;\u003c/span\u003econfig.\u003cspan class=\"pl-smi\"\u003eConfig\u003c/span\u003e{\n \u003cspan class=\"pl-s1\"\u003eBroker\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"gcppubsub://YOUR_GCP_PROJECT_ID/YOUR_PUBSUB_SUBSCRIPTION_NAME\"\u003c/span\u003e\n \u003cspan class=\"pl-s1\"\u003eDefaultQueue\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"YOUR_PUBSUB_TOPIC_NAME\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eResultBackend\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"YOUR_BACKEND_URL\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eGCPPubSub\u003c/span\u003e: config.\u003cspan class=\"pl-smi\"\u003eGCPPubSubConfig\u003c/span\u003e{\n \u003cspan class=\"pl-s1\"\u003eClient\u003c/span\u003e: \u003cspan class=\"pl-s1\"\u003epubsubClient\u003c/span\u003e,\n },\n}\u003c/pre\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eDefaultQueue\u003c/h4\u003e\u003ca id=\"user-content-defaultqueue\" class=\"anchor\" aria-label=\"Permalink: DefaultQueue\" href=\"#defaultqueue\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eDefault queue name, e.g. \u003ccode\u003emachinery_tasks\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eResultBackend\u003c/h4\u003e\u003ca id=\"user-content-resultbackend\" class=\"anchor\" aria-label=\"Permalink: ResultBackend\" href=\"#resultbackend\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eResult backend to use for keeping task states and results.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eCurrently supported backends are:\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch5 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eRedis\u003c/h5\u003e\u003ca id=\"user-content-redis-2\" class=\"anchor\" aria-label=\"Permalink: Redis\" href=\"#redis-2\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eUse Redis URL in one of these formats:\u003c/p\u003e\n\u003cdiv class=\"snippet-clipboard-content notranslate position-relative overflow-auto\" data-snippet-clipboard-copy-content=\"redis://[password@]host[port][/db_num]\nredis+socket://[password@]/path/to/file.sock[:/db_num]\"\u003e\u003cpre class=\"notranslate\"\u003e\u003ccode\u003eredis://[password@]host[port][/db_num]\nredis+socket://[password@]/path/to/file.sock[:/db_num]\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eFor example:\u003c/p\u003e\n\u003col dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003eredis://localhost:6379\u003c/code\u003e, or with password \u003ccode\u003eredis://password@localhost:6379\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eredis+socket://password@/path/to/file.sock:/0\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003ecluster \u003ccode\u003eredis://host1:port1,host2:port2,host3:port3\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003ecluster with password \u003ccode\u003eredis://pass@host1:port1,host2:port2,host3:port3\u003c/code\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch5 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eMemcache\u003c/h5\u003e\u003ca id=\"user-content-memcache\" class=\"anchor\" aria-label=\"Permalink: Memcache\" href=\"#memcache\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eUse Memcache URL in the format:\u003c/p\u003e\n\u003cdiv class=\"snippet-clipboard-content notranslate position-relative overflow-auto\" data-snippet-clipboard-copy-content=\"memcache://host1[:port1][,host2[:port2],...[,hostN[:portN]]]\"\u003e\u003cpre class=\"notranslate\"\u003e\u003ccode\u003ememcache://host1[:port1][,host2[:port2],...[,hostN[:portN]]]\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eFor example:\u003c/p\u003e\n\u003col dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003ememcache://localhost:11211\u003c/code\u003e for a single instance, or\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ememcache://10.0.0.1:11211,10.0.0.2:11211\u003c/code\u003e for a cluster\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch5 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eAMQP\u003c/h5\u003e\u003ca id=\"user-content-amqp-1\" class=\"anchor\" aria-label=\"Permalink: AMQP\" href=\"#amqp-1\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eUse AMQP URL in the format:\u003c/p\u003e\n\u003cdiv class=\"snippet-clipboard-content notranslate position-relative overflow-auto\" data-snippet-clipboard-copy-content=\"amqp://[username:password@]@host[:port]\"\u003e\u003cpre class=\"notranslate\"\u003e\u003ccode\u003eamqp://[username:password@]@host[:port]\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eFor example:\u003c/p\u003e\n\u003col dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003eamqp://guest:guest@localhost:5672\u003c/code\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003cblockquote\u003e\n\u003cp dir=\"auto\"\u003eKeep in mind AMQP is not recommended as a result backend. See \u003ca href=\"https://github.com/RichardKnop/machinery#keeping-results\"\u003eKeeping Results\u003c/a\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch5 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eMongoDB\u003c/h5\u003e\u003ca id=\"user-content-mongodb\" class=\"anchor\" aria-label=\"Permalink: MongoDB\" href=\"#mongodb\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eUse Mongodb URL in the format:\u003c/p\u003e\n\u003cdiv class=\"snippet-clipboard-content notranslate position-relative overflow-auto\" data-snippet-clipboard-copy-content=\"mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]\"\u003e\u003cpre class=\"notranslate\"\u003e\u003ccode\u003emongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eFor example:\u003c/p\u003e\n\u003col dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003emongodb://localhost:27017/taskresults\u003c/code\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp dir=\"auto\"\u003eSee \u003ca href=\"https://docs.mongodb.org/manual/reference/connection-string/\" rel=\"nofollow\"\u003eMongoDB docs\u003c/a\u003e for more information.\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eResultsExpireIn\u003c/h4\u003e\u003ca id=\"user-content-resultsexpirein\" class=\"anchor\" aria-label=\"Permalink: ResultsExpireIn\" href=\"#resultsexpirein\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eHow long to store task results for in seconds. Defaults to \u003ccode\u003e3600\u003c/code\u003e (1 hour).\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eAMQP\u003c/h4\u003e\u003ca id=\"user-content-amqp-2\" class=\"anchor\" aria-label=\"Permalink: AMQP\" href=\"#amqp-2\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eRabbitMQ related configuration. Not necessary if you are using other broker/backend.\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003eExchange\u003c/code\u003e: exchange name, e.g. \u003ccode\u003emachinery_exchange\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eExchangeType\u003c/code\u003e: exchange type, e.g. \u003ccode\u003edirect\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eQueueBindingArguments\u003c/code\u003e: an optional map of additional arguments used when binding to an AMQP queue\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eBindingKey\u003c/code\u003e: The queue is bind to the exchange with this key, e.g. \u003ccode\u003emachinery_task\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ePrefetchCount\u003c/code\u003e: How many tasks to prefetch (set to \u003ccode\u003e1\u003c/code\u003e if you have long running tasks)\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eDelayedQueue\u003c/code\u003e: delayed queue name to be used for task retry or delayed task (if empty it will follow auto create and delate delayed queues)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eDynamoDB\u003c/h4\u003e\u003ca id=\"user-content-dynamodb\" class=\"anchor\" aria-label=\"Permalink: DynamoDB\" href=\"#dynamodb\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eDynamoDB related configuration. Not necessary if you are using other backend.\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003eTaskStatesTable\u003c/code\u003e: Custom table name for saving task states. Default one is \u003ccode\u003etask_states\u003c/code\u003e, and make sure to create this table in your AWS admin first, using \u003ccode\u003eTaskUUID\u003c/code\u003e as table's primary key.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eGroupMetasTable\u003c/code\u003e: Custom table name for saving group metas. Default one is \u003ccode\u003egroup_metas\u003c/code\u003e, and make sure to create this table in your AWS admin first, using \u003ccode\u003eGroupUUID\u003c/code\u003e as table's primary key.\nFor example:\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"snippet-clipboard-content notranslate position-relative overflow-auto\" data-snippet-clipboard-copy-content=\"dynamodb:\n task_states_table: 'task_states'\n group_metas_table: 'group_metas'\"\u003e\u003cpre class=\"notranslate\"\u003e\u003ccode\u003edynamodb:\n task_states_table: 'task_states'\n group_metas_table: 'group_metas'\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eIf these tables are not found, an fatal error would be thrown.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eIf you wish to expire the records, you can configure the \u003ccode\u003eTTL\u003c/code\u003e field in AWS admin for these tables. The \u003ccode\u003eTTL\u003c/code\u003e field is set based on the \u003ccode\u003eResultsExpireIn\u003c/code\u003e value in the Server's config. See \u003ca href=\"https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/howitworks-ttl.html\" rel=\"nofollow\"\u003ehttps://docs.aws.amazon.com/amazondynamodb/latest/developerguide/howitworks-ttl.html\u003c/a\u003e for more information.\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eRedis\u003c/h4\u003e\u003ca id=\"user-content-redis-3\" class=\"anchor\" aria-label=\"Permalink: Redis\" href=\"#redis-3\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eRedis related configuration. Not necessary if you are using other backend.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eSee: \u003ca href=\"/RichardKnop/machinery/blob/master/v1/config/config.go\"\u003econfig\u003c/a\u003e (TODO)\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eGCPPubSub\u003c/h4\u003e\u003ca id=\"user-content-gcppubsub\" class=\"anchor\" aria-label=\"Permalink: GCPPubSub\" href=\"#gcppubsub\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eGCPPubSub related configuration. Not necessary if you are using other backend.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eSee: \u003ca href=\"/RichardKnop/machinery/blob/master/v1/config/config.go\"\u003econfig\u003c/a\u003e (TODO)\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eCustom Logger\u003c/h3\u003e\u003ca id=\"user-content-custom-logger\" class=\"anchor\" aria-label=\"Permalink: Custom Logger\" href=\"#custom-logger\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eYou can define a custom logger by implementing the following interface:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"type Interface interface {\n Print(...interface{})\n Printf(string, ...interface{})\n Println(...interface{})\n\n Fatal(...interface{})\n Fatalf(string, ...interface{})\n Fatalln(...interface{})\n\n Panic(...interface{})\n Panicf(string, ...interface{})\n Panicln(...interface{})\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003etype\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eInterface\u003c/span\u003e \u003cspan class=\"pl-k\"\u003einterface\u003c/span\u003e {\n \u003cspan class=\"pl-c1\"\u003ePrint\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e...\u003c/span\u003e\u003cspan class=\"pl-k\"\u003einterface\u003c/span\u003e{})\n \u003cspan class=\"pl-c1\"\u003ePrintf\u003c/span\u003e(\u003cspan class=\"pl-smi\"\u003estring\u003c/span\u003e, \u003cspan class=\"pl-c1\"\u003e...\u003c/span\u003e\u003cspan class=\"pl-k\"\u003einterface\u003c/span\u003e{})\n \u003cspan class=\"pl-c1\"\u003ePrintln\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e...\u003c/span\u003e\u003cspan class=\"pl-k\"\u003einterface\u003c/span\u003e{})\n\n \u003cspan class=\"pl-c1\"\u003eFatal\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e...\u003c/span\u003e\u003cspan class=\"pl-k\"\u003einterface\u003c/span\u003e{})\n \u003cspan class=\"pl-c1\"\u003eFatalf\u003c/span\u003e(\u003cspan class=\"pl-smi\"\u003estring\u003c/span\u003e, \u003cspan class=\"pl-c1\"\u003e...\u003c/span\u003e\u003cspan class=\"pl-k\"\u003einterface\u003c/span\u003e{})\n \u003cspan class=\"pl-c1\"\u003eFatalln\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e...\u003c/span\u003e\u003cspan class=\"pl-k\"\u003einterface\u003c/span\u003e{})\n\n \u003cspan class=\"pl-c1\"\u003ePanic\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e...\u003c/span\u003e\u003cspan class=\"pl-k\"\u003einterface\u003c/span\u003e{})\n \u003cspan class=\"pl-c1\"\u003ePanicf\u003c/span\u003e(\u003cspan class=\"pl-smi\"\u003estring\u003c/span\u003e, \u003cspan class=\"pl-c1\"\u003e...\u003c/span\u003e\u003cspan class=\"pl-k\"\u003einterface\u003c/span\u003e{})\n \u003cspan class=\"pl-c1\"\u003ePanicln\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e...\u003c/span\u003e\u003cspan class=\"pl-k\"\u003einterface\u003c/span\u003e{})\n}\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eThen just set the logger in your setup code by calling \u003ccode\u003eSet\u003c/code\u003e function exported by \u003ccode\u003egithub.com/RichardKnop/machinery/v1/log\u003c/code\u003e package:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"log.Set(myCustomLogger)\"\u003e\u003cpre\u003e\u003cspan class=\"pl-s1\"\u003elog\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eSet\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003emyCustomLogger\u003c/span\u003e)\u003c/pre\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eServer\u003c/h3\u003e\u003ca id=\"user-content-server\" class=\"anchor\" aria-label=\"Permalink: Server\" href=\"#server\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eA Machinery library must be instantiated before use. The way this is done is by creating a \u003ccode\u003eServer\u003c/code\u003e instance. \u003ccode\u003eServer\u003c/code\u003e is a base object which stores Machinery configuration and registered tasks. E.g.:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"import (\n \u0026quot;github.com/RichardKnop/machinery/v1/config\u0026quot;\n \u0026quot;github.com/RichardKnop/machinery/v1\u0026quot;\n)\n\nvar cnf = \u0026amp;config.Config{\n Broker: \u0026quot;amqp://guest:guest@localhost:5672/\u0026quot;,\n DefaultQueue: \u0026quot;machinery_tasks\u0026quot;,\n ResultBackend: \u0026quot;amqp://guest:guest@localhost:5672/\u0026quot;,\n AMQP: \u0026amp;config.AMQPConfig{\n Exchange: \u0026quot;machinery_exchange\u0026quot;,\n ExchangeType: \u0026quot;direct\u0026quot;,\n BindingKey: \u0026quot;machinery_task\u0026quot;,\n },\n}\n\nserver, err := machinery.NewServer(cnf)\nif err != nil {\n // do something with the error\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e (\n \u003cspan class=\"pl-s\"\u003e\"github.com/RichardKnop/machinery/v1/config\"\u003c/span\u003e\n \u003cspan class=\"pl-s\"\u003e\"github.com/RichardKnop/machinery/v1\"\u003c/span\u003e\n)\n\n\u003cspan class=\"pl-k\"\u003evar\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003ecnf\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e=\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e\u0026amp;\u003c/span\u003econfig.\u003cspan class=\"pl-smi\"\u003eConfig\u003c/span\u003e{\n \u003cspan class=\"pl-s1\"\u003eBroker\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"amqp://guest:guest@localhost:5672/\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eDefaultQueue\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"machinery_tasks\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eResultBackend\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"amqp://guest:guest@localhost:5672/\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eAMQP\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e\u0026amp;\u003c/span\u003econfig.\u003cspan class=\"pl-smi\"\u003eAMQPConfig\u003c/span\u003e{\n \u003cspan class=\"pl-s1\"\u003eExchange\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"machinery_exchange\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eExchangeType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"direct\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eBindingKey\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"machinery_task\"\u003c/span\u003e,\n },\n}\n\n\u003cspan class=\"pl-s1\"\u003eserver\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003emachinery\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eNewServer\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003ecnf\u003c/span\u003e)\n\u003cspan class=\"pl-k\"\u003eif\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e!=\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003enil\u003c/span\u003e {\n \u003cspan class=\"pl-c\"\u003e// do something with the error\u003c/span\u003e\n}\u003c/pre\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eWorkers\u003c/h3\u003e\u003ca id=\"user-content-workers\" class=\"anchor\" aria-label=\"Permalink: Workers\" href=\"#workers\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eIn order to consume tasks, you need to have one or more workers running. All you need to run a worker is a \u003ccode\u003eServer\u003c/code\u003e instance with registered tasks. E.g.:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"worker := server.NewWorker(\u0026quot;worker_name\u0026quot;, 10)\nerr := worker.Launch()\nif err != nil {\n // do something with the error\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-s1\"\u003eworker\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eserver\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eNewWorker\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"worker_name\"\u003c/span\u003e, \u003cspan class=\"pl-c1\"\u003e10\u003c/span\u003e)\n\u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eworker\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eLaunch\u003c/span\u003e()\n\u003cspan class=\"pl-k\"\u003eif\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e!=\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003enil\u003c/span\u003e {\n \u003cspan class=\"pl-c\"\u003e// do something with the error\u003c/span\u003e\n}\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eEach worker will only consume registered tasks. For each task on the queue the Worker.Process() method will be run\nin a goroutine. Use the second parameter of \u003ccode\u003eserver.NewWorker\u003c/code\u003e to limit the number of concurrently running Worker.Process()\ncalls (per worker). Example: 1 will serialize task execution while 0 makes the number of concurrently executed tasks unlimited (default).\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eTasks\u003c/h3\u003e\u003ca id=\"user-content-tasks\" class=\"anchor\" aria-label=\"Permalink: Tasks\" href=\"#tasks\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eTasks are a building block of Machinery applications. A task is a function which defines what happens when a worker receives a message.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eEach task needs to return an error as a last return value. In addition to error tasks can now return any number of arguments.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eExamples of valid tasks:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"func Add(args ...int64) (int64, error) {\n sum := int64(0)\n for _, arg := range args {\n sum += arg\n }\n return sum, nil\n}\n\nfunc Multiply(args ...int64) (int64, error) {\n sum := int64(1)\n for _, arg := range args {\n sum *= arg\n }\n return sum, nil\n}\n\n// You can use context.Context as first argument to tasks, useful for open tracing\nfunc TaskWithContext(ctx context.Context, arg Arg) error {\n // ... use ctx ...\n return nil\n}\n\n// Tasks need to return at least error as a minimal requirement\nfunc DummyTask(arg string) error {\n return errors.New(arg)\n}\n\n// You can also return multiple results from the task\nfunc DummyTask2(arg1, arg2 string) (string, string, error) {\n return arg1, arg2, nil\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003efunc\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eAdd\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003eargs\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e...\u003c/span\u003e\u003cspan class=\"pl-smi\"\u003eint64\u003c/span\u003e) (\u003cspan class=\"pl-smi\"\u003eint64\u003c/span\u003e, \u003cspan class=\"pl-smi\"\u003eerror\u003c/span\u003e) {\n \u003cspan class=\"pl-s1\"\u003esum\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eint64\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e0\u003c/span\u003e)\n \u003cspan class=\"pl-k\"\u003efor\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003e_\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003earg\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-k\"\u003erange\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eargs\u003c/span\u003e {\n \u003cspan class=\"pl-s1\"\u003esum\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e+=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003earg\u003c/span\u003e\n }\n \u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003esum\u003c/span\u003e, \u003cspan class=\"pl-c1\"\u003enil\u003c/span\u003e\n}\n\n\u003cspan class=\"pl-k\"\u003efunc\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eMultiply\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003eargs\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e...\u003c/span\u003e\u003cspan class=\"pl-smi\"\u003eint64\u003c/span\u003e) (\u003cspan class=\"pl-smi\"\u003eint64\u003c/span\u003e, \u003cspan class=\"pl-smi\"\u003eerror\u003c/span\u003e) {\n \u003cspan class=\"pl-s1\"\u003esum\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eint64\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e)\n \u003cspan class=\"pl-k\"\u003efor\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003e_\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003earg\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-k\"\u003erange\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eargs\u003c/span\u003e {\n \u003cspan class=\"pl-s1\"\u003esum\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e*=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003earg\u003c/span\u003e\n }\n \u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003esum\u003c/span\u003e, \u003cspan class=\"pl-c1\"\u003enil\u003c/span\u003e\n}\n\n\u003cspan class=\"pl-c\"\u003e// You can use context.Context as first argument to tasks, useful for open tracing\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003efunc\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eTaskWithContext\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003ectx\u003c/span\u003e context.\u003cspan class=\"pl-smi\"\u003eContext\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003earg\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eArg\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003eerror\u003c/span\u003e {\n \u003cspan class=\"pl-c\"\u003e// ... use ctx ...\u003c/span\u003e\n \u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003enil\u003c/span\u003e\n}\n\n\u003cspan class=\"pl-c\"\u003e// Tasks need to return at least error as a minimal requirement\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003efunc\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eDummyTask\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003earg\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003estring\u003c/span\u003e) \u003cspan class=\"pl-smi\"\u003eerror\u003c/span\u003e {\n \u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eerrors\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eNew\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003earg\u003c/span\u003e)\n}\n\n\u003cspan class=\"pl-c\"\u003e// You can also return multiple results from the task\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003efunc\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eDummyTask2\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003earg1\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003earg2\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003estring\u003c/span\u003e) (\u003cspan class=\"pl-smi\"\u003estring\u003c/span\u003e, \u003cspan class=\"pl-smi\"\u003estring\u003c/span\u003e, \u003cspan class=\"pl-smi\"\u003eerror\u003c/span\u003e) {\n \u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003earg1\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003earg2\u003c/span\u003e, \u003cspan class=\"pl-c1\"\u003enil\u003c/span\u003e\n}\u003c/pre\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eRegistering Tasks\u003c/h4\u003e\u003ca id=\"user-content-registering-tasks\" class=\"anchor\" aria-label=\"Permalink: Registering Tasks\" href=\"#registering-tasks\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eBefore your workers can consume a task, you need to register it with the server. This is done by assigning a task a unique name:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"server.RegisterTasks(map[string]interface{}{\n \u0026quot;add\u0026quot;: Add,\n \u0026quot;multiply\u0026quot;: Multiply,\n})\"\u003e\u003cpre\u003e\u003cspan class=\"pl-s1\"\u003eserver\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eRegisterTasks\u003c/span\u003e(\u003cspan class=\"pl-k\"\u003emap\u003c/span\u003e[\u003cspan class=\"pl-smi\"\u003estring\u003c/span\u003e]\u003cspan class=\"pl-k\"\u003einterface\u003c/span\u003e{}{\n \u003cspan class=\"pl-s\"\u003e\"add\"\u003c/span\u003e: \u003cspan class=\"pl-s1\"\u003eAdd\u003c/span\u003e,\n \u003cspan class=\"pl-s\"\u003e\"multiply\"\u003c/span\u003e: \u003cspan class=\"pl-s1\"\u003eMultiply\u003c/span\u003e,\n})\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eTasks can also be registered one by one:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"server.RegisterTask(\u0026quot;add\u0026quot;, Add)\nserver.RegisterTask(\u0026quot;multiply\u0026quot;, Multiply)\"\u003e\u003cpre\u003e\u003cspan class=\"pl-s1\"\u003eserver\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eRegisterTask\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"add\"\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003eAdd\u003c/span\u003e)\n\u003cspan class=\"pl-s1\"\u003eserver\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eRegisterTask\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"multiply\"\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003eMultiply\u003c/span\u003e)\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eSimply put, when a worker receives a message like this:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-json notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"{\n \u0026quot;UUID\u0026quot;: \u0026quot;48760a1a-8576-4536-973b-da09048c2ac5\u0026quot;,\n \u0026quot;Name\u0026quot;: \u0026quot;add\u0026quot;,\n \u0026quot;RoutingKey\u0026quot;: \u0026quot;\u0026quot;,\n \u0026quot;ETA\u0026quot;: null,\n \u0026quot;GroupUUID\u0026quot;: \u0026quot;\u0026quot;,\n \u0026quot;GroupTaskCount\u0026quot;: 0,\n \u0026quot;Args\u0026quot;: [\n {\n \u0026quot;Type\u0026quot;: \u0026quot;int64\u0026quot;,\n \u0026quot;Value\u0026quot;: 1,\n },\n {\n \u0026quot;Type\u0026quot;: \u0026quot;int64\u0026quot;,\n \u0026quot;Value\u0026quot;: 1,\n }\n ],\n \u0026quot;Immutable\u0026quot;: false,\n \u0026quot;RetryCount\u0026quot;: 0,\n \u0026quot;RetryTimeout\u0026quot;: 0,\n \u0026quot;OnSuccess\u0026quot;: null,\n \u0026quot;OnError\u0026quot;: null,\n \u0026quot;ChordCallback\u0026quot;: null\n}\"\u003e\u003cpre\u003e{\n \u003cspan class=\"pl-ent\"\u003e\"UUID\"\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e48760a1a-8576-4536-973b-da09048c2ac5\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e,\n \u003cspan class=\"pl-ent\"\u003e\"Name\"\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003eadd\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e,\n \u003cspan class=\"pl-ent\"\u003e\"RoutingKey\"\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e,\n \u003cspan class=\"pl-ent\"\u003e\"ETA\"\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003enull\u003c/span\u003e,\n \u003cspan class=\"pl-ent\"\u003e\"GroupUUID\"\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e,\n \u003cspan class=\"pl-ent\"\u003e\"GroupTaskCount\"\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e0\u003c/span\u003e,\n \u003cspan class=\"pl-ent\"\u003e\"Args\"\u003c/span\u003e: [\n {\n \u003cspan class=\"pl-ent\"\u003e\"Type\"\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003eint64\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e,\n \u003cspan class=\"pl-ent\"\u003e\"Value\"\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e,\n },\n {\n \u003cspan class=\"pl-ent\"\u003e\"Type\"\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003eint64\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e,\n \u003cspan class=\"pl-ent\"\u003e\"Value\"\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e,\n }\n ],\n \u003cspan class=\"pl-ent\"\u003e\"Immutable\"\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003efalse\u003c/span\u003e,\n \u003cspan class=\"pl-ent\"\u003e\"RetryCount\"\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e0\u003c/span\u003e,\n \u003cspan class=\"pl-ent\"\u003e\"RetryTimeout\"\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e0\u003c/span\u003e,\n \u003cspan class=\"pl-ent\"\u003e\"OnSuccess\"\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003enull\u003c/span\u003e,\n \u003cspan class=\"pl-ent\"\u003e\"OnError\"\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003enull\u003c/span\u003e,\n \u003cspan class=\"pl-ent\"\u003e\"ChordCallback\"\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003enull\u003c/span\u003e\n}\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eIt will call Add(1, 1). Each task should return an error as well so we can handle failures.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eIdeally, tasks should be idempotent which means there will be no unintended consequences when a task is called multiple times with the same arguments.\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eSignatures\u003c/h4\u003e\u003ca id=\"user-content-signatures\" class=\"anchor\" aria-label=\"Permalink: Signatures\" href=\"#signatures\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eA signature wraps calling arguments, execution options (such as immutability) and success/error callbacks of a task so it can be sent across the wire to workers. Task signatures implement a simple interface:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"// Arg represents a single argument passed to invocation fo a task\ntype Arg struct {\n Type string\n Value interface{}\n}\n\n// Headers represents the headers which should be used to direct the task\ntype Headers map[string]interface{}\n\n// Signature represents a single task invocation\ntype Signature struct {\n UUID string\n Name string\n RoutingKey string\n ETA *time.Time\n GroupUUID string\n GroupTaskCount int\n Args []Arg\n Headers Headers\n Immutable bool\n RetryCount int\n RetryTimeout int\n OnSuccess []*Signature\n OnError []*Signature\n ChordCallback *Signature\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c\"\u003e// Arg represents a single argument passed to invocation fo a task\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003etype\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eArg\u003c/span\u003e \u003cspan class=\"pl-k\"\u003estruct\u003c/span\u003e {\n \u003cspan class=\"pl-c1\"\u003eType\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003estring\u003c/span\u003e\n \u003cspan class=\"pl-c1\"\u003eValue\u003c/span\u003e \u003cspan class=\"pl-k\"\u003einterface\u003c/span\u003e{}\n}\n\n\u003cspan class=\"pl-c\"\u003e// Headers represents the headers which should be used to direct the task\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003etype\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eHeaders\u003c/span\u003e \u003cspan class=\"pl-k\"\u003emap\u003c/span\u003e[\u003cspan class=\"pl-smi\"\u003estring\u003c/span\u003e]\u003cspan class=\"pl-k\"\u003einterface\u003c/span\u003e{}\n\n\u003cspan class=\"pl-c\"\u003e// Signature represents a single task invocation\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003etype\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eSignature\u003c/span\u003e \u003cspan class=\"pl-k\"\u003estruct\u003c/span\u003e {\n \u003cspan class=\"pl-c1\"\u003eUUID\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003estring\u003c/span\u003e\n \u003cspan class=\"pl-c1\"\u003eName\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003estring\u003c/span\u003e\n \u003cspan class=\"pl-c1\"\u003eRoutingKey\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003estring\u003c/span\u003e\n \u003cspan class=\"pl-c1\"\u003eETA\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e*\u003c/span\u003etime.\u003cspan class=\"pl-smi\"\u003eTime\u003c/span\u003e\n \u003cspan class=\"pl-c1\"\u003eGroupUUID\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003estring\u003c/span\u003e\n \u003cspan class=\"pl-c1\"\u003eGroupTaskCount\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eint\u003c/span\u003e\n \u003cspan class=\"pl-c1\"\u003eArgs\u003c/span\u003e []\u003cspan class=\"pl-smi\"\u003eArg\u003c/span\u003e\n \u003cspan class=\"pl-c1\"\u003eHeaders\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eHeaders\u003c/span\u003e\n \u003cspan class=\"pl-c1\"\u003eImmutable\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003ebool\u003c/span\u003e\n \u003cspan class=\"pl-c1\"\u003eRetryCount\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eint\u003c/span\u003e\n \u003cspan class=\"pl-c1\"\u003eRetryTimeout\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eint\u003c/span\u003e\n \u003cspan class=\"pl-c1\"\u003eOnSuccess\u003c/span\u003e []\u003cspan class=\"pl-c1\"\u003e*\u003c/span\u003e\u003cspan class=\"pl-smi\"\u003eSignature\u003c/span\u003e\n \u003cspan class=\"pl-c1\"\u003eOnError\u003c/span\u003e []\u003cspan class=\"pl-c1\"\u003e*\u003c/span\u003e\u003cspan class=\"pl-smi\"\u003eSignature\u003c/span\u003e\n \u003cspan class=\"pl-c1\"\u003eChordCallback\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e*\u003c/span\u003e\u003cspan class=\"pl-smi\"\u003eSignature\u003c/span\u003e\n}\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003eUUID\u003c/code\u003e is a unique ID of a task. You can either set it yourself or it will be automatically generated.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003eName\u003c/code\u003e is the unique task name by which it is registered against a Server instance.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003eRoutingKey\u003c/code\u003e is used for routing a task to correct queue. If you leave it empty, the default behaviour will be to set it to the default queue's binding key for direct exchange type and to the default queue name for other exchange types.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003eETA\u003c/code\u003e is a timestamp used for delaying a task. if it's nil, the task will be published for workers to consume immediately. If it is set, the task will be delayed until the ETA timestamp.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003eGroupUUID\u003c/code\u003e, \u003ccode\u003eGroupTaskCount\u003c/code\u003e are useful for creating groups of tasks.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003eArgs\u003c/code\u003e is a list of arguments that will be passed to the task when it is executed by a worker.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003eHeaders\u003c/code\u003e is a list of headers that will be used when publishing the task to AMQP queue.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003eImmutable\u003c/code\u003e is a flag which defines whether a result of the executed task can be modified or not. This is important with \u003ccode\u003eOnSuccess\u003c/code\u003e callbacks. Immutable task will not pass its result to its success callbacks while a mutable task will prepend its result to args sent to callback tasks. Long story short, set Immutable to false if you want to pass result of the first task in a chain to the second task.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003eRetryCount\u003c/code\u003e specifies how many times a failed task should be retried (defaults to 0). Retry attempts will be spaced out in time, after each failure another attempt will be scheduled further to the future.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003eRetryTimeout\u003c/code\u003e specifies how long to wait before resending task to the queue for retry attempt. Default behaviour is to use fibonacci sequence to increase the timeout after each failed retry attempt.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003eOnSuccess\u003c/code\u003e defines tasks which will be called after the task has executed successfully. It is a slice of task signature structs.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003eOnError\u003c/code\u003e defines tasks which will be called after the task execution fails. The first argument passed to error callbacks will be the error string returned from the failed task.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003eChordCallback\u003c/code\u003e is used to create a callback to a group of tasks.\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eSupported Types\u003c/h4\u003e\u003ca id=\"user-content-supported-types\" class=\"anchor\" aria-label=\"Permalink: Supported Types\" href=\"#supported-types\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eMachinery encodes tasks to JSON before sending them to the broker. Task results are also stored in the backend as JSON encoded strings. Therefor only types with native JSON representation can be supported. Currently supported types are:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003ebool\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eint\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eint8\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eint16\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eint32\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eint64\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003euint\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003euint8\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003euint16\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003euint32\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003euint64\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003efloat32\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003efloat64\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003estring\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e[]bool\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e[]int\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e[]int8\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e[]int16\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e[]int32\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e[]int64\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e[]uint\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e[]uint8\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e[]uint16\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e[]uint32\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e[]uint64\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e[]float32\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e[]float64\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e[]string\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eSending Tasks\u003c/h4\u003e\u003ca id=\"user-content-sending-tasks\" class=\"anchor\" aria-label=\"Permalink: Sending Tasks\" href=\"#sending-tasks\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eTasks can be called by passing an instance of \u003ccode\u003eSignature\u003c/code\u003e to an \u003ccode\u003eServer\u003c/code\u003e instance. E.g:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"import (\n \u0026quot;github.com/RichardKnop/machinery/v1/tasks\u0026quot;\n)\n\nsignature := \u0026amp;tasks.Signature{\n Name: \u0026quot;add\u0026quot;,\n Args: []tasks.Arg{\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 1,\n },\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 1,\n },\n },\n}\n\nasyncResult, err := server.SendTask(signature)\nif err != nil {\n // failed to send the task\n // do something with the error\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e (\n \u003cspan class=\"pl-s\"\u003e\"github.com/RichardKnop/machinery/v1/tasks\"\u003c/span\u003e\n)\n\n\u003cspan class=\"pl-s1\"\u003esignature\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e\u0026amp;\u003c/span\u003etasks.\u003cspan class=\"pl-smi\"\u003eSignature\u003c/span\u003e{\n \u003cspan class=\"pl-s1\"\u003eName\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"add\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eArgs\u003c/span\u003e: []tasks.\u003cspan class=\"pl-smi\"\u003eArg\u003c/span\u003e{\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e,\n },\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e,\n },\n },\n}\n\n\u003cspan class=\"pl-s1\"\u003easyncResult\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eserver\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eSendTask\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003esignature\u003c/span\u003e)\n\u003cspan class=\"pl-k\"\u003eif\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e!=\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003enil\u003c/span\u003e {\n \u003cspan class=\"pl-c\"\u003e// failed to send the task\u003c/span\u003e\n \u003cspan class=\"pl-c\"\u003e// do something with the error\u003c/span\u003e\n}\u003c/pre\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eDelayed Tasks\u003c/h4\u003e\u003ca id=\"user-content-delayed-tasks\" class=\"anchor\" aria-label=\"Permalink: Delayed Tasks\" href=\"#delayed-tasks\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eYou can delay a task by setting the \u003ccode\u003eETA\u003c/code\u003e timestamp field on the task signature.\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"// Delay the task by 5 seconds\neta := time.Now().UTC().Add(time.Second * 5)\nsignature.ETA = \u0026amp;eta\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c\"\u003e// Delay the task by 5 seconds\u003c/span\u003e\n\u003cspan class=\"pl-s1\"\u003eeta\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003etime\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eNow\u003c/span\u003e().\u003cspan class=\"pl-c1\"\u003eUTC\u003c/span\u003e().\u003cspan class=\"pl-c1\"\u003eAdd\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003etime\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eSecond\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e*\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e5\u003c/span\u003e)\n\u003cspan class=\"pl-s1\"\u003esignature\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eETA\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e=\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"pl-s1\"\u003eeta\u003c/span\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eRetry Tasks\u003c/h4\u003e\u003ca id=\"user-content-retry-tasks\" class=\"anchor\" aria-label=\"Permalink: Retry Tasks\" href=\"#retry-tasks\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eYou can set a number of retry attempts before declaring task as failed. Fibonacci sequence will be used to space out retry requests over time. (See \u003ccode\u003eRetryTimeout\u003c/code\u003e for details.)\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"// If the task fails, retry it up to 3 times\nsignature.RetryCount = 3\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c\"\u003e// If the task fails, retry it up to 3 times\u003c/span\u003e\n\u003cspan class=\"pl-s1\"\u003esignature\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eRetryCount\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e=\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e3\u003c/span\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eAlternatively, you can return \u003ccode\u003etasks.ErrRetryTaskLater\u003c/code\u003e from your task and specify duration after which the task should be retried, e.g.:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"return tasks.NewErrRetryTaskLater(\u0026quot;some error\u0026quot;, 4 * time.Hour)\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003etasks\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eNewErrRetryTaskLater\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"some error\"\u003c/span\u003e, \u003cspan class=\"pl-c1\"\u003e4\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e*\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003etime\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eHour\u003c/span\u003e)\u003c/pre\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eGet Pending Tasks\u003c/h4\u003e\u003ca id=\"user-content-get-pending-tasks\" class=\"anchor\" aria-label=\"Permalink: Get Pending Tasks\" href=\"#get-pending-tasks\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eTasks currently waiting in the queue to be consumed by workers can be inspected, e.g.:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"server.GetBroker().GetPendingTasks(\u0026quot;some_queue\u0026quot;)\"\u003e\u003cpre\u003e\u003cspan class=\"pl-s1\"\u003eserver\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eGetBroker\u003c/span\u003e().\u003cspan class=\"pl-c1\"\u003eGetPendingTasks\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"some_queue\"\u003c/span\u003e)\u003c/pre\u003e\u003c/div\u003e\n\u003cblockquote\u003e\n\u003cp dir=\"auto\"\u003eCurrently only supported by Redis broker.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eKeeping Results\u003c/h4\u003e\u003ca id=\"user-content-keeping-results\" class=\"anchor\" aria-label=\"Permalink: Keeping Results\" href=\"#keeping-results\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eIf you configure a result backend, the task states and results will be persisted. Possible states:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"const (\n\t// StatePending - initial state of a task\n\tStatePending = \u0026quot;PENDING\u0026quot;\n\t// StateReceived - when task is received by a worker\n\tStateReceived = \u0026quot;RECEIVED\u0026quot;\n\t// StateStarted - when the worker starts processing the task\n\tStateStarted = \u0026quot;STARTED\u0026quot;\n\t// StateRetry - when failed task has been scheduled for retry\n\tStateRetry = \u0026quot;RETRY\u0026quot;\n\t// StateSuccess - when the task is processed successfully\n\tStateSuccess = \u0026quot;SUCCESS\u0026quot;\n\t// StateFailure - when processing of the task fails\n\tStateFailure = \u0026quot;FAILURE\u0026quot;\n)\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003econst\u003c/span\u003e (\n\t\u003cspan class=\"pl-c\"\u003e// StatePending - initial state of a task\u003c/span\u003e\n\t\u003cspan class=\"pl-s1\"\u003eStatePending\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e=\u003c/span\u003e \u003cspan class=\"pl-s\"\u003e\"PENDING\"\u003c/span\u003e\n\t\u003cspan class=\"pl-c\"\u003e// StateReceived - when task is received by a worker\u003c/span\u003e\n\t\u003cspan class=\"pl-s1\"\u003eStateReceived\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e=\u003c/span\u003e \u003cspan class=\"pl-s\"\u003e\"RECEIVED\"\u003c/span\u003e\n\t\u003cspan class=\"pl-c\"\u003e// StateStarted - when the worker starts processing the task\u003c/span\u003e\n\t\u003cspan class=\"pl-s1\"\u003eStateStarted\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e=\u003c/span\u003e \u003cspan class=\"pl-s\"\u003e\"STARTED\"\u003c/span\u003e\n\t\u003cspan class=\"pl-c\"\u003e// StateRetry - when failed task has been scheduled for retry\u003c/span\u003e\n\t\u003cspan class=\"pl-s1\"\u003eStateRetry\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e=\u003c/span\u003e \u003cspan class=\"pl-s\"\u003e\"RETRY\"\u003c/span\u003e\n\t\u003cspan class=\"pl-c\"\u003e// StateSuccess - when the task is processed successfully\u003c/span\u003e\n\t\u003cspan class=\"pl-s1\"\u003eStateSuccess\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e=\u003c/span\u003e \u003cspan class=\"pl-s\"\u003e\"SUCCESS\"\u003c/span\u003e\n\t\u003cspan class=\"pl-c\"\u003e// StateFailure - when processing of the task fails\u003c/span\u003e\n\t\u003cspan class=\"pl-s1\"\u003eStateFailure\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e=\u003c/span\u003e \u003cspan class=\"pl-s\"\u003e\"FAILURE\"\u003c/span\u003e\n)\u003c/pre\u003e\u003c/div\u003e\n\u003cblockquote\u003e\n\u003cp dir=\"auto\"\u003eWhen using AMQP as a result backend, task states will be persisted in separate queues for each task. Although RabbitMQ can scale up to thousands of queues, it is strongly advised to use a better suited result backend (e.g. Memcache) when you are expecting to run a large number of parallel tasks.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"// TaskResult represents an actual return value of a processed task\ntype TaskResult struct {\n Type string `bson:\u0026quot;type\u0026quot;`\n Value interface{} `bson:\u0026quot;value\u0026quot;`\n}\n\n// TaskState represents a state of a task\ntype TaskState struct {\n TaskUUID string `bson:\u0026quot;_id\u0026quot;`\n State string `bson:\u0026quot;state\u0026quot;`\n Results []*TaskResult `bson:\u0026quot;results\u0026quot;`\n Error string `bson:\u0026quot;error\u0026quot;`\n}\n\n// GroupMeta stores useful metadata about tasks within the same group\n// E.g. UUIDs of all tasks which are used in order to check if all tasks\n// completed successfully or not and thus whether to trigger chord callback\ntype GroupMeta struct {\n GroupUUID string `bson:\u0026quot;_id\u0026quot;`\n TaskUUIDs []string `bson:\u0026quot;task_uuids\u0026quot;`\n ChordTriggered bool `bson:\u0026quot;chord_triggered\u0026quot;`\n Lock bool `bson:\u0026quot;lock\u0026quot;`\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c\"\u003e// TaskResult represents an actual return value of a processed task\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003etype\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eTaskResult\u003c/span\u003e \u003cspan class=\"pl-k\"\u003estruct\u003c/span\u003e {\n \u003cspan class=\"pl-c1\"\u003eType\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003estring\u003c/span\u003e \u003cspan class=\"pl-s\"\u003e`bson:\"type\"`\u003c/span\u003e\n \u003cspan class=\"pl-c1\"\u003eValue\u003c/span\u003e \u003cspan class=\"pl-k\"\u003einterface\u003c/span\u003e{} \u003cspan class=\"pl-s\"\u003e`bson:\"value\"`\u003c/span\u003e\n}\n\n\u003cspan class=\"pl-c\"\u003e// TaskState represents a state of a task\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003etype\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eTaskState\u003c/span\u003e \u003cspan class=\"pl-k\"\u003estruct\u003c/span\u003e {\n \u003cspan class=\"pl-c1\"\u003eTaskUUID\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003estring\u003c/span\u003e \u003cspan class=\"pl-s\"\u003e`bson:\"_id\"`\u003c/span\u003e\n \u003cspan class=\"pl-c1\"\u003eState\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003estring\u003c/span\u003e \u003cspan class=\"pl-s\"\u003e`bson:\"state\"`\u003c/span\u003e\n \u003cspan class=\"pl-c1\"\u003eResults\u003c/span\u003e []\u003cspan class=\"pl-c1\"\u003e*\u003c/span\u003e\u003cspan class=\"pl-smi\"\u003eTaskResult\u003c/span\u003e \u003cspan class=\"pl-s\"\u003e`bson:\"results\"`\u003c/span\u003e\n \u003cspan class=\"pl-c1\"\u003eError\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003estring\u003c/span\u003e \u003cspan class=\"pl-s\"\u003e`bson:\"error\"`\u003c/span\u003e\n}\n\n\u003cspan class=\"pl-c\"\u003e// GroupMeta stores useful metadata about tasks within the same group\u003c/span\u003e\n\u003cspan class=\"pl-c\"\u003e// E.g. UUIDs of all tasks which are used in order to check if all tasks\u003c/span\u003e\n\u003cspan class=\"pl-c\"\u003e// completed successfully or not and thus whether to trigger chord callback\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003etype\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eGroupMeta\u003c/span\u003e \u003cspan class=\"pl-k\"\u003estruct\u003c/span\u003e {\n \u003cspan class=\"pl-c1\"\u003eGroupUUID\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003estring\u003c/span\u003e \u003cspan class=\"pl-s\"\u003e`bson:\"_id\"`\u003c/span\u003e\n \u003cspan class=\"pl-c1\"\u003eTaskUUIDs\u003c/span\u003e []\u003cspan class=\"pl-smi\"\u003estring\u003c/span\u003e \u003cspan class=\"pl-s\"\u003e`bson:\"task_uuids\"`\u003c/span\u003e\n \u003cspan class=\"pl-c1\"\u003eChordTriggered\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003ebool\u003c/span\u003e \u003cspan class=\"pl-s\"\u003e`bson:\"chord_triggered\"`\u003c/span\u003e\n \u003cspan class=\"pl-c1\"\u003eLock\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003ebool\u003c/span\u003e \u003cspan class=\"pl-s\"\u003e`bson:\"lock\"`\u003c/span\u003e\n}\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003eTaskResult\u003c/code\u003e represents a slice of return values of a processed task.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003eTaskState\u003c/code\u003e struct will be serialized and stored every time a task state changes.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003eGroupMeta\u003c/code\u003e stores useful metadata about tasks within the same group. E.g. UUIDs of all tasks which are used in order to check if all tasks completed successfully or not and thus whether to trigger chord callback.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003eAsyncResult\u003c/code\u003e object allows you to check for the state of a task:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"taskState := asyncResult.GetState()\nfmt.Printf(\u0026quot;Current state of %v task is:\\n\u0026quot;, taskState.TaskUUID)\nfmt.Println(taskState.State)\"\u003e\u003cpre\u003e\u003cspan class=\"pl-s1\"\u003etaskState\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003easyncResult\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eGetState\u003c/span\u003e()\n\u003cspan class=\"pl-s1\"\u003efmt\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003ePrintf\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"Current state of %v task is:\u003cspan class=\"pl-cce\"\u003e\\n\u003c/span\u003e\"\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003etaskState\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eTaskUUID\u003c/span\u003e)\n\u003cspan class=\"pl-s1\"\u003efmt\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003ePrintln\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003etaskState\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eState\u003c/span\u003e)\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eThere are couple of convenient methods to inspect the task status:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"asyncResult.GetState().IsCompleted()\nasyncResult.GetState().IsSuccess()\nasyncResult.GetState().IsFailure()\"\u003e\u003cpre\u003e\u003cspan class=\"pl-s1\"\u003easyncResult\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eGetState\u003c/span\u003e().\u003cspan class=\"pl-c1\"\u003eIsCompleted\u003c/span\u003e()\n\u003cspan class=\"pl-s1\"\u003easyncResult\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eGetState\u003c/span\u003e().\u003cspan class=\"pl-c1\"\u003eIsSuccess\u003c/span\u003e()\n\u003cspan class=\"pl-s1\"\u003easyncResult\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eGetState\u003c/span\u003e().\u003cspan class=\"pl-c1\"\u003eIsFailure\u003c/span\u003e()\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eYou can also do a synchronous blocking call to wait for a task result:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"results, err := asyncResult.Get(time.Duration(time.Millisecond * 5))\nif err != nil {\n // getting result of a task failed\n // do something with the error\n}\nfor _, result := range results {\n fmt.Println(result.Interface())\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-s1\"\u003eresults\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003easyncResult\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eGet\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003etime\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eDuration\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003etime\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eMillisecond\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e*\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e5\u003c/span\u003e))\n\u003cspan class=\"pl-k\"\u003eif\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e!=\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003enil\u003c/span\u003e {\n \u003cspan class=\"pl-c\"\u003e// getting result of a task failed\u003c/span\u003e\n \u003cspan class=\"pl-c\"\u003e// do something with the error\u003c/span\u003e\n}\n\u003cspan class=\"pl-k\"\u003efor\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003e_\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003eresult\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-k\"\u003erange\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eresults\u003c/span\u003e {\n \u003cspan class=\"pl-s1\"\u003efmt\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003ePrintln\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003eresult\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eInterface\u003c/span\u003e())\n}\u003c/pre\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eError Handling\u003c/h4\u003e\u003ca id=\"user-content-error-handling\" class=\"anchor\" aria-label=\"Permalink: Error Handling\" href=\"#error-handling\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eWhen a task returns with an error, the default behavior is to first attempty to retry the task if it's retriable, otherwise log the error and then eventually call any error callbacks.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eTo customize this, you can set a custom error handler on the worker which can do more than just logging after retries fail and error callbacks are trigerred:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"worker.SetErrorHandler(func (err error) {\n customHandler(err)\n})\"\u003e\u003cpre\u003e\u003cspan class=\"pl-s1\"\u003eworker\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eSetErrorHandler\u003c/span\u003e(\u003cspan class=\"pl-k\"\u003efunc\u003c/span\u003e (\u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-smi\"\u003eerror\u003c/span\u003e) {\n \u003cspan class=\"pl-s1\"\u003ecustomHandler\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e)\n})\u003c/pre\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eWorkflows\u003c/h3\u003e\u003ca id=\"user-content-workflows\" class=\"anchor\" aria-label=\"Permalink: Workflows\" href=\"#workflows\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eRunning a single asynchronous task is fine but often you will want to design a workflow of tasks to be executed in an orchestrated way. There are couple of useful functions to help you design workflows.\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eGroups\u003c/h4\u003e\u003ca id=\"user-content-groups\" class=\"anchor\" aria-label=\"Permalink: Groups\" href=\"#groups\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003eGroup\u003c/code\u003e is a set of tasks which will be executed in parallel, independent of each other. E.g.:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"import (\n \u0026quot;github.com/RichardKnop/machinery/v1/tasks\u0026quot;\n \u0026quot;github.com/RichardKnop/machinery/v1\u0026quot;\n)\n\nsignature1 := tasks.Signature{\n Name: \u0026quot;add\u0026quot;,\n Args: []tasks.Arg{\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 1,\n },\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 1,\n },\n },\n}\n\nsignature2 := tasks.Signature{\n Name: \u0026quot;add\u0026quot;,\n Args: []tasks.Arg{\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 5,\n },\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 5,\n },\n },\n}\n\ngroup, _ := tasks.NewGroup(\u0026amp;signature1, \u0026amp;signature2)\nasyncResults, err := server.SendGroup(group, 0) //The second parameter specifies the number of concurrent sending tasks. 0 means unlimited.\nif err != nil {\n // failed to send the group\n // do something with the error\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e (\n \u003cspan class=\"pl-s\"\u003e\"github.com/RichardKnop/machinery/v1/tasks\"\u003c/span\u003e\n \u003cspan class=\"pl-s\"\u003e\"github.com/RichardKnop/machinery/v1\"\u003c/span\u003e\n)\n\n\u003cspan class=\"pl-s1\"\u003esignature1\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e tasks.\u003cspan class=\"pl-smi\"\u003eSignature\u003c/span\u003e{\n \u003cspan class=\"pl-s1\"\u003eName\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"add\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eArgs\u003c/span\u003e: []tasks.\u003cspan class=\"pl-smi\"\u003eArg\u003c/span\u003e{\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e,\n },\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e,\n },\n },\n}\n\n\u003cspan class=\"pl-s1\"\u003esignature2\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e tasks.\u003cspan class=\"pl-smi\"\u003eSignature\u003c/span\u003e{\n \u003cspan class=\"pl-s1\"\u003eName\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"add\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eArgs\u003c/span\u003e: []tasks.\u003cspan class=\"pl-smi\"\u003eArg\u003c/span\u003e{\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e5\u003c/span\u003e,\n },\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e5\u003c/span\u003e,\n },\n },\n}\n\n\u003cspan class=\"pl-s1\"\u003egroup\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003e_\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003etasks\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eNewGroup\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"pl-s1\"\u003esignature1\u003c/span\u003e, \u003cspan class=\"pl-c1\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"pl-s1\"\u003esignature2\u003c/span\u003e)\n\u003cspan class=\"pl-s1\"\u003easyncResults\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eserver\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eSendGroup\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003egroup\u003c/span\u003e, \u003cspan class=\"pl-c1\"\u003e0\u003c/span\u003e) \u003cspan class=\"pl-c\"\u003e//The second parameter specifies the number of concurrent sending tasks. 0 means unlimited.\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003eif\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e!=\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003enil\u003c/span\u003e {\n \u003cspan class=\"pl-c\"\u003e// failed to send the group\u003c/span\u003e\n \u003cspan class=\"pl-c\"\u003e// do something with the error\u003c/span\u003e\n}\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003eSendGroup\u003c/code\u003e returns a slice of \u003ccode\u003eAsyncResult\u003c/code\u003e objects. So you can do a blocking call and wait for the result of groups tasks:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"for _, asyncResult := range asyncResults {\n results, err := asyncResult.Get(time.Duration(time.Millisecond * 5))\n if err != nil {\n // getting result of a task failed\n // do something with the error\n }\n for _, result := range results {\n fmt.Println(result.Interface())\n }\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003efor\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003e_\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003easyncResult\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-k\"\u003erange\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003easyncResults\u003c/span\u003e {\n \u003cspan class=\"pl-s1\"\u003eresults\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003easyncResult\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eGet\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003etime\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eDuration\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003etime\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eMillisecond\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e*\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e5\u003c/span\u003e))\n \u003cspan class=\"pl-k\"\u003eif\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e!=\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003enil\u003c/span\u003e {\n \u003cspan class=\"pl-c\"\u003e// getting result of a task failed\u003c/span\u003e\n \u003cspan class=\"pl-c\"\u003e// do something with the error\u003c/span\u003e\n }\n \u003cspan class=\"pl-k\"\u003efor\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003e_\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003eresult\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-k\"\u003erange\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eresults\u003c/span\u003e {\n \u003cspan class=\"pl-s1\"\u003efmt\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003ePrintln\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003eresult\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eInterface\u003c/span\u003e())\n }\n}\u003c/pre\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eChords\u003c/h4\u003e\u003ca id=\"user-content-chords\" class=\"anchor\" aria-label=\"Permalink: Chords\" href=\"#chords\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003eChord\u003c/code\u003e allows you to define a callback to be executed after all tasks in a group finished processing, e.g.:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"import (\n \u0026quot;github.com/RichardKnop/machinery/v1/tasks\u0026quot;\n \u0026quot;github.com/RichardKnop/machinery/v1\u0026quot;\n)\n\nsignature1 := tasks.Signature{\n Name: \u0026quot;add\u0026quot;,\n Args: []tasks.Arg{\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 1,\n },\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 1,\n },\n },\n}\n\nsignature2 := tasks.Signature{\n Name: \u0026quot;add\u0026quot;,\n Args: []tasks.Arg{\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 5,\n },\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 5,\n },\n },\n}\n\nsignature3 := tasks.Signature{\n Name: \u0026quot;multiply\u0026quot;,\n}\n\ngroup := tasks.NewGroup(\u0026amp;signature1, \u0026amp;signature2)\nchord, _ := tasks.NewChord(group, \u0026amp;signature3)\nchordAsyncResult, err := server.SendChord(chord, 0) //The second parameter specifies the number of concurrent sending tasks. 0 means unlimited.\nif err != nil {\n // failed to send the chord\n // do something with the error\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e (\n \u003cspan class=\"pl-s\"\u003e\"github.com/RichardKnop/machinery/v1/tasks\"\u003c/span\u003e\n \u003cspan class=\"pl-s\"\u003e\"github.com/RichardKnop/machinery/v1\"\u003c/span\u003e\n)\n\n\u003cspan class=\"pl-s1\"\u003esignature1\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e tasks.\u003cspan class=\"pl-smi\"\u003eSignature\u003c/span\u003e{\n \u003cspan class=\"pl-s1\"\u003eName\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"add\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eArgs\u003c/span\u003e: []tasks.\u003cspan class=\"pl-smi\"\u003eArg\u003c/span\u003e{\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e,\n },\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e,\n },\n },\n}\n\n\u003cspan class=\"pl-s1\"\u003esignature2\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e tasks.\u003cspan class=\"pl-smi\"\u003eSignature\u003c/span\u003e{\n \u003cspan class=\"pl-s1\"\u003eName\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"add\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eArgs\u003c/span\u003e: []tasks.\u003cspan class=\"pl-smi\"\u003eArg\u003c/span\u003e{\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e5\u003c/span\u003e,\n },\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e5\u003c/span\u003e,\n },\n },\n}\n\n\u003cspan class=\"pl-s1\"\u003esignature3\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e tasks.\u003cspan class=\"pl-smi\"\u003eSignature\u003c/span\u003e{\n \u003cspan class=\"pl-s1\"\u003eName\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"multiply\"\u003c/span\u003e,\n}\n\n\u003cspan class=\"pl-s1\"\u003egroup\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003etasks\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eNewGroup\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"pl-s1\"\u003esignature1\u003c/span\u003e, \u003cspan class=\"pl-c1\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"pl-s1\"\u003esignature2\u003c/span\u003e)\n\u003cspan class=\"pl-s1\"\u003echord\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003e_\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003etasks\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eNewChord\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003egroup\u003c/span\u003e, \u003cspan class=\"pl-c1\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"pl-s1\"\u003esignature3\u003c/span\u003e)\n\u003cspan class=\"pl-s1\"\u003echordAsyncResult\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eserver\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eSendChord\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003echord\u003c/span\u003e, \u003cspan class=\"pl-c1\"\u003e0\u003c/span\u003e) \u003cspan class=\"pl-c\"\u003e//The second parameter specifies the number of concurrent sending tasks. 0 means unlimited.\u003c/span\u003e\n\u003cspan class=\"pl-k\"\u003eif\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e!=\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003enil\u003c/span\u003e {\n \u003cspan class=\"pl-c\"\u003e// failed to send the chord\u003c/span\u003e\n \u003cspan class=\"pl-c\"\u003e// do something with the error\u003c/span\u003e\n}\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eThe above example executes task1 and task2 in parallel, aggregates their results and passes them to task3. Therefore what would end up happening is:\u003c/p\u003e\n\u003cdiv class=\"snippet-clipboard-content notranslate position-relative overflow-auto\" data-snippet-clipboard-copy-content=\"multiply(add(1, 1), add(5, 5))\"\u003e\u003cpre class=\"notranslate\"\u003e\u003ccode\u003emultiply(add(1, 1), add(5, 5))\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eMore explicitly:\u003c/p\u003e\n\u003cdiv class=\"snippet-clipboard-content notranslate position-relative overflow-auto\" data-snippet-clipboard-copy-content=\"(1 + 1) * (5 + 5) = 2 * 10 = 20\"\u003e\u003cpre class=\"notranslate\"\u003e\u003ccode\u003e(1 + 1) * (5 + 5) = 2 * 10 = 20\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003eSendChord\u003c/code\u003e returns \u003ccode\u003eChordAsyncResult\u003c/code\u003e which follows AsyncResult's interface. So you can do a blocking call and wait for the result of the callback:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"results, err := chordAsyncResult.Get(time.Duration(time.Millisecond * 5))\nif err != nil {\n // getting result of a chord failed\n // do something with the error\n}\nfor _, result := range results {\n fmt.Println(result.Interface())\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-s1\"\u003eresults\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003echordAsyncResult\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eGet\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003etime\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eDuration\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003etime\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eMillisecond\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e*\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e5\u003c/span\u003e))\n\u003cspan class=\"pl-k\"\u003eif\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e!=\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003enil\u003c/span\u003e {\n \u003cspan class=\"pl-c\"\u003e// getting result of a chord failed\u003c/span\u003e\n \u003cspan class=\"pl-c\"\u003e// do something with the error\u003c/span\u003e\n}\n\u003cspan class=\"pl-k\"\u003efor\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003e_\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003eresult\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-k\"\u003erange\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eresults\u003c/span\u003e {\n \u003cspan class=\"pl-s1\"\u003efmt\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003ePrintln\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003eresult\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eInterface\u003c/span\u003e())\n}\u003c/pre\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eChains\u003c/h4\u003e\u003ca id=\"user-content-chains\" class=\"anchor\" aria-label=\"Permalink: Chains\" href=\"#chains\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003eChain\u003c/code\u003e is simply a set of tasks which will be executed one by one, each successful task triggering the next task in the chain. E.g.:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"import (\n \u0026quot;github.com/RichardKnop/machinery/v1/tasks\u0026quot;\n \u0026quot;github.com/RichardKnop/machinery/v1\u0026quot;\n)\n\nsignature1 := tasks.Signature{\n Name: \u0026quot;add\u0026quot;,\n Args: []tasks.Arg{\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 1,\n },\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 1,\n },\n },\n}\n\nsignature2 := tasks.Signature{\n Name: \u0026quot;add\u0026quot;,\n Args: []tasks.Arg{\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 5,\n },\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 5,\n },\n },\n}\n\nsignature3 := tasks.Signature{\n Name: \u0026quot;multiply\u0026quot;,\n Args: []tasks.Arg{\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 4,\n },\n },\n}\n\nchain, _ := tasks.NewChain(\u0026amp;signature1, \u0026amp;signature2, \u0026amp;signature3)\nchainAsyncResult, err := server.SendChain(chain)\nif err != nil {\n // failed to send the chain\n // do something with the error\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e (\n \u003cspan class=\"pl-s\"\u003e\"github.com/RichardKnop/machinery/v1/tasks\"\u003c/span\u003e\n \u003cspan class=\"pl-s\"\u003e\"github.com/RichardKnop/machinery/v1\"\u003c/span\u003e\n)\n\n\u003cspan class=\"pl-s1\"\u003esignature1\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e tasks.\u003cspan class=\"pl-smi\"\u003eSignature\u003c/span\u003e{\n \u003cspan class=\"pl-s1\"\u003eName\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"add\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eArgs\u003c/span\u003e: []tasks.\u003cspan class=\"pl-smi\"\u003eArg\u003c/span\u003e{\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e,\n },\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e,\n },\n },\n}\n\n\u003cspan class=\"pl-s1\"\u003esignature2\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e tasks.\u003cspan class=\"pl-smi\"\u003eSignature\u003c/span\u003e{\n \u003cspan class=\"pl-s1\"\u003eName\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"add\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eArgs\u003c/span\u003e: []tasks.\u003cspan class=\"pl-smi\"\u003eArg\u003c/span\u003e{\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e5\u003c/span\u003e,\n },\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e5\u003c/span\u003e,\n },\n },\n}\n\n\u003cspan class=\"pl-s1\"\u003esignature3\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e tasks.\u003cspan class=\"pl-smi\"\u003eSignature\u003c/span\u003e{\n \u003cspan class=\"pl-s1\"\u003eName\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"multiply\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eArgs\u003c/span\u003e: []tasks.\u003cspan class=\"pl-smi\"\u003eArg\u003c/span\u003e{\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e4\u003c/span\u003e,\n },\n },\n}\n\n\u003cspan class=\"pl-s1\"\u003echain\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003e_\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003etasks\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eNewChain\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"pl-s1\"\u003esignature1\u003c/span\u003e, \u003cspan class=\"pl-c1\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"pl-s1\"\u003esignature2\u003c/span\u003e, \u003cspan class=\"pl-c1\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"pl-s1\"\u003esignature3\u003c/span\u003e)\n\u003cspan class=\"pl-s1\"\u003echainAsyncResult\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eserver\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eSendChain\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003echain\u003c/span\u003e)\n\u003cspan class=\"pl-k\"\u003eif\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e!=\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003enil\u003c/span\u003e {\n \u003cspan class=\"pl-c\"\u003e// failed to send the chain\u003c/span\u003e\n \u003cspan class=\"pl-c\"\u003e// do something with the error\u003c/span\u003e\n}\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eThe above example executes task1, then task2 and then task3. When a task is completed successfully, the result is appended to the end of list of arguments for the next task in the chain. Therefore what would end up happening is:\u003c/p\u003e\n\u003cdiv class=\"snippet-clipboard-content notranslate position-relative overflow-auto\" data-snippet-clipboard-copy-content=\"multiply(4, add(5, 5, add(1, 1)))\"\u003e\u003cpre class=\"notranslate\"\u003e\u003ccode\u003emultiply(4, add(5, 5, add(1, 1)))\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eMore explicitly:\u003c/p\u003e\n\u003cdiv class=\"snippet-clipboard-content notranslate position-relative overflow-auto\" data-snippet-clipboard-copy-content=\" 4 * (5 + 5 + (1 + 1)) # task1: add(1, 1) returns 2\n= 4 * (5 + 5 + 2) # task2: add(5, 5, 2) returns 12\n= 4 * (12) # task3: multiply(4, 12) returns 48\n= 48\"\u003e\u003cpre class=\"notranslate\"\u003e\u003ccode\u003e 4 * (5 + 5 + (1 + 1)) # task1: add(1, 1) returns 2\n= 4 * (5 + 5 + 2) # task2: add(5, 5, 2) returns 12\n= 4 * (12) # task3: multiply(4, 12) returns 48\n= 48\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003e\u003ccode\u003eSendChain\u003c/code\u003e returns \u003ccode\u003eChainAsyncResult\u003c/code\u003e which follows AsyncResult's interface. So you can do a blocking call and wait for the result of the whole chain:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"results, err := chainAsyncResult.Get(time.Duration(time.Millisecond * 5))\nif err != nil {\n // getting result of a chain failed\n // do something with the error\n}\nfor _, result := range results {\n fmt.Println(result.Interface())\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-s1\"\u003eresults\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003echainAsyncResult\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eGet\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003etime\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eDuration\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003etime\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eMillisecond\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e*\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e5\u003c/span\u003e))\n\u003cspan class=\"pl-k\"\u003eif\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e!=\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003enil\u003c/span\u003e {\n \u003cspan class=\"pl-c\"\u003e// getting result of a chain failed\u003c/span\u003e\n \u003cspan class=\"pl-c\"\u003e// do something with the error\u003c/span\u003e\n}\n\u003cspan class=\"pl-k\"\u003efor\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003e_\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003eresult\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-k\"\u003erange\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eresults\u003c/span\u003e {\n \u003cspan class=\"pl-s1\"\u003efmt\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003ePrintln\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003eresult\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eInterface\u003c/span\u003e())\n}\u003c/pre\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003ePeriodic Tasks \u0026amp; Workflows\u003c/h3\u003e\u003ca id=\"user-content-periodic-tasks--workflows\" class=\"anchor\" aria-label=\"Permalink: Periodic Tasks \u0026amp; Workflows\" href=\"#periodic-tasks--workflows\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eMachinery now supports scheduling periodic tasks and workflows. See examples bellow.\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003ePeriodic Tasks\u003c/h4\u003e\u003ca id=\"user-content-periodic-tasks\" class=\"anchor\" aria-label=\"Permalink: Periodic Tasks\" href=\"#periodic-tasks\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"import (\n \u0026quot;github.com/RichardKnop/machinery/v1/tasks\u0026quot;\n)\n\nsignature := \u0026amp;tasks.Signature{\n Name: \u0026quot;add\u0026quot;,\n Args: []tasks.Arg{\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 1,\n },\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 1,\n },\n },\n}\nerr := server.RegisterPeriodicTask(\u0026quot;0 6 * * ?\u0026quot;, \u0026quot;periodic-task\u0026quot;, signature)\nif err != nil {\n // failed to register periodic task\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e (\n \u003cspan class=\"pl-s\"\u003e\"github.com/RichardKnop/machinery/v1/tasks\"\u003c/span\u003e\n)\n\n\u003cspan class=\"pl-s1\"\u003esignature\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e\u0026amp;\u003c/span\u003etasks.\u003cspan class=\"pl-smi\"\u003eSignature\u003c/span\u003e{\n \u003cspan class=\"pl-s1\"\u003eName\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"add\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eArgs\u003c/span\u003e: []tasks.\u003cspan class=\"pl-smi\"\u003eArg\u003c/span\u003e{\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e,\n },\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e,\n },\n },\n}\n\u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eserver\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eRegisterPeriodicTask\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"0 6 * * ?\"\u003c/span\u003e, \u003cspan class=\"pl-s\"\u003e\"periodic-task\"\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003esignature\u003c/span\u003e)\n\u003cspan class=\"pl-k\"\u003eif\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e!=\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003enil\u003c/span\u003e {\n \u003cspan class=\"pl-c\"\u003e// failed to register periodic task\u003c/span\u003e\n}\u003c/pre\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003ePeriodic Groups\u003c/h4\u003e\u003ca id=\"user-content-periodic-groups\" class=\"anchor\" aria-label=\"Permalink: Periodic Groups\" href=\"#periodic-groups\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"import (\n \u0026quot;github.com/RichardKnop/machinery/v1/tasks\u0026quot;\n \u0026quot;github.com/RichardKnop/machinery/v1\u0026quot;\n)\n\nsignature1 := tasks.Signature{\n Name: \u0026quot;add\u0026quot;,\n Args: []tasks.Arg{\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 1,\n },\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 1,\n },\n },\n}\n\nsignature2 := tasks.Signature{\n Name: \u0026quot;add\u0026quot;,\n Args: []tasks.Arg{\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 5,\n },\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 5,\n },\n },\n}\n\ngroup, _ := tasks.NewGroup(\u0026amp;signature1, \u0026amp;signature2)\nerr := server.RegisterPeriodicGroup(\u0026quot;0 6 * * ?\u0026quot;, \u0026quot;periodic-group\u0026quot;, group)\nif err != nil {\n // failed to register periodic group\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e (\n \u003cspan class=\"pl-s\"\u003e\"github.com/RichardKnop/machinery/v1/tasks\"\u003c/span\u003e\n \u003cspan class=\"pl-s\"\u003e\"github.com/RichardKnop/machinery/v1\"\u003c/span\u003e\n)\n\n\u003cspan class=\"pl-s1\"\u003esignature1\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e tasks.\u003cspan class=\"pl-smi\"\u003eSignature\u003c/span\u003e{\n \u003cspan class=\"pl-s1\"\u003eName\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"add\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eArgs\u003c/span\u003e: []tasks.\u003cspan class=\"pl-smi\"\u003eArg\u003c/span\u003e{\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e,\n },\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e,\n },\n },\n}\n\n\u003cspan class=\"pl-s1\"\u003esignature2\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e tasks.\u003cspan class=\"pl-smi\"\u003eSignature\u003c/span\u003e{\n \u003cspan class=\"pl-s1\"\u003eName\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"add\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eArgs\u003c/span\u003e: []tasks.\u003cspan class=\"pl-smi\"\u003eArg\u003c/span\u003e{\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e5\u003c/span\u003e,\n },\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e5\u003c/span\u003e,\n },\n },\n}\n\n\u003cspan class=\"pl-s1\"\u003egroup\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003e_\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003etasks\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eNewGroup\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"pl-s1\"\u003esignature1\u003c/span\u003e, \u003cspan class=\"pl-c1\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"pl-s1\"\u003esignature2\u003c/span\u003e)\n\u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eserver\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eRegisterPeriodicGroup\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"0 6 * * ?\"\u003c/span\u003e, \u003cspan class=\"pl-s\"\u003e\"periodic-group\"\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003egroup\u003c/span\u003e)\n\u003cspan class=\"pl-k\"\u003eif\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e!=\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003enil\u003c/span\u003e {\n \u003cspan class=\"pl-c\"\u003e// failed to register periodic group\u003c/span\u003e\n}\u003c/pre\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003ePeriodic Chains\u003c/h4\u003e\u003ca id=\"user-content-periodic-chains\" class=\"anchor\" aria-label=\"Permalink: Periodic Chains\" href=\"#periodic-chains\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"import (\n \u0026quot;github.com/RichardKnop/machinery/v1/tasks\u0026quot;\n \u0026quot;github.com/RichardKnop/machinery/v1\u0026quot;\n)\n\nsignature1 := tasks.Signature{\n Name: \u0026quot;add\u0026quot;,\n Args: []tasks.Arg{\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 1,\n },\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 1,\n },\n },\n}\n\nsignature2 := tasks.Signature{\n Name: \u0026quot;add\u0026quot;,\n Args: []tasks.Arg{\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 5,\n },\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 5,\n },\n },\n}\n\nsignature3 := tasks.Signature{\n Name: \u0026quot;multiply\u0026quot;,\n Args: []tasks.Arg{\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 4,\n },\n },\n}\n\nchain, _ := tasks.NewChain(\u0026amp;signature1, \u0026amp;signature2, \u0026amp;signature3)\nerr := server.RegisterPeriodicChain(\u0026quot;0 6 * * ?\u0026quot;, \u0026quot;periodic-chain\u0026quot;, chain)\nif err != nil {\n // failed to register periodic chain\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e (\n \u003cspan class=\"pl-s\"\u003e\"github.com/RichardKnop/machinery/v1/tasks\"\u003c/span\u003e\n \u003cspan class=\"pl-s\"\u003e\"github.com/RichardKnop/machinery/v1\"\u003c/span\u003e\n)\n\n\u003cspan class=\"pl-s1\"\u003esignature1\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e tasks.\u003cspan class=\"pl-smi\"\u003eSignature\u003c/span\u003e{\n \u003cspan class=\"pl-s1\"\u003eName\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"add\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eArgs\u003c/span\u003e: []tasks.\u003cspan class=\"pl-smi\"\u003eArg\u003c/span\u003e{\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e,\n },\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e,\n },\n },\n}\n\n\u003cspan class=\"pl-s1\"\u003esignature2\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e tasks.\u003cspan class=\"pl-smi\"\u003eSignature\u003c/span\u003e{\n \u003cspan class=\"pl-s1\"\u003eName\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"add\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eArgs\u003c/span\u003e: []tasks.\u003cspan class=\"pl-smi\"\u003eArg\u003c/span\u003e{\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e5\u003c/span\u003e,\n },\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e5\u003c/span\u003e,\n },\n },\n}\n\n\u003cspan class=\"pl-s1\"\u003esignature3\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e tasks.\u003cspan class=\"pl-smi\"\u003eSignature\u003c/span\u003e{\n \u003cspan class=\"pl-s1\"\u003eName\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"multiply\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eArgs\u003c/span\u003e: []tasks.\u003cspan class=\"pl-smi\"\u003eArg\u003c/span\u003e{\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e4\u003c/span\u003e,\n },\n },\n}\n\n\u003cspan class=\"pl-s1\"\u003echain\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003e_\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003etasks\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eNewChain\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"pl-s1\"\u003esignature1\u003c/span\u003e, \u003cspan class=\"pl-c1\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"pl-s1\"\u003esignature2\u003c/span\u003e, \u003cspan class=\"pl-c1\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"pl-s1\"\u003esignature3\u003c/span\u003e)\n\u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eserver\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eRegisterPeriodicChain\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"0 6 * * ?\"\u003c/span\u003e, \u003cspan class=\"pl-s\"\u003e\"periodic-chain\"\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003echain\u003c/span\u003e)\n\u003cspan class=\"pl-k\"\u003eif\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e!=\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003enil\u003c/span\u003e {\n \u003cspan class=\"pl-c\"\u003e// failed to register periodic chain\u003c/span\u003e\n}\u003c/pre\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eChord\u003c/h4\u003e\u003ca id=\"user-content-chord\" class=\"anchor\" aria-label=\"Permalink: Chord\" href=\"#chord\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight highlight-source-go notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"import (\n \u0026quot;github.com/RichardKnop/machinery/v1/tasks\u0026quot;\n \u0026quot;github.com/RichardKnop/machinery/v1\u0026quot;\n)\n\nsignature1 := tasks.Signature{\n Name: \u0026quot;add\u0026quot;,\n Args: []tasks.Arg{\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 1,\n },\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 1,\n },\n },\n}\n\nsignature2 := tasks.Signature{\n Name: \u0026quot;add\u0026quot;,\n Args: []tasks.Arg{\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 5,\n },\n {\n Type: \u0026quot;int64\u0026quot;,\n Value: 5,\n },\n },\n}\n\nsignature3 := tasks.Signature{\n Name: \u0026quot;multiply\u0026quot;,\n}\n\ngroup := tasks.NewGroup(\u0026amp;signature1, \u0026amp;signature2)\nchord, _ := tasks.NewChord(group, \u0026amp;signature3)\nerr := server.RegisterPeriodicChord(\u0026quot;0 6 * * ?\u0026quot;, \u0026quot;periodic-chord\u0026quot;, chord)\nif err != nil {\n // failed to register periodic chord\n}\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003eimport\u003c/span\u003e (\n \u003cspan class=\"pl-s\"\u003e\"github.com/RichardKnop/machinery/v1/tasks\"\u003c/span\u003e\n \u003cspan class=\"pl-s\"\u003e\"github.com/RichardKnop/machinery/v1\"\u003c/span\u003e\n)\n\n\u003cspan class=\"pl-s1\"\u003esignature1\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e tasks.\u003cspan class=\"pl-smi\"\u003eSignature\u003c/span\u003e{\n \u003cspan class=\"pl-s1\"\u003eName\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"add\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eArgs\u003c/span\u003e: []tasks.\u003cspan class=\"pl-smi\"\u003eArg\u003c/span\u003e{\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e,\n },\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e1\u003c/span\u003e,\n },\n },\n}\n\n\u003cspan class=\"pl-s1\"\u003esignature2\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e tasks.\u003cspan class=\"pl-smi\"\u003eSignature\u003c/span\u003e{\n \u003cspan class=\"pl-s1\"\u003eName\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"add\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eArgs\u003c/span\u003e: []tasks.\u003cspan class=\"pl-smi\"\u003eArg\u003c/span\u003e{\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e5\u003c/span\u003e,\n },\n {\n \u003cspan class=\"pl-s1\"\u003eType\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"int64\"\u003c/span\u003e,\n \u003cspan class=\"pl-s1\"\u003eValue\u003c/span\u003e: \u003cspan class=\"pl-c1\"\u003e5\u003c/span\u003e,\n },\n },\n}\n\n\u003cspan class=\"pl-s1\"\u003esignature3\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e tasks.\u003cspan class=\"pl-smi\"\u003eSignature\u003c/span\u003e{\n \u003cspan class=\"pl-s1\"\u003eName\u003c/span\u003e: \u003cspan class=\"pl-s\"\u003e\"multiply\"\u003c/span\u003e,\n}\n\n\u003cspan class=\"pl-s1\"\u003egroup\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003etasks\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eNewGroup\u003c/span\u003e(\u003cspan class=\"pl-c1\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"pl-s1\"\u003esignature1\u003c/span\u003e, \u003cspan class=\"pl-c1\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"pl-s1\"\u003esignature2\u003c/span\u003e)\n\u003cspan class=\"pl-s1\"\u003echord\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003e_\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003etasks\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eNewChord\u003c/span\u003e(\u003cspan class=\"pl-s1\"\u003egroup\u003c/span\u003e, \u003cspan class=\"pl-c1\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"pl-s1\"\u003esignature3\u003c/span\u003e)\n\u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e:=\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eserver\u003c/span\u003e.\u003cspan class=\"pl-c1\"\u003eRegisterPeriodicChord\u003c/span\u003e(\u003cspan class=\"pl-s\"\u003e\"0 6 * * ?\"\u003c/span\u003e, \u003cspan class=\"pl-s\"\u003e\"periodic-chord\"\u003c/span\u003e, \u003cspan class=\"pl-s1\"\u003echord\u003c/span\u003e)\n\u003cspan class=\"pl-k\"\u003eif\u003c/span\u003e \u003cspan class=\"pl-s1\"\u003eerr\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003e!=\u003c/span\u003e \u003cspan class=\"pl-c1\"\u003enil\u003c/span\u003e {\n \u003cspan class=\"pl-c\"\u003e// failed to register periodic chord\u003c/span\u003e\n}\u003c/pre\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eDevelopment\u003c/h3\u003e\u003ca id=\"user-content-development\" class=\"anchor\" aria-label=\"Permalink: Development\" href=\"#development\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eRequirements\u003c/h4\u003e\u003ca id=\"user-content-requirements\" class=\"anchor\" aria-label=\"Permalink: Requirements\" href=\"#requirements\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eGo\u003c/li\u003e\n\u003cli\u003eRabbitMQ (optional)\u003c/li\u003e\n\u003cli\u003eRedis\u003c/li\u003e\n\u003cli\u003eMemcached (optional)\u003c/li\u003e\n\u003cli\u003eMongoDB (optional)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eOn OS X systems, you can install requirements using \u003ca href=\"http://brew.sh/\" rel=\"nofollow\"\u003eHomebrew\u003c/a\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"brew install go\nbrew install rabbitmq\nbrew install redis\nbrew install memcached\nbrew install mongodb\"\u003e\u003cpre\u003ebrew install go\nbrew install rabbitmq\nbrew install redis\nbrew install memcached\nbrew install mongodb\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eOr optionally use the corresponding \u003ca href=\"http://docker.io/\" rel=\"nofollow\"\u003eDocker\u003c/a\u003e containers:\u003c/p\u003e\n\u003cdiv class=\"snippet-clipboard-content notranslate position-relative overflow-auto\" data-snippet-clipboard-copy-content=\"docker run -d -p 5672:5672 rabbitmq\ndocker run -d -p 6379:6379 redis\ndocker run -d -p 11211:11211 memcached\ndocker run -d -p 27017:27017 mongo\ndocker run -d -p 6831:6831/udp -p 16686:16686 jaegertracing/all-in-one:latest\"\u003e\u003cpre class=\"notranslate\"\u003e\u003ccode\u003edocker run -d -p 5672:5672 rabbitmq\ndocker run -d -p 6379:6379 redis\ndocker run -d -p 11211:11211 memcached\ndocker run -d -p 27017:27017 mongo\ndocker run -d -p 6831:6831/udp -p 16686:16686 jaegertracing/all-in-one:latest\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eDependencies\u003c/h4\u003e\u003ca id=\"user-content-dependencies\" class=\"anchor\" aria-label=\"Permalink: Dependencies\" href=\"#dependencies\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eSince Go 1.11, a new recommended dependency management system is via \u003ca href=\"https://github.com/golang/go/wiki/Modules\"\u003emodules\u003c/a\u003e.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eThis is one of slight weaknesses of Go as dependency management is not a solved problem. Previously Go was officially recommending to use the \u003ca href=\"https://github.com/golang/dep\"\u003edep tool\u003c/a\u003e but that has been abandoned now in favor of modules.\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eTesting\u003c/h4\u003e\u003ca id=\"user-content-testing\" class=\"anchor\" aria-label=\"Permalink: Testing\" href=\"#testing\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eEasiest (and platform agnostic) way to run tests is via \u003ccode\u003edocker-compose\u003c/code\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"make ci\"\u003e\u003cpre\u003emake ci\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eThis will basically run docker-compose command:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"(docker-compose -f docker-compose.test.yml -p machinery_ci up --build -d) \u0026amp;\u0026amp; (docker logs -f machinery_sut \u0026amp;) \u0026amp;\u0026amp; (docker wait machinery_sut)\"\u003e\u003cpre\u003e(docker-compose -f docker-compose.test.yml -p machinery_ci up --build -d) \u003cspan class=\"pl-k\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e (docker logs -f machinery_sut \u003cspan class=\"pl-k\"\u003e\u0026amp;\u003c/span\u003e) \u003cspan class=\"pl-k\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e (docker \u003cspan class=\"pl-c1\"\u003ewait\u003c/span\u003e machinery_sut)\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eAlternative approach is to setup a development environment on your machine.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eIn order to enable integration tests, you will need to install all required services (RabbitMQ, Redis, Memcache, MongoDB) and export these environment variables:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"export AMQP_URL=amqp://guest:guest@localhost:5672/\nexport REDIS_URL=localhost:6379\nexport MEMCACHE_URL=localhost:11211\nexport MONGODB_URL=localhost:27017\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003eexport\u003c/span\u003e AMQP_URL=amqp://guest:guest@localhost:5672/\n\u003cspan class=\"pl-k\"\u003eexport\u003c/span\u003e REDIS_URL=localhost:6379\n\u003cspan class=\"pl-k\"\u003eexport\u003c/span\u003e MEMCACHE_URL=localhost:11211\n\u003cspan class=\"pl-k\"\u003eexport\u003c/span\u003e MONGODB_URL=localhost:27017\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eTo run integration tests against an SQS instance, you will need to create a \"test_queue\" in SQS and export these environment variables:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"export SQS_URL=https://YOUR_SQS_URL\nexport AWS_ACCESS_KEY_ID=YOUR_AWS_ACCESS_KEY_ID\nexport AWS_SECRET_ACCESS_KEY=YOUR_AWS_SECRET_ACCESS_KEY\nexport AWS_DEFAULT_REGION=YOUR_AWS_DEFAULT_REGION\"\u003e\u003cpre\u003e\u003cspan class=\"pl-k\"\u003eexport\u003c/span\u003e SQS_URL=https://YOUR_SQS_URL\n\u003cspan class=\"pl-k\"\u003eexport\u003c/span\u003e AWS_ACCESS_KEY_ID=YOUR_AWS_ACCESS_KEY_ID\n\u003cspan class=\"pl-k\"\u003eexport\u003c/span\u003e AWS_SECRET_ACCESS_KEY=YOUR_AWS_SECRET_ACCESS_KEY\n\u003cspan class=\"pl-k\"\u003eexport\u003c/span\u003e AWS_DEFAULT_REGION=YOUR_AWS_DEFAULT_REGION\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eThen just run:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"make test\"\u003e\u003cpre\u003emake \u003cspan class=\"pl-c1\"\u003etest\u003c/span\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eIf the environment variables are not exported, \u003ccode\u003emake test\u003c/code\u003e will only run unit tests.\u003c/p\u003e\n\u003c/article\u003e","loaded":true,"timedOut":false,"errorMessage":null,"headerInfo":{"toc":[{"level":2,"text":"Machinery","anchor":"machinery","htmlText":"Machinery"},{"level":3,"text":"V2","anchor":"v2","htmlText":"V2"},{"level":3,"text":"First Steps","anchor":"first-steps","htmlText":"First Steps"},{"level":3,"text":"Configuration","anchor":"configuration","htmlText":"Configuration"},{"level":4,"text":"Lock","anchor":"lock","htmlText":"Lock"},{"level":5,"text":"Redis","anchor":"redis","htmlText":"Redis"},{"level":4,"text":"Broker","anchor":"broker","htmlText":"Broker"},{"level":5,"text":"AMQP","anchor":"amqp","htmlText":"AMQP"},{"level":5,"text":"Redis","anchor":"redis-1","htmlText":"Redis"},{"level":5,"text":"AWS SQS","anchor":"aws-sqs","htmlText":"AWS SQS"},{"level":5,"text":"GCP Pub/Sub","anchor":"gcp-pubsub","htmlText":"GCP Pub/Sub"},{"level":4,"text":"DefaultQueue","anchor":"defaultqueue","htmlText":"DefaultQueue"},{"level":4,"text":"ResultBackend","anchor":"resultbackend","htmlText":"ResultBackend"},{"level":5,"text":"Redis","anchor":"redis-2","htmlText":"Redis"},{"level":5,"text":"Memcache","anchor":"memcache","htmlText":"Memcache"},{"level":5,"text":"AMQP","anchor":"amqp-1","htmlText":"AMQP"},{"level":5,"text":"MongoDB","anchor":"mongodb","htmlText":"MongoDB"},{"level":4,"text":"ResultsExpireIn","anchor":"resultsexpirein","htmlText":"ResultsExpireIn"},{"level":4,"text":"AMQP","anchor":"amqp-2","htmlText":"AMQP"},{"level":4,"text":"DynamoDB","anchor":"dynamodb","htmlText":"DynamoDB"},{"level":4,"text":"Redis","anchor":"redis-3","htmlText":"Redis"},{"level":4,"text":"GCPPubSub","anchor":"gcppubsub","htmlText":"GCPPubSub"},{"level":3,"text":"Custom Logger","anchor":"custom-logger","htmlText":"Custom Logger"},{"level":3,"text":"Server","anchor":"server","htmlText":"Server"},{"level":3,"text":"Workers","anchor":"workers","htmlText":"Workers"},{"level":3,"text":"Tasks","anchor":"tasks","htmlText":"Tasks"},{"level":4,"text":"Registering Tasks","anchor":"registering-tasks","htmlText":"Registering Tasks"},{"level":4,"text":"Signatures","anchor":"signatures","htmlText":"Signatures"},{"level":4,"text":"Supported Types","anchor":"supported-types","htmlText":"Supported Types"},{"level":4,"text":"Sending Tasks","anchor":"sending-tasks","htmlText":"Sending Tasks"},{"level":4,"text":"Delayed Tasks","anchor":"delayed-tasks","htmlText":"Delayed Tasks"},{"level":4,"text":"Retry Tasks","anchor":"retry-tasks","htmlText":"Retry Tasks"},{"level":4,"text":"Get Pending Tasks","anchor":"get-pending-tasks","htmlText":"Get Pending Tasks"},{"level":4,"text":"Keeping Results","anchor":"keeping-results","htmlText":"Keeping Results"},{"level":4,"text":"Error Handling","anchor":"error-handling","htmlText":"Error Handling"},{"level":3,"text":"Workflows","anchor":"workflows","htmlText":"Workflows"},{"level":4,"text":"Groups","anchor":"groups","htmlText":"Groups"},{"level":4,"text":"Chords","anchor":"chords","htmlText":"Chords"},{"level":4,"text":"Chains","anchor":"chains","htmlText":"Chains"},{"level":3,"text":"Periodic Tasks \u0026 Workflows","anchor":"periodic-tasks--workflows","htmlText":"Periodic Tasks \u0026amp; Workflows"},{"level":4,"text":"Periodic Tasks","anchor":"periodic-tasks","htmlText":"Periodic Tasks"},{"level":4,"text":"Periodic Groups","anchor":"periodic-groups","htmlText":"Periodic Groups"},{"level":4,"text":"Periodic Chains","anchor":"periodic-chains","htmlText":"Periodic Chains"},{"level":4,"text":"Chord","anchor":"chord","htmlText":"Chord"},{"level":3,"text":"Development","anchor":"development","htmlText":"Development"},{"level":4,"text":"Requirements","anchor":"requirements","htmlText":"Requirements"},{"level":4,"text":"Dependencies","anchor":"dependencies","htmlText":"Dependencies"},{"level":4,"text":"Testing","anchor":"testing","htmlText":"Testing"}],"siteNavLoginPath":"/login?return_to=https%3A%2F%2Fgithub.com%2FRichardKnop%2Fmachinery"}},{"displayName":"LICENSE","repoName":"machinery","refName":"master","path":"LICENSE","preferredFileType":"license","tabName":"MPL-2.0","richText":null,"loaded":false,"timedOut":false,"errorMessage":null,"headerInfo":{"toc":null,"siteNavLoginPath":"/login?return_to=https%3A%2F%2Fgithub.com%2FRichardKnop%2Fmachinery"}}],"overviewFilesProcessingTime":0}},"appPayload":{"helpUrl":"https://docs.github.com","findFileWorkerPath":"/assets-cdn/worker/find-file-worker-7d7eb7c71814.js","findInFileWorkerPath":"/assets-cdn/worker/find-in-file-worker-96e76d5fdb2c.js","githubDevUrl":null,"enabled_features":{"copilot_workspace":null,"code_nav_ui_events":false,"overview_shared_code_dropdown_button":false,"react_blob_overlay":false,"accessible_code_button":true,"github_models_repo_integration":false}}}}</script> <div data-target="react-partial.reactRoot"><style data-styled="true" data-styled-version="5.3.11">.iVEunk{margin-top:16px;margin-bottom:16px;}/*!sc*/ .jzuOtQ{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;}/*!sc*/ .bGojzy{margin-bottom:0;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;row-gap:16px;}/*!sc*/ .iNSVHo{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;padding-bottom:16px;padding-top:8px;}/*!sc*/ .bVgnfw{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;gap:8px;}/*!sc*/ @media screen and (max-width:320px){.bVgnfw{-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;}}/*!sc*/ .CEgMp{position:relative;}/*!sc*/ @media screen and (max-width:380px){.CEgMp .ref-selector-button-text-container{max-width:80px;}}/*!sc*/ @media screen and (max-width:320px){.CEgMp{-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;}.CEgMp .overview-ref-selector{width:100%;}.CEgMp .overview-ref-selector > span{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-pack:start;-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start;}.CEgMp .overview-ref-selector > span > span[data-component="text"]{-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;}}/*!sc*/ .gMOVLe[data-size="medium"]{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;min-width:0;}/*!sc*/ .gMOVLe[data-size="medium"] svg{color:var(--fgColor-muted,var(--color-fg-muted,#656d76));}/*!sc*/ .gMOVLe[data-size="medium"] > span{width:inherit;}/*!sc*/ .gUkoLg{-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;}/*!sc*/ .bZBlpz{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;}/*!sc*/ .lhTYNA{margin-right:4px;color:var(--fgColor-muted,var(--color-fg-muted,#656d76));}/*!sc*/ .ffLUq{font-size:14px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}/*!sc*/ .bmcJak{min-width:0;}/*!sc*/ .fLXEGX{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}/*!sc*/ @media screen and (max-width:1079px){.fLXEGX{display:none;}}/*!sc*/ .lmSMZJ[data-size="medium"]{color:var(--fgColor-muted,var(--color-fg-muted,#656d76));padding-left:4px;padding-right:4px;}/*!sc*/ .lmSMZJ[data-size="medium"] span[data-component="leadingVisual"]{margin-right:4px !important;}/*!sc*/ .dqfxud{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}/*!sc*/ @media screen and (min-width:1080px){.dqfxud{display:none;}}/*!sc*/ @media screen and (max-width:543px){.dqfxud{display:none;}}/*!sc*/ .fGwBZA[data-size="medium"][data-no-visuals]{color:var(--fgColor-muted,var(--color-fg-muted,#656d76));}/*!sc*/ .jxTzTd{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;padding-left:8px;gap:8px;}/*!sc*/ .gqqBXN{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;gap:8px;}/*!sc*/ @media screen and (max-width:543px){.gqqBXN{display:none;}}/*!sc*/ .dzXgxt{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}/*!sc*/ @media screen and (max-width:1011px){.dzXgxt{display:none;}}/*!sc*/ .iWFGlI{margin-left:8px;margin-right:8px;margin:0;}/*!sc*/ .vcvyP{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;min-width:160px;}/*!sc*/ .YUPas{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}/*!sc*/ @media screen and (min-width:1012px){.YUPas{display:none;}}/*!sc*/ .izFOf{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}/*!sc*/ @media screen and (min-width:544px){.izFOf{display:none;}}/*!sc*/ .vIPPs{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;gap:16px;}/*!sc*/ .fdROMU{width:100%;border-collapse:separate;border-spacing:0;border:1px solid;border-color:var(--borderColor-default,var(--color-border-default,#d0d7de));border-radius:6px;table-layout:fixed;overflow:unset;}/*!sc*/ .jGKpsv{height:0px;line-height:0px;}/*!sc*/ .jGKpsv tr{height:0px;font-size:0px;}/*!sc*/ .jdgHnn{padding:16px;color:var(--fgColor-muted,var(--color-fg-muted,#656d76));font-size:12px;text-align:left;height:40px;}/*!sc*/ .jdgHnn th{padding-left:16px;background-color:var(--bgColor-muted,var(--color-canvas-subtle,#f6f8fa));}/*!sc*/ .bQivRW{width:100%;border-top-left-radius:6px;}/*!sc*/ @media screen and (min-width:544px){.bQivRW{display:none;}}/*!sc*/ .ldkMIO{width:40%;border-top-left-radius:6px;}/*!sc*/ @media screen and (max-width:543px){.ldkMIO{display:none;}}/*!sc*/ .jMbWeI{text-align:right;padding-right:16px;width:136px;border-top-right-radius:6px;}/*!sc*/ .gpqjiB{color:var(--fgColor-muted,var(--color-fg-muted,#656d76));font-size:12px;height:40px;}/*!sc*/ .dzCJzi{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;gap:8px;min-width:273px;padding:8px;}/*!sc*/ @media screen and (min-width:544px){.dzCJzi{-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;}}/*!sc*/ .eNCcrz{text-align:center;vertical-align:center;height:40px;border-top:1px solid;border-color:var(--borderColor-default,var(--color-border-default,#d0d7de));}/*!sc*/ .bHTcCe{border-top:1px solid var(--borderColor-default,var(--color-border-default));cursor:pointer;}/*!sc*/ .csrIcr{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;gap:16px;}/*!sc*/ .bUQNHB{border:1px solid;border-color:var(--borderColor-default,var(--color-border-default,#d0d7de));border-radius:6px;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;}/*!sc*/ @media screen and (max-width:543px){.bUQNHB{margin-left:-16px;margin-right:-16px;max-width:calc(100% + 32px);}}/*!sc*/ @media screen and (min-width:544px){.bUQNHB{max-width:100%;}}/*!sc*/ .jPdcfu{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;border-bottom:1px solid;border-bottom-color:var(--borderColor-default,var(--color-border-default,#d0d7de));-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;padding-right:8px;position:-webkit-sticky;position:sticky;top:0;background-color:var(--bgColor-default,var(--color-canvas-default,#ffffff));z-index:1;border-top-left-radius:6px;border-top-right-radius:6px;}/*!sc*/ .iphEWz{-webkit-box-flex:1;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;border-bottom:none;max-width:100%;padding-left:8px;padding-right:8px;}/*!sc*/ .hUCRAk{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-align-items:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;}/*!sc*/ .cwoBXV[data-size="medium"]{color:var(--fgColor-muted,var(--color-fg-subtle,#6e7781));padding-left:8px;padding-right:8px;}/*!sc*/ .QkQOb{padding:32px;overflow:auto;}/*!sc*/ data-styled.g1[id="Box-sc-g0xbh4-0"]{content:"iVEunk,jzuOtQ,bGojzy,iNSVHo,bVgnfw,CEgMp,gMOVLe,gUkoLg,bZBlpz,lhTYNA,ffLUq,bmcJak,fLXEGX,lmSMZJ,dqfxud,fGwBZA,jxTzTd,gqqBXN,dzXgxt,iWFGlI,vcvyP,YUPas,izFOf,vIPPs,fdROMU,jGKpsv,jdgHnn,bQivRW,ldkMIO,jMbWeI,gpqjiB,dzCJzi,eNCcrz,bHTcCe,csrIcr,bUQNHB,jPdcfu,iphEWz,hUCRAk,cwoBXV,QkQOb,"}/*!sc*/ .brGdpi{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;-webkit-clip:rect(0,0,0,0);clip:rect(0,0,0,0);white-space:nowrap;border-width:0;}/*!sc*/ data-styled.g6[id="_VisuallyHidden__VisuallyHidden-sc-11jhm7a-0"]{content:"brGdpi,"}/*!sc*/ .hWlpPn{position:relative;display:inline-block;}/*!sc*/ .hWlpPn::after{position:absolute;z-index:1000000;display:none;padding:0.5em 0.75em;font:normal normal 11px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";-webkit-font-smoothing:subpixel-antialiased;color:var(--tooltip-fgColor,var(--fgColor-onEmphasis,var(--color-fg-on-emphasis,#ffffff)));text-align:center;-webkit-text-decoration:none;text-decoration:none;text-shadow:none;text-transform:none;-webkit-letter-spacing:normal;-moz-letter-spacing:normal;-ms-letter-spacing:normal;letter-spacing:normal;word-wrap:break-word;white-space:pre;pointer-events:none;content:attr(aria-label);background:var(--tooltip-bgColor,var(--bgColor-emphasis,var(--color-neutral-emphasis-plus,#24292f)));border-radius:6px;opacity:0;}/*!sc*/ @-webkit-keyframes tooltip-appear{from{opacity:0;}to{opacity:1;}}/*!sc*/ @keyframes tooltip-appear{from{opacity:0;}to{opacity:1;}}/*!sc*/ .hWlpPn:hover::after,.hWlpPn:active::after,.hWlpPn:focus::after,.hWlpPn:focus-within::after{display:inline-block;-webkit-text-decoration:none;text-decoration:none;-webkit-animation-name:tooltip-appear;animation-name:tooltip-appear;-webkit-animation-duration:0.1s;animation-duration:0.1s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in;-webkit-animation-delay:0s;animation-delay:0s;}/*!sc*/ .hWlpPn.tooltipped-no-delay:hover::after,.hWlpPn.tooltipped-no-delay:active::after,.hWlpPn.tooltipped-no-delay:focus::after,.hWlpPn.tooltipped-no-delay:focus-within::after{-webkit-animation-delay:0s;animation-delay:0s;}/*!sc*/ .hWlpPn.tooltipped-multiline:hover::after,.hWlpPn.tooltipped-multiline:active::after,.hWlpPn.tooltipped-multiline:focus::after,.hWlpPn.tooltipped-multiline:focus-within::after{display:table-cell;}/*!sc*/ .hWlpPn.tooltipped-s::after,.hWlpPn.tooltipped-se::after,.hWlpPn.tooltipped-sw::after{top:100%;right:50%;margin-top:6px;}/*!sc*/ .hWlpPn.tooltipped-se::after{right:auto;left:50%;margin-left:-16px;}/*!sc*/ .hWlpPn.tooltipped-sw::after{margin-right:-16px;}/*!sc*/ .hWlpPn.tooltipped-n::after,.hWlpPn.tooltipped-ne::after,.hWlpPn.tooltipped-nw::after{right:50%;bottom:100%;margin-bottom:6px;}/*!sc*/ .hWlpPn.tooltipped-ne::after{right:auto;left:50%;margin-left:-16px;}/*!sc*/ .hWlpPn.tooltipped-nw::after{margin-right:-16px;}/*!sc*/ .hWlpPn.tooltipped-s::after,.hWlpPn.tooltipped-n::after{-webkit-transform:translateX(50%);-ms-transform:translateX(50%);transform:translateX(50%);}/*!sc*/ .hWlpPn.tooltipped-w::after{right:100%;bottom:50%;margin-right:6px;-webkit-transform:translateY(50%);-ms-transform:translateY(50%);transform:translateY(50%);}/*!sc*/ .hWlpPn.tooltipped-e::after{bottom:50%;left:100%;margin-left:6px;-webkit-transform:translateY(50%);-ms-transform:translateY(50%);transform:translateY(50%);}/*!sc*/ .hWlpPn.tooltipped-multiline::after{width:-webkit-max-content;width:-moz-max-content;width:max-content;max-width:250px;word-wrap:break-word;white-space:pre-line;border-collapse:separate;}/*!sc*/ .hWlpPn.tooltipped-multiline.tooltipped-s::after,.hWlpPn.tooltipped-multiline.tooltipped-n::after{right:auto;left:50%;-webkit-transform:translateX(-50%);-ms-transform:translateX(-50%);transform:translateX(-50%);}/*!sc*/ .hWlpPn.tooltipped-multiline.tooltipped-w::after,.hWlpPn.tooltipped-multiline.tooltipped-e::after{right:100%;}/*!sc*/ .hWlpPn.tooltipped-align-right-2::after{right:0;margin-right:0;}/*!sc*/ .hWlpPn.tooltipped-align-left-2::after{left:0;margin-left:0;}/*!sc*/ data-styled.g17[id="Tooltip__TooltipBase-sc-17tf59c-0"]{content:"hWlpPn,"}/*!sc*/ .liVpTx{display:inline-block;overflow:hidden;text-overflow:ellipsis;vertical-align:top;white-space:nowrap;max-width:125px;}/*!sc*/ data-styled.g19[id="Truncate__StyledTruncate-sc-23o1d2-0"]{content:"liVpTx,"}/*!sc*/ </style> <!-- --> <!-- --> <div class="Box-sc-g0xbh4-0 iVEunk"><div class="Box-sc-g0xbh4-0 jzuOtQ"><div class="Box-sc-g0xbh4-0 bGojzy"></div></div><div class="Box-sc-g0xbh4-0 iNSVHo"><div class="Box-sc-g0xbh4-0 bVgnfw"><div class="Box-sc-g0xbh4-0 CEgMp"><button type="button" aria-haspopup="true" aria-expanded="false" tabindex="0" aria-label="master branch" data-testid="anchor-button" class="Box-sc-g0xbh4-0 gMOVLe prc-Button-ButtonBase-c50BI overview-ref-selector width-full" data-loading="false" data-size="medium" data-variant="default" aria-describedby="branch-picker-repos-header-ref-selector-loading-announcement" id="branch-picker-repos-header-ref-selector"><span data-component="buttonContent" class="Box-sc-g0xbh4-0 gUkoLg prc-Button-ButtonContent-HKbr-"><span data-component="text" class="prc-Button-Label-pTQ3x"><div class="Box-sc-g0xbh4-0 bZBlpz"><div class="Box-sc-g0xbh4-0 lhTYNA"><svg aria-hidden="true" focusable="false" class="octicon octicon-git-branch" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M9.5 3.25a2.25 2.25 0 1 1 3 2.122V6A2.5 2.5 0 0 1 10 8.5H6a1 1 0 0 0-1 1v1.128a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0v1.836A2.493 2.493 0 0 1 6 7h4a1 1 0 0 0 1-1v-.628A2.25 2.25 0 0 1 9.5 3.25Zm-6 0a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Zm8.25-.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM4.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z"></path></svg></div><div class="Box-sc-g0xbh4-0 ffLUq ref-selector-button-text-container"><span class="Box-sc-g0xbh4-0 bmcJak prc-Text-Text-0ima0"> <!-- -->master</span></div></div></span><span data-component="trailingVisual" class="prc-Button-Visual-2epfX prc-Button-VisualWrap-Db-eB"><svg aria-hidden="true" focusable="false" class="octicon octicon-triangle-down" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m4.427 7.427 3.396 3.396a.25.25 0 0 0 .354 0l3.396-3.396A.25.25 0 0 0 11.396 7H4.604a.25.25 0 0 0-.177.427Z"></path></svg></span></span></button><button hidden="" data-hotkey-scope="read-only-cursor-text-area"></button></div><div class="Box-sc-g0xbh4-0 fLXEGX"><a style="--button-color:fg.muted" type="button" href="/RichardKnop/machinery/branches" class="Box-sc-g0xbh4-0 lmSMZJ prc-Button-ButtonBase-c50BI" data-loading="false" data-size="medium" data-variant="invisible" aria-describedby=":Rclab:-loading-announcement"><span data-component="buttonContent" class="Box-sc-g0xbh4-0 gUkoLg prc-Button-ButtonContent-HKbr-"><span data-component="leadingVisual" class="prc-Button-Visual-2epfX prc-Button-VisualWrap-Db-eB"><svg aria-hidden="true" focusable="false" class="octicon octicon-git-branch" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M9.5 3.25a2.25 2.25 0 1 1 3 2.122V6A2.5 2.5 0 0 1 10 8.5H6a1 1 0 0 0-1 1v1.128a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0v1.836A2.493 2.493 0 0 1 6 7h4a1 1 0 0 0 1-1v-.628A2.25 2.25 0 0 1 9.5 3.25Zm-6 0a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Zm8.25-.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM4.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z"></path></svg></span><span data-component="text" class="prc-Button-Label-pTQ3x">Branches</span></span></a><a style="--button-color:fg.muted" type="button" href="/RichardKnop/machinery/tags" class="Box-sc-g0xbh4-0 lmSMZJ prc-Button-ButtonBase-c50BI" data-loading="false" data-size="medium" data-variant="invisible" aria-describedby=":Rklab:-loading-announcement"><span data-component="buttonContent" class="Box-sc-g0xbh4-0 gUkoLg prc-Button-ButtonContent-HKbr-"><span data-component="leadingVisual" class="prc-Button-Visual-2epfX prc-Button-VisualWrap-Db-eB"><svg aria-hidden="true" focusable="false" class="octicon octicon-tag" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1 7.775V2.75C1 1.784 1.784 1 2.75 1h5.025c.464 0 .91.184 1.238.513l6.25 6.25a1.75 1.75 0 0 1 0 2.474l-5.026 5.026a1.75 1.75 0 0 1-2.474 0l-6.25-6.25A1.752 1.752 0 0 1 1 7.775Zm1.5 0c0 .066.026.13.073.177l6.25 6.25a.25.25 0 0 0 .354 0l5.025-5.025a.25.25 0 0 0 0-.354l-6.25-6.25a.25.25 0 0 0-.177-.073H2.75a.25.25 0 0 0-.25.25ZM6 5a1 1 0 1 1 0 2 1 1 0 0 1 0-2Z"></path></svg></span><span data-component="text" class="prc-Button-Label-pTQ3x">Tags</span></span></a></div><div class="Box-sc-g0xbh4-0 dqfxud"><a style="--button-color:fg.muted" type="button" aria-label="Go to Branches page" href="/RichardKnop/machinery/branches" class="Box-sc-g0xbh4-0 fGwBZA prc-Button-ButtonBase-c50BI" data-loading="false" data-no-visuals="true" data-size="medium" data-variant="invisible" aria-describedby=":Relab:-loading-announcement"><svg aria-hidden="true" focusable="false" class="octicon octicon-git-branch" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M9.5 3.25a2.25 2.25 0 1 1 3 2.122V6A2.5 2.5 0 0 1 10 8.5H6a1 1 0 0 0-1 1v1.128a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0v1.836A2.493 2.493 0 0 1 6 7h4a1 1 0 0 0 1-1v-.628A2.25 2.25 0 0 1 9.5 3.25Zm-6 0a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Zm8.25-.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM4.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z"></path></svg></a><a style="--button-color:fg.muted" type="button" aria-label="Go to Tags page" href="/RichardKnop/machinery/tags" class="Box-sc-g0xbh4-0 fGwBZA prc-Button-ButtonBase-c50BI" data-loading="false" data-no-visuals="true" data-size="medium" data-variant="invisible" aria-describedby=":Rmlab:-loading-announcement"><svg aria-hidden="true" focusable="false" class="octicon octicon-tag" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1 7.775V2.75C1 1.784 1.784 1 2.75 1h5.025c.464 0 .91.184 1.238.513l6.25 6.25a1.75 1.75 0 0 1 0 2.474l-5.026 5.026a1.75 1.75 0 0 1-2.474 0l-6.25-6.25A1.752 1.752 0 0 1 1 7.775Zm1.5 0c0 .066.026.13.073.177l6.25 6.25a.25.25 0 0 0 .354 0l5.025-5.025a.25.25 0 0 0 0-.354l-6.25-6.25a.25.25 0 0 0-.177-.073H2.75a.25.25 0 0 0-.25.25ZM6 5a1 1 0 1 1 0 2 1 1 0 0 1 0-2Z"></path></svg></a></div></div><div class="Box-sc-g0xbh4-0 jxTzTd"><div class="Box-sc-g0xbh4-0 gqqBXN"><div class="Box-sc-g0xbh4-0 dzXgxt"><!--$--><div class="Box-sc-g0xbh4-0 iWFGlI"><span class="Box-sc-g0xbh4-0 vcvyP TextInput-wrapper prc-components-TextInputWrapper-i1ofR prc-components-TextInputBaseWrapper-ueK9q" data-leading-visual="true" data-trailing-visual="true" aria-busy="false"><span class="TextInput-icon" id=":R2j5ab:" aria-hidden="true"><svg aria-hidden="true" focusable="false" class="octicon octicon-search" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M10.68 11.74a6 6 0 0 1-7.922-8.982 6 6 0 0 1 8.982 7.922l3.04 3.04a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215ZM11.5 7a4.499 4.499 0 1 0-8.997 0A4.499 4.499 0 0 0 11.5 7Z"></path></svg></span><input type="text" aria-label="Go to file" role="combobox" aria-controls="file-results-list" aria-expanded="false" aria-haspopup="dialog" autoCorrect="off" spellcheck="false" placeholder="Go to file" aria-describedby=":R2j5ab: :R2j5abH1:" data-component="input" class="prc-components-Input-Ic-y8" value=""/><span class="TextInput-icon" id=":R2j5abH1:" aria-hidden="true"></span></span></div><!--/$--></div><div class="Box-sc-g0xbh4-0 YUPas"><button type="button" class="prc-Button-ButtonBase-c50BI" data-loading="false" data-no-visuals="true" data-size="medium" data-variant="default" aria-describedby=":Rr5ab:-loading-announcement"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-HKbr-"><span data-component="text" class="prc-Button-Label-pTQ3x">Go to file</span></span></button></div><div class="react-directory-add-file-icon"></div><div class="react-directory-remove-file-icon"></div></div><button type="button" aria-haspopup="true" aria-expanded="false" tabindex="0" class="prc-Button-ButtonBase-c50BI" data-loading="false" data-size="medium" data-variant="primary" aria-describedby=":R55ab:-loading-announcement" id=":R55ab:"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-HKbr-"><span data-component="leadingVisual" class="prc-Button-Visual-2epfX prc-Button-VisualWrap-Db-eB"><svg aria-hidden="true" focusable="false" class="octicon octicon-code hide-sm" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m11.28 3.22 4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L13.94 8l-3.72-3.72a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215Zm-6.56 0a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042L2.06 8l3.72 3.72a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L.47 8.53a.75.75 0 0 1 0-1.06Z"></path></svg></span><span data-component="text" class="prc-Button-Label-pTQ3x">Code</span><span data-component="trailingVisual" class="prc-Button-Visual-2epfX prc-Button-VisualWrap-Db-eB"><svg aria-hidden="true" focusable="false" class="octicon octicon-triangle-down" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m4.427 7.427 3.396 3.396a.25.25 0 0 0 .354 0l3.396-3.396A.25.25 0 0 0 11.396 7H4.604a.25.25 0 0 0-.177.427Z"></path></svg></span></span></button><div class="Box-sc-g0xbh4-0 izFOf"><button data-component="IconButton" type="button" aria-label="Open more actions menu" aria-haspopup="true" aria-expanded="false" tabindex="0" class="prc-Button-ButtonBase-c50BI prc-Button-IconButton-szpyj" data-loading="false" data-no-visuals="true" data-size="medium" data-variant="default" aria-describedby=":R75ab:-loading-announcement" id=":R75ab:"><svg aria-hidden="true" focusable="false" class="octicon octicon-kebab-horizontal" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M8 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM1.5 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Zm13 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"></path></svg></button></div></div></div><div class="Box-sc-g0xbh4-0 vIPPs"><div data-hpc="true"><button hidden="" data-testid="focus-next-element-button" data-hotkey="j"></button><button hidden="" data-testid="focus-previous-element-button" data-hotkey="k"></button><h2 class="sr-only ScreenReaderHeading-module__userSelectNone--vW4Cq prc-Heading-Heading-6CmGO" data-testid="screen-reader-heading" id="folders-and-files">Folders and files</h2><table aria-labelledby="folders-and-files" class="Box-sc-g0xbh4-0 fdROMU"><thead class="Box-sc-g0xbh4-0 jGKpsv"><tr class="Box-sc-g0xbh4-0 jdgHnn"><th colSpan="2" class="Box-sc-g0xbh4-0 bQivRW"><span class="text-bold">Name</span></th><th colSpan="1" class="Box-sc-g0xbh4-0 ldkMIO"><span class="text-bold">Name</span></th><th class="hide-sm"><div title="Last commit message" class="Truncate__StyledTruncate-sc-23o1d2-0 liVpTx width-fit"><span class="text-bold">Last commit message</span></div></th><th colSpan="1" class="Box-sc-g0xbh4-0 jMbWeI"><div title="Last commit date" class="Truncate__StyledTruncate-sc-23o1d2-0 liVpTx width-fit"><span class="text-bold">Last commit date</span></div></th></tr></thead><tbody><tr class="Box-sc-g0xbh4-0 gpqjiB"><td colSpan="3" class="bgColor-muted p-1 rounded-top-2"><div class="Box-sc-g0xbh4-0 dzCJzi"><h2 class="sr-only ScreenReaderHeading-module__userSelectNone--vW4Cq prc-Heading-Heading-6CmGO" data-testid="screen-reader-heading">Latest commit</h2><div style="width:120px" class="Skeleton Skeleton--text" data-testid="loading"> </div><div class="d-flex flex-shrink-0 gap-2"><div data-testid="latest-commit-details" class="d-none d-sm-flex flex-items-center"></div><div class="d-flex gap-2"><h2 class="sr-only ScreenReaderHeading-module__userSelectNone--vW4Cq prc-Heading-Heading-6CmGO" data-testid="screen-reader-heading">History</h2><a href="/RichardKnop/machinery/commits/master/" class="prc-Button-ButtonBase-c50BI d-none d-lg-flex LinkButton-module__code-view-link-button--xvCGA flex-items-center fgColor-default" data-loading="false" data-size="small" data-variant="invisible" aria-describedby=":Raqj8pab:-loading-announcement"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-HKbr-"><span data-component="leadingVisual" class="prc-Button-Visual-2epfX prc-Button-VisualWrap-Db-eB"><svg aria-hidden="true" focusable="false" class="octicon octicon-history" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m.427 1.927 1.215 1.215a8.002 8.002 0 1 1-1.6 5.685.75.75 0 1 1 1.493-.154 6.5 6.5 0 1 0 1.18-4.458l1.358 1.358A.25.25 0 0 1 3.896 6H.25A.25.25 0 0 1 0 5.75V2.104a.25.25 0 0 1 .427-.177ZM7.75 4a.75.75 0 0 1 .75.75v2.992l2.028.812a.75.75 0 0 1-.557 1.392l-2.5-1A.751.751 0 0 1 7 8.25v-3.5A.75.75 0 0 1 7.75 4Z"></path></svg></span><span data-component="text" class="prc-Button-Label-pTQ3x"><span class="fgColor-default">1,063 Commits</span></span></span></a><div class="d-sm-none"></div><div class="d-flex d-lg-none"><span role="tooltip" aria-label="1,063 Commits" id="history-icon-button-tooltip" class="Tooltip__TooltipBase-sc-17tf59c-0 hWlpPn tooltipped-n"><a href="/RichardKnop/machinery/commits/master/" class="prc-Button-ButtonBase-c50BI LinkButton-module__code-view-link-button--xvCGA flex-items-center fgColor-default" data-loading="false" data-size="small" data-variant="invisible" aria-describedby=":R1iqj8pab:-loading-announcement history-icon-button-tooltip"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-HKbr-"><span data-component="leadingVisual" class="prc-Button-Visual-2epfX prc-Button-VisualWrap-Db-eB"><svg aria-hidden="true" focusable="false" class="octicon octicon-history" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m.427 1.927 1.215 1.215a8.002 8.002 0 1 1-1.6 5.685.75.75 0 1 1 1.493-.154 6.5 6.5 0 1 0 1.18-4.458l1.358 1.358A.25.25 0 0 1 3.896 6H.25A.25.25 0 0 1 0 5.75V2.104a.25.25 0 0 1 .427-.177ZM7.75 4a.75.75 0 0 1 .75.75v2.992l2.028.812a.75.75 0 0 1-.557 1.392l-2.5-1A.751.751 0 0 1 7 8.25v-3.5A.75.75 0 0 1 7.75 4Z"></path></svg></span></span></a></span></div></div></div></div></td></tr><tr class="react-directory-row undefined" id="folder-row-0"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="example" aria-label="example, (Directory)" class="Link--primary" href="/RichardKnop/machinery/tree/master/example">example</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="example" aria-label="example, (Directory)" class="Link--primary" href="/RichardKnop/machinery/tree/master/example">example</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row undefined" id="folder-row-1"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="instruction-notes" aria-label="instruction-notes, (Directory)" class="Link--primary" href="/RichardKnop/machinery/tree/master/instruction-notes">instruction-notes</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="instruction-notes" aria-label="instruction-notes, (Directory)" class="Link--primary" href="/RichardKnop/machinery/tree/master/instruction-notes">instruction-notes</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row undefined" id="folder-row-2"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="integration-tests" aria-label="integration-tests, (Directory)" class="Link--primary" href="/RichardKnop/machinery/tree/master/integration-tests">integration-tests</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="integration-tests" aria-label="integration-tests, (Directory)" class="Link--primary" href="/RichardKnop/machinery/tree/master/integration-tests">integration-tests</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row undefined" id="folder-row-3"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="v1" aria-label="v1, (Directory)" class="Link--primary" href="/RichardKnop/machinery/tree/master/v1">v1</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="v1" aria-label="v1, (Directory)" class="Link--primary" href="/RichardKnop/machinery/tree/master/v1">v1</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row undefined" id="folder-row-4"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="v2" aria-label="v2, (Directory)" class="Link--primary" href="/RichardKnop/machinery/tree/master/v2">v2</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file-directory-fill icon-directory" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1A1.75 1.75 0 0 0 0 2.75v10.5C0 14.216.784 15 1.75 15h12.5A1.75 1.75 0 0 0 16 13.25v-8.5A1.75 1.75 0 0 0 14.25 3H7.5a.25.25 0 0 1-.2-.1l-.9-1.2C6.07 1.26 5.55 1 5 1H1.75Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="v2" aria-label="v2, (Directory)" class="Link--primary" href="/RichardKnop/machinery/tree/master/v2">v2</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row undefined" id="folder-row-5"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title=".deepsource.toml" aria-label=".deepsource.toml, (File)" class="Link--primary" href="/RichardKnop/machinery/blob/master/.deepsource.toml">.deepsource.toml</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title=".deepsource.toml" aria-label=".deepsource.toml, (File)" class="Link--primary" href="/RichardKnop/machinery/blob/master/.deepsource.toml">.deepsource.toml</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row undefined" id="folder-row-6"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title=".gitignore" aria-label=".gitignore, (File)" class="Link--primary" href="/RichardKnop/machinery/blob/master/.gitignore">.gitignore</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title=".gitignore" aria-label=".gitignore, (File)" class="Link--primary" href="/RichardKnop/machinery/blob/master/.gitignore">.gitignore</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row undefined" id="folder-row-7"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title=".travis.yml" aria-label=".travis.yml, (File)" class="Link--primary" href="/RichardKnop/machinery/blob/master/.travis.yml">.travis.yml</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title=".travis.yml" aria-label=".travis.yml, (File)" class="Link--primary" href="/RichardKnop/machinery/blob/master/.travis.yml">.travis.yml</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row undefined" id="folder-row-8"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="Dockerfile.gcppubsub" aria-label="Dockerfile.gcppubsub, (File)" class="Link--primary" href="/RichardKnop/machinery/blob/master/Dockerfile.gcppubsub">Dockerfile.gcppubsub</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="Dockerfile.gcppubsub" aria-label="Dockerfile.gcppubsub, (File)" class="Link--primary" href="/RichardKnop/machinery/blob/master/Dockerfile.gcppubsub">Dockerfile.gcppubsub</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row undefined" id="folder-row-9"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="Dockerfile.test" aria-label="Dockerfile.test, (File)" class="Link--primary" href="/RichardKnop/machinery/blob/master/Dockerfile.test">Dockerfile.test</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="Dockerfile.test" aria-label="Dockerfile.test, (File)" class="Link--primary" href="/RichardKnop/machinery/blob/master/Dockerfile.test">Dockerfile.test</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row truncate-for-mobile" id="folder-row-10"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="LICENSE" aria-label="LICENSE, (File)" class="Link--primary" href="/RichardKnop/machinery/blob/master/LICENSE">LICENSE</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="LICENSE" aria-label="LICENSE, (File)" class="Link--primary" href="/RichardKnop/machinery/blob/master/LICENSE">LICENSE</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row truncate-for-mobile" id="folder-row-11"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="Makefile" aria-label="Makefile, (File)" class="Link--primary" href="/RichardKnop/machinery/blob/master/Makefile">Makefile</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="Makefile" aria-label="Makefile, (File)" class="Link--primary" href="/RichardKnop/machinery/blob/master/Makefile">Makefile</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row truncate-for-mobile" id="folder-row-12"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="README.md" aria-label="README.md, (File)" class="Link--primary" href="/RichardKnop/machinery/blob/master/README.md">README.md</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="README.md" aria-label="README.md, (File)" class="Link--primary" href="/RichardKnop/machinery/blob/master/README.md">README.md</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row truncate-for-mobile" id="folder-row-13"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="docker-compose.test.yml" aria-label="docker-compose.test.yml, (File)" class="Link--primary" href="/RichardKnop/machinery/blob/master/docker-compose.test.yml">docker-compose.test.yml</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="docker-compose.test.yml" aria-label="docker-compose.test.yml, (File)" class="Link--primary" href="/RichardKnop/machinery/blob/master/docker-compose.test.yml">docker-compose.test.yml</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row truncate-for-mobile" id="folder-row-14"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="go.mod" aria-label="go.mod, (File)" class="Link--primary" href="/RichardKnop/machinery/blob/master/go.mod">go.mod</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="go.mod" aria-label="go.mod, (File)" class="Link--primary" href="/RichardKnop/machinery/blob/master/go.mod">go.mod</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row truncate-for-mobile" id="folder-row-15"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="go.sum" aria-label="go.sum, (File)" class="Link--primary" href="/RichardKnop/machinery/blob/master/go.sum">go.sum</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="go.sum" aria-label="go.sum, (File)" class="Link--primary" href="/RichardKnop/machinery/blob/master/go.sum">go.sum</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="react-directory-row truncate-for-mobile" id="folder-row-16"><td class="react-directory-row-name-cell-small-screen" colSpan="2"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="wait-for-it.sh" aria-label="wait-for-it.sh, (File)" class="Link--primary" href="/RichardKnop/machinery/blob/master/wait-for-it.sh">wait-for-it.sh</a></div></div></div></div></td><td class="react-directory-row-name-cell-large-screen" colSpan="1"><div class="react-directory-filename-column"><svg aria-hidden="true" focusable="false" class="octicon octicon-file color-fg-muted" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2 1.75C2 .784 2.784 0 3.75 0h6.586c.464 0 .909.184 1.237.513l2.914 2.914c.329.328.513.773.513 1.237v9.586A1.75 1.75 0 0 1 13.25 16h-9.5A1.75 1.75 0 0 1 2 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h9.5a.25.25 0 0 0 .25-.25V6h-2.75A1.75 1.75 0 0 1 9 4.25V1.5Zm6.75.062V4.25c0 .138.112.25.25.25h2.688l-.011-.013-2.914-2.914-.013-.011Z"></path></svg><div class="overflow-hidden"><div class="react-directory-filename-cell"><div class="react-directory-truncate"><a title="wait-for-it.sh" aria-label="wait-for-it.sh, (File)" class="Link--primary" href="/RichardKnop/machinery/blob/master/wait-for-it.sh">wait-for-it.sh</a></div></div></div></div></td><td class="react-directory-row-commit-cell"><div class="Skeleton Skeleton--text"> </div></td><td><div class="Skeleton Skeleton--text"> </div></td></tr><tr class="Box-sc-g0xbh4-0 eNCcrz show-for-mobile" data-testid="view-all-files-row"><td colSpan="3" class="Box-sc-g0xbh4-0 bHTcCe"><div><button class="prc-Link-Link-85e08">View all files</button></div></td></tr></tbody></table></div><div class="Box-sc-g0xbh4-0 csrIcr"><div class="Box-sc-g0xbh4-0 bUQNHB"><div itemscope="" itemType="https://schema.org/abstract" class="Box-sc-g0xbh4-0 jPdcfu"><h2 class="_VisuallyHidden__VisuallyHidden-sc-11jhm7a-0 brGdpi">Repository files navigation</h2><nav class="Box-sc-g0xbh4-0 iphEWz prc-components-UnderlineWrapper-oOh5J" aria-label="Repository files"><ul class="prc-components-UnderlineItemList-b23Hf" role="list"><li class="Box-sc-g0xbh4-0 hUCRAk"><a class="prc-components-UnderlineItem-lJsg-" href="#" aria-current="page"><span data-component="icon"><svg aria-hidden="true" focusable="false" class="octicon octicon-book" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M0 1.75A.75.75 0 0 1 .75 1h4.253c1.227 0 2.317.59 3 1.501A3.743 3.743 0 0 1 11.006 1h4.245a.75.75 0 0 1 .75.75v10.5a.75.75 0 0 1-.75.75h-4.507a2.25 2.25 0 0 0-1.591.659l-.622.621a.75.75 0 0 1-1.06 0l-.622-.621A2.25 2.25 0 0 0 5.258 13H.75a.75.75 0 0 1-.75-.75Zm7.251 10.324.004-5.073-.002-2.253A2.25 2.25 0 0 0 5.003 2.5H1.5v9h3.757a3.75 3.75 0 0 1 1.994.574ZM8.755 4.75l-.004 7.322a3.752 3.752 0 0 1 1.992-.572H14.5v-9h-3.495a2.25 2.25 0 0 0-2.25 2.25Z"></path></svg></span><span data-component="text" data-content="README">README</span></a></li><li class="Box-sc-g0xbh4-0 hUCRAk"><a class="prc-components-UnderlineItem-lJsg-" href="#"><span data-component="icon"><svg aria-hidden="true" focusable="false" class="octicon octicon-law" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M8.75.75V2h.985c.304 0 .603.08.867.231l1.29.736c.038.022.08.033.124.033h2.234a.75.75 0 0 1 0 1.5h-.427l2.111 4.692a.75.75 0 0 1-.154.838l-.53-.53.529.531-.001.002-.002.002-.006.006-.006.005-.01.01-.045.04c-.21.176-.441.327-.686.45C14.556 10.78 13.88 11 13 11a4.498 4.498 0 0 1-2.023-.454 3.544 3.544 0 0 1-.686-.45l-.045-.04-.016-.015-.006-.006-.004-.004v-.001a.75.75 0 0 1-.154-.838L12.178 4.5h-.162c-.305 0-.604-.079-.868-.231l-1.29-.736a.245.245 0 0 0-.124-.033H8.75V13h2.5a.75.75 0 0 1 0 1.5h-6.5a.75.75 0 0 1 0-1.5h2.5V3.5h-.984a.245.245 0 0 0-.124.033l-1.289.737c-.265.15-.564.23-.869.23h-.162l2.112 4.692a.75.75 0 0 1-.154.838l-.53-.53.529.531-.001.002-.002.002-.006.006-.016.015-.045.04c-.21.176-.441.327-.686.45C4.556 10.78 3.88 11 3 11a4.498 4.498 0 0 1-2.023-.454 3.544 3.544 0 0 1-.686-.45l-.045-.04-.016-.015-.006-.006-.004-.004v-.001a.75.75 0 0 1-.154-.838L2.178 4.5H1.75a.75.75 0 0 1 0-1.5h2.234a.249.249 0 0 0 .125-.033l1.288-.737c.265-.15.564-.23.869-.23h.984V.75a.75.75 0 0 1 1.5 0Zm2.945 8.477c.285.135.718.273 1.305.273s1.02-.138 1.305-.273L13 6.327Zm-10 0c.285.135.718.273 1.305.273s1.02-.138 1.305-.273L3 6.327Z"></path></svg></span><span data-component="text" data-content="MPL-2.0 license">MPL-2.0 license</span></a></li></ul></nav><button style="--button-color:fg.subtle" type="button" aria-label="Outline" aria-haspopup="true" aria-expanded="false" tabindex="0" class="Box-sc-g0xbh4-0 cwoBXV prc-Button-ButtonBase-c50BI" data-loading="false" data-size="medium" data-variant="invisible" aria-describedby=":Rr9ab:-loading-announcement" id=":Rr9ab:"><svg aria-hidden="true" focusable="false" class="octicon octicon-list-unordered" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M5.75 2.5h8.5a.75.75 0 0 1 0 1.5h-8.5a.75.75 0 0 1 0-1.5Zm0 5h8.5a.75.75 0 0 1 0 1.5h-8.5a.75.75 0 0 1 0-1.5Zm0 5h8.5a.75.75 0 0 1 0 1.5h-8.5a.75.75 0 0 1 0-1.5ZM2 14a1 1 0 1 1 0-2 1 1 0 0 1 0 2Zm1-6a1 1 0 1 1-2 0 1 1 0 0 1 2 0ZM2 4a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg></button></div><div class="Box-sc-g0xbh4-0 QkQOb js-snippet-clipboard-copy-unpositioned undefined" data-hpc="true"><article class="markdown-body entry-content container-lg" itemprop="text"><div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">Machinery</h2><a id="user-content-machinery" class="anchor" aria-label="Permalink: Machinery" href="#machinery"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">Machinery is an asynchronous task queue/job queue based on distributed message passing.</p> <p dir="auto"><a href="https://travis-ci.org/RichardKnop/machinery" rel="nofollow"><img src="https://camo.githubusercontent.com/40b0c030de54012daf268a5c2ada1027708c41206550ec7c64ca1aed732eca5b/68747470733a2f2f7472617669732d63692e6f72672f526963686172644b6e6f702f6d616368696e6572792e7376673f6272616e63683d6d6173746572266c6162656c3d6c696e75782b6275696c64" alt="Travis Status for RichardKnop/machinery" data-canonical-src="https://travis-ci.org/RichardKnop/machinery.svg?branch=master&label=linux+build" style="max-width: 100%;"></a> <a href="http://godoc.org/github.com/RichardKnop/machinery/v1" rel="nofollow"><img src="https://camo.githubusercontent.com/ba8e1fdfc10c2e72e21e374db1726077ffce854e7506ae647f981fa384a4f2c3/68747470733a2f2f676f646f632e6f72672f6769746875622e636f6d2f6e617468616e792f6c6f6f7065723f7374617475732e737667" alt="godoc for RichardKnop/machinery" data-canonical-src="https://godoc.org/github.com/nathany/looper?status.svg" style="max-width: 100%;"></a> <a href="https://codecov.io/gh/RichardKnop/machinery" rel="nofollow"><img src="https://camo.githubusercontent.com/74e2b5bb7ee0fa2e7684d5003a119e5105e05de749d217f7a4dc57f0dc5c92e3/68747470733a2f2f636f6465636f762e696f2f67682f526963686172644b6e6f702f6d616368696e6572792f6272616e63682f6d61737465722f67726170682f62616467652e737667" alt="codecov for RichardKnop/machinery" data-canonical-src="https://codecov.io/gh/RichardKnop/machinery/branch/master/graph/badge.svg" style="max-width: 100%;"></a></p> <p dir="auto"><a href="https://goreportcard.com/report/github.com/RichardKnop/machinery" rel="nofollow"><img src="https://camo.githubusercontent.com/fe06941ff762bfeaa21a01a4b7205a176363bb8ed6f404d96897631f82d04227/68747470733a2f2f676f7265706f7274636172642e636f6d2f62616467652f6769746875622e636f6d2f526963686172644b6e6f702f6d616368696e657279" alt="Go Report Card" data-canonical-src="https://goreportcard.com/badge/github.com/RichardKnop/machinery" style="max-width: 100%;"></a> <a href="https://golangci.com" rel="nofollow"><img src="https://camo.githubusercontent.com/324eed5ff78053caa5b7b25c59540c253f2824585dd6bbe7e7f2e2f64d223136/68747470733a2f2f676f6c616e6763692e636f6d2f6261646765732f6769746875622e636f6d2f526963686172644b6e6f702f6d616368696e6572792e737667" alt="GolangCI" data-canonical-src="https://golangci.com/badges/github.com/RichardKnop/machinery.svg" style="max-width: 100%;"></a> <a href="http://opentracing.io" rel="nofollow"><img src="https://camo.githubusercontent.com/ad854b5cd661412d83ad272bdedc460f22784bb13327cfc9a5110d5375130665/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4f70656e54726163696e672d656e61626c65642d626c75652e737667" alt="OpenTracing Badge" data-canonical-src="https://img.shields.io/badge/OpenTracing-enabled-blue.svg" style="max-width: 100%;"></a></p> <p dir="auto"><a href="https://sourcegraph.com/github.com/RichardKnop/machinery?badge" rel="nofollow"><img src="https://camo.githubusercontent.com/25e031010279447153e701ddc01762052b1ea58032103332178f10224cb52f4f/68747470733a2f2f736f7572636567726170682e636f6d2f6769746875622e636f6d2f526963686172644b6e6f702f6d616368696e6572792f2d2f62616467652e737667" alt="Sourcegraph for RichardKnop/machinery" data-canonical-src="https://sourcegraph.com/github.com/RichardKnop/machinery/-/badge.svg" style="max-width: 100%;"></a> <a href="https://richardknop.github.io/donate/" rel="nofollow"><img src="https://camo.githubusercontent.com/6f06e72688e64d1f15fef24e157b92611ce29c54f94fb1ee84784a857d7db920/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f646f6e6174652d626974636f696e2d6f72616e67652e737667" alt="Donate Bitcoin" data-canonical-src="https://img.shields.io/badge/donate-bitcoin-orange.svg" style="max-width: 100%;"></a></p> <hr> <ul dir="auto"> <li><a href="#v2-experiment">V2 Experiment</a></li> <li><a href="#first-steps">First Steps</a></li> <li><a href="#configuration">Configuration</a> <ul dir="auto"> <li><a href="#lock">Lock</a></li> <li><a href="#broker">Broker</a></li> <li><a href="#defaultqueue">DefaultQueue</a></li> <li><a href="#resultbackend">ResultBackend</a></li> <li><a href="#resultsexpirein">ResultsExpireIn</a></li> <li><a href="#amqp-2">AMQP</a></li> <li><a href="#dynamodb">DynamoDB</a></li> <li><a href="#redis-2">Redis</a></li> <li><a href="#gcppubsub">GCPPubSub</a></li> </ul> </li> <li><a href="#custom-logger">Custom Logger</a></li> <li><a href="#server">Server</a></li> <li><a href="#workers">Workers</a></li> <li><a href="#tasks">Tasks</a> <ul dir="auto"> <li><a href="#registering-tasks">Registering Tasks</a></li> <li><a href="#signatures">Signatures</a></li> <li><a href="#supported-types">Supported Types</a></li> <li><a href="#sending-tasks">Sending Tasks</a></li> <li><a href="#delayed-tasks">Delayed Tasks</a></li> <li><a href="#retry-tasks">Retry Tasks</a></li> <li><a href="#get-pending-tasks">Get Pending Tasks</a></li> <li><a href="#keeping-results">Keeping Results</a></li> </ul> </li> <li><a href="#workflows">Workflows</a> <ul dir="auto"> <li><a href="#groups">Groups</a></li> <li><a href="#chords">Chords</a></li> <li><a href="#chains">Chains</a></li> </ul> </li> <li><a href="#periodic-tasks--workflows">Periodic Tasks & Workflows</a> <ul dir="auto"> <li><a href="#periodic-tasks">Periodic Tasks</a></li> <li><a href="#periodic-groups">Periodic Groups</a></li> <li><a href="#periodic-chains">Periodic Chains</a></li> <li><a href="#periodic-chords">Periodic Chords</a></li> </ul> </li> <li><a href="#development">Development</a> <ul dir="auto"> <li><a href="#requirements">Requirements</a></li> <li><a href="#dependencies">Dependencies</a></li> <li><a href="#testing">Testing</a></li> </ul> </li> </ul> <div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">V2</h3><a id="user-content-v2" class="anchor" aria-label="Permalink: V2" href="#v2"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">I recommend using V2 in order to avoid having to import all dependencies for brokers and backends you are not using.</p> <p dir="auto">Instead of factory, you will need to inject broker and backend objects to the server constructor:</p> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import ( "github.com/RichardKnop/machinery/v2" backendsiface "github.com/RichardKnop/machinery/v2/backends/iface" brokersiface "github.com/RichardKnop/machinery/v2/brokers/iface" locksiface "github.com/RichardKnop/machinery/v2/locks/iface" ) var broker brokersiface.Broker var backend backendsiface.Backend var lock locksiface.Lock server := machinery.NewServer(cnf, broker, backend, lock) // server.NewWorker("machinery", 10)"><pre><span class="pl-k">import</span> ( <span class="pl-s">"github.com/RichardKnop/machinery/v2"</span> backendsiface <span class="pl-s">"github.com/RichardKnop/machinery/v2/backends/iface"</span> brokersiface <span class="pl-s">"github.com/RichardKnop/machinery/v2/brokers/iface"</span> locksiface <span class="pl-s">"github.com/RichardKnop/machinery/v2/locks/iface"</span> ) <span class="pl-k">var</span> <span class="pl-s1">broker</span> brokersiface.<span class="pl-smi">Broker</span> <span class="pl-k">var</span> <span class="pl-s1">backend</span> backendsiface.<span class="pl-smi">Backend</span> <span class="pl-k">var</span> <span class="pl-s1">lock</span> locksiface.<span class="pl-smi">Lock</span> <span class="pl-s1">server</span> <span class="pl-c1">:=</span> <span class="pl-s1">machinery</span>.<span class="pl-c1">NewServer</span>(<span class="pl-s1">cnf</span>, <span class="pl-s1">broker</span>, <span class="pl-s1">backend</span>, <span class="pl-s1">lock</span>) <span class="pl-c">// server.NewWorker("machinery", 10)</span></pre></div> <div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">First Steps</h3><a id="user-content-first-steps" class="anchor" aria-label="Permalink: First Steps" href="#first-steps"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">To install recommended v2 release:</p> <div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="go get github.com/RichardKnop/machinery/v2"><pre>go get github.com/RichardKnop/machinery/v2</pre></div> <p dir="auto">If you want to use legacy v1 version, you still can:</p> <div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="go get github.com/RichardKnop/machinery"><pre>go get github.com/RichardKnop/machinery</pre></div> <p dir="auto">First, you will need to define some tasks. Look at sample tasks in <code>v2/example/tasks/tasks.go</code> to see a few examples.</p> <p dir="auto">Second, you will need to launch a worker process with one of these commands (v2 is recommended since it doesn't import dependencies for all brokers / backends, only those you actually need):</p> <div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="cd v2/ go run example/amqp/main.go worker go run example/redigo/main.go worker // Redis with redigo driver go run example/go-redis/main.go worker // Redis with Go Redis driver go run example/amqp/main.go worker go run example/redis/main.go worker"><pre><span class="pl-c1">cd</span> v2/ go run example/amqp/main.go worker go run example/redigo/main.go worker // Redis with redigo driver go run example/go-redis/main.go worker // Redis with Go Redis driver go run example/amqp/main.go worker go run example/redis/main.go worker</pre></div> <p dir="auto"><a target="_blank" rel="noopener noreferrer nofollow" href="https://raw.githubusercontent.com/RichardKnop/assets/master/machinery/example_worker.png"><img src="https://raw.githubusercontent.com/RichardKnop/assets/master/machinery/example_worker.png" alt="Example worker" style="max-width: 100%;"></a></p> <p dir="auto">Finally, once you have a worker running and waiting for tasks to consume, send some tasks with one of these commands (v2 is recommended since it doesn't import dependencies for all brokers / backends, only those you actually need):</p> <div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="cd v2 go run v2/example/amqp/main.go send go run v2/example/redigo/main.go send // Redis with redigo driver go run v2/example/go-redis/main.go send // Redis with Go Redis driver"><pre><span class="pl-c1">cd</span> v2 go run v2/example/amqp/main.go send go run v2/example/redigo/main.go send // Redis with redigo driver go run v2/example/go-redis/main.go send // Redis with Go Redis driver</pre></div> <p dir="auto">You will be able to see the tasks being processed asynchronously by the worker:</p> <p dir="auto"><a target="_blank" rel="noopener noreferrer nofollow" href="https://raw.githubusercontent.com/RichardKnop/assets/master/machinery/example_worker_receives_tasks.png"><img src="https://raw.githubusercontent.com/RichardKnop/assets/master/machinery/example_worker_receives_tasks.png" alt="Example worker receives tasks" style="max-width: 100%;"></a></p> <div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">Configuration</h3><a id="user-content-configuration" class="anchor" aria-label="Permalink: Configuration" href="#configuration"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">The <a href="/RichardKnop/machinery/blob/master/v2/config/config.go">config</a> package has convenience methods for loading configuration from environment variables or a YAML file. For example, load configuration from environment variables:</p> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="cnf, err := config.NewFromEnvironment()"><pre><span class="pl-s1">cnf</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">config</span>.<span class="pl-c1">NewFromEnvironment</span>()</pre></div> <p dir="auto">Or load from YAML file:</p> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="cnf, err := config.NewFromYaml("config.yml", true)"><pre><span class="pl-s1">cnf</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">config</span>.<span class="pl-c1">NewFromYaml</span>(<span class="pl-s">"config.yml"</span>, <span class="pl-c1">true</span>)</pre></div> <p dir="auto">Second boolean flag enables live reloading of configuration every 10 seconds. Use <code>false</code> to disable live reloading.</p> <p dir="auto">Machinery configuration is encapsulated by a <code>Config</code> struct and injected as a dependency to objects that need it.</p> <div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">Lock</h4><a id="user-content-lock" class="anchor" aria-label="Permalink: Lock" href="#lock"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div class="markdown-heading" dir="auto"><h5 tabindex="-1" class="heading-element" dir="auto">Redis</h5><a id="user-content-redis" class="anchor" aria-label="Permalink: Redis" href="#redis"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">Use Redis URL in one of these formats:</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="redis://[password@]host[port][/db_num]"><pre class="notranslate"><code>redis://[password@]host[port][/db_num] </code></pre></div> <p dir="auto">For example:</p> <ol dir="auto"> <li><code>redis://localhost:6379</code>, or with password <code>redis://password@localhost:6379</code></li> </ol> <div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">Broker</h4><a id="user-content-broker" class="anchor" aria-label="Permalink: Broker" href="#broker"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">A message broker. Currently supported brokers are:</p> <div class="markdown-heading" dir="auto"><h5 tabindex="-1" class="heading-element" dir="auto">AMQP</h5><a id="user-content-amqp" class="anchor" aria-label="Permalink: AMQP" href="#amqp"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">Use AMQP URL in the format:</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="amqp://[username:password@]@host[:port]"><pre class="notranslate"><code>amqp://[username:password@]@host[:port] </code></pre></div> <p dir="auto">For example:</p> <ol dir="auto"> <li><code>amqp://guest:guest@localhost:5672</code></li> </ol> <p dir="auto">AMQP also supports multiples brokers urls. You need to specify the URL separator in the <code>MultipleBrokerSeparator</code> field.</p> <div class="markdown-heading" dir="auto"><h5 tabindex="-1" class="heading-element" dir="auto">Redis</h5><a id="user-content-redis-1" class="anchor" aria-label="Permalink: Redis" href="#redis-1"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">Use Redis URL in one of these formats:</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="redis://[password@]host[port][/db_num] redis+socket://[password@]/path/to/file.sock[:/db_num]"><pre class="notranslate"><code>redis://[password@]host[port][/db_num] redis+socket://[password@]/path/to/file.sock[:/db_num] </code></pre></div> <p dir="auto">For example:</p> <ol dir="auto"> <li><code>redis://localhost:6379</code>, or with password <code>redis://password@localhost:6379</code></li> <li><code>redis+socket://password@/path/to/file.sock:/0</code></li> </ol> <div class="markdown-heading" dir="auto"><h5 tabindex="-1" class="heading-element" dir="auto">AWS SQS</h5><a id="user-content-aws-sqs" class="anchor" aria-label="Permalink: AWS SQS" href="#aws-sqs"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">Use AWS SQS URL in the format:</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="https://sqs.us-east-2.amazonaws.com/123456789012"><pre class="notranslate"><code>https://sqs.us-east-2.amazonaws.com/123456789012 </code></pre></div> <p dir="auto">See <a href="https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/configuring-sdk.html" rel="nofollow">AWS SQS docs</a> for more information. Also, configuring <code>AWS_REGION</code> is required, or an error would be thrown.</p> <p dir="auto">To use a manually configured SQS Client:</p> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="var sqsClient = sqs.New(session.Must(session.NewSession(&aws.Config{ Region: aws.String("YOUR_AWS_REGION"), Credentials: credentials.NewStaticCredentials("YOUR_AWS_ACCESS_KEY", "YOUR_AWS_ACCESS_SECRET", ""), HTTPClient: &http.Client{ Timeout: time.Second * 120, }, }))) var visibilityTimeout = 20 var cnf = &config.Config{ Broker: "YOUR_SQS_URL" DefaultQueue: "machinery_tasks", ResultBackend: "YOUR_BACKEND_URL", SQS: &config.SQSConfig{ Client: sqsClient, // if VisibilityTimeout is nil default to the overall visibility timeout setting for the queue // https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-visibility-timeout.html VisibilityTimeout: &visibilityTimeout, WaitTimeSeconds: 30, }, }"><pre><span class="pl-k">var</span> <span class="pl-s1">sqsClient</span> <span class="pl-c1">=</span> <span class="pl-s1">sqs</span>.<span class="pl-c1">New</span>(<span class="pl-s1">session</span>.<span class="pl-c1">Must</span>(<span class="pl-s1">session</span>.<span class="pl-c1">NewSession</span>(<span class="pl-c1">&</span>aws.<span class="pl-smi">Config</span>{ <span class="pl-s1">Region</span>: <span class="pl-s1">aws</span>.<span class="pl-c1">String</span>(<span class="pl-s">"YOUR_AWS_REGION"</span>), <span class="pl-s1">Credentials</span>: <span class="pl-s1">credentials</span>.<span class="pl-c1">NewStaticCredentials</span>(<span class="pl-s">"YOUR_AWS_ACCESS_KEY"</span>, <span class="pl-s">"YOUR_AWS_ACCESS_SECRET"</span>, <span class="pl-s">""</span>), <span class="pl-s1">HTTPClient</span>: <span class="pl-c1">&</span>http.<span class="pl-smi">Client</span>{ <span class="pl-s1">Timeout</span>: <span class="pl-s1">time</span>.<span class="pl-c1">Second</span> <span class="pl-c1">*</span> <span class="pl-c1">120</span>, }, }))) <span class="pl-k">var</span> <span class="pl-s1">visibilityTimeout</span> <span class="pl-c1">=</span> <span class="pl-c1">20</span> <span class="pl-k">var</span> <span class="pl-s1">cnf</span> <span class="pl-c1">=</span> <span class="pl-c1">&</span>config.<span class="pl-smi">Config</span>{ <span class="pl-s1">Broker</span>: <span class="pl-s">"YOUR_SQS_URL"</span> <span class="pl-s1">DefaultQueue</span>: <span class="pl-s">"machinery_tasks"</span>, <span class="pl-s1">ResultBackend</span>: <span class="pl-s">"YOUR_BACKEND_URL"</span>, <span class="pl-s1">SQS</span>: <span class="pl-c1">&</span>config.<span class="pl-smi">SQSConfig</span>{ <span class="pl-s1">Client</span>: <span class="pl-s1">sqsClient</span>, <span class="pl-c">// if VisibilityTimeout is nil default to the overall visibility timeout setting for the queue</span> <span class="pl-c">// https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-visibility-timeout.html</span> <span class="pl-s1">VisibilityTimeout</span>: <span class="pl-c1">&</span><span class="pl-s1">visibilityTimeout</span>, <span class="pl-s1">WaitTimeSeconds</span>: <span class="pl-c1">30</span>, }, }</pre></div> <div class="markdown-heading" dir="auto"><h5 tabindex="-1" class="heading-element" dir="auto">GCP Pub/Sub</h5><a id="user-content-gcp-pubsub" class="anchor" aria-label="Permalink: GCP Pub/Sub" href="#gcp-pubsub"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">Use GCP Pub/Sub URL in the format:</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="gcppubsub://YOUR_GCP_PROJECT_ID/YOUR_PUBSUB_SUBSCRIPTION_NAME"><pre class="notranslate"><code>gcppubsub://YOUR_GCP_PROJECT_ID/YOUR_PUBSUB_SUBSCRIPTION_NAME </code></pre></div> <p dir="auto">To use a manually configured Pub/Sub Client:</p> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="pubsubClient, err := pubsub.NewClient( context.Background(), "YOUR_GCP_PROJECT_ID", option.WithServiceAccountFile("YOUR_GCP_SERVICE_ACCOUNT_FILE"), ) cnf := &config.Config{ Broker: "gcppubsub://YOUR_GCP_PROJECT_ID/YOUR_PUBSUB_SUBSCRIPTION_NAME" DefaultQueue: "YOUR_PUBSUB_TOPIC_NAME", ResultBackend: "YOUR_BACKEND_URL", GCPPubSub: config.GCPPubSubConfig{ Client: pubsubClient, }, }"><pre><span class="pl-s1">pubsubClient</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">pubsub</span>.<span class="pl-c1">NewClient</span>( <span class="pl-s1">context</span>.<span class="pl-c1">Background</span>(), <span class="pl-s">"YOUR_GCP_PROJECT_ID"</span>, <span class="pl-s1">option</span>.<span class="pl-c1">WithServiceAccountFile</span>(<span class="pl-s">"YOUR_GCP_SERVICE_ACCOUNT_FILE"</span>), ) <span class="pl-s1">cnf</span> <span class="pl-c1">:=</span> <span class="pl-c1">&</span>config.<span class="pl-smi">Config</span>{ <span class="pl-s1">Broker</span>: <span class="pl-s">"gcppubsub://YOUR_GCP_PROJECT_ID/YOUR_PUBSUB_SUBSCRIPTION_NAME"</span> <span class="pl-s1">DefaultQueue</span>: <span class="pl-s">"YOUR_PUBSUB_TOPIC_NAME"</span>, <span class="pl-s1">ResultBackend</span>: <span class="pl-s">"YOUR_BACKEND_URL"</span>, <span class="pl-s1">GCPPubSub</span>: config.<span class="pl-smi">GCPPubSubConfig</span>{ <span class="pl-s1">Client</span>: <span class="pl-s1">pubsubClient</span>, }, }</pre></div> <div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">DefaultQueue</h4><a id="user-content-defaultqueue" class="anchor" aria-label="Permalink: DefaultQueue" href="#defaultqueue"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">Default queue name, e.g. <code>machinery_tasks</code>.</p> <div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">ResultBackend</h4><a id="user-content-resultbackend" class="anchor" aria-label="Permalink: ResultBackend" href="#resultbackend"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">Result backend to use for keeping task states and results.</p> <p dir="auto">Currently supported backends are:</p> <div class="markdown-heading" dir="auto"><h5 tabindex="-1" class="heading-element" dir="auto">Redis</h5><a id="user-content-redis-2" class="anchor" aria-label="Permalink: Redis" href="#redis-2"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">Use Redis URL in one of these formats:</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="redis://[password@]host[port][/db_num] redis+socket://[password@]/path/to/file.sock[:/db_num]"><pre class="notranslate"><code>redis://[password@]host[port][/db_num] redis+socket://[password@]/path/to/file.sock[:/db_num] </code></pre></div> <p dir="auto">For example:</p> <ol dir="auto"> <li><code>redis://localhost:6379</code>, or with password <code>redis://password@localhost:6379</code></li> <li><code>redis+socket://password@/path/to/file.sock:/0</code></li> <li>cluster <code>redis://host1:port1,host2:port2,host3:port3</code></li> <li>cluster with password <code>redis://pass@host1:port1,host2:port2,host3:port3</code></li> </ol> <div class="markdown-heading" dir="auto"><h5 tabindex="-1" class="heading-element" dir="auto">Memcache</h5><a id="user-content-memcache" class="anchor" aria-label="Permalink: Memcache" href="#memcache"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">Use Memcache URL in the format:</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="memcache://host1[:port1][,host2[:port2],...[,hostN[:portN]]]"><pre class="notranslate"><code>memcache://host1[:port1][,host2[:port2],...[,hostN[:portN]]] </code></pre></div> <p dir="auto">For example:</p> <ol dir="auto"> <li><code>memcache://localhost:11211</code> for a single instance, or</li> <li><code>memcache://10.0.0.1:11211,10.0.0.2:11211</code> for a cluster</li> </ol> <div class="markdown-heading" dir="auto"><h5 tabindex="-1" class="heading-element" dir="auto">AMQP</h5><a id="user-content-amqp-1" class="anchor" aria-label="Permalink: AMQP" href="#amqp-1"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">Use AMQP URL in the format:</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="amqp://[username:password@]@host[:port]"><pre class="notranslate"><code>amqp://[username:password@]@host[:port] </code></pre></div> <p dir="auto">For example:</p> <ol dir="auto"> <li><code>amqp://guest:guest@localhost:5672</code></li> </ol> <blockquote> <p dir="auto">Keep in mind AMQP is not recommended as a result backend. See <a href="https://github.com/RichardKnop/machinery#keeping-results">Keeping Results</a></p> </blockquote> <div class="markdown-heading" dir="auto"><h5 tabindex="-1" class="heading-element" dir="auto">MongoDB</h5><a id="user-content-mongodb" class="anchor" aria-label="Permalink: MongoDB" href="#mongodb"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">Use Mongodb URL in the format:</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]"><pre class="notranslate"><code>mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]] </code></pre></div> <p dir="auto">For example:</p> <ol dir="auto"> <li><code>mongodb://localhost:27017/taskresults</code></li> </ol> <p dir="auto">See <a href="https://docs.mongodb.org/manual/reference/connection-string/" rel="nofollow">MongoDB docs</a> for more information.</p> <div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">ResultsExpireIn</h4><a id="user-content-resultsexpirein" class="anchor" aria-label="Permalink: ResultsExpireIn" href="#resultsexpirein"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">How long to store task results for in seconds. Defaults to <code>3600</code> (1 hour).</p> <div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">AMQP</h4><a id="user-content-amqp-2" class="anchor" aria-label="Permalink: AMQP" href="#amqp-2"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">RabbitMQ related configuration. Not necessary if you are using other broker/backend.</p> <ul dir="auto"> <li><code>Exchange</code>: exchange name, e.g. <code>machinery_exchange</code></li> <li><code>ExchangeType</code>: exchange type, e.g. <code>direct</code></li> <li><code>QueueBindingArguments</code>: an optional map of additional arguments used when binding to an AMQP queue</li> <li><code>BindingKey</code>: The queue is bind to the exchange with this key, e.g. <code>machinery_task</code></li> <li><code>PrefetchCount</code>: How many tasks to prefetch (set to <code>1</code> if you have long running tasks)</li> <li><code>DelayedQueue</code>: delayed queue name to be used for task retry or delayed task (if empty it will follow auto create and delate delayed queues)</li> </ul> <div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">DynamoDB</h4><a id="user-content-dynamodb" class="anchor" aria-label="Permalink: DynamoDB" href="#dynamodb"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">DynamoDB related configuration. Not necessary if you are using other backend.</p> <ul dir="auto"> <li><code>TaskStatesTable</code>: Custom table name for saving task states. Default one is <code>task_states</code>, and make sure to create this table in your AWS admin first, using <code>TaskUUID</code> as table's primary key.</li> <li><code>GroupMetasTable</code>: Custom table name for saving group metas. Default one is <code>group_metas</code>, and make sure to create this table in your AWS admin first, using <code>GroupUUID</code> as table's primary key. For example:</li> </ul> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="dynamodb: task_states_table: 'task_states' group_metas_table: 'group_metas'"><pre class="notranslate"><code>dynamodb: task_states_table: 'task_states' group_metas_table: 'group_metas' </code></pre></div> <p dir="auto">If these tables are not found, an fatal error would be thrown.</p> <p dir="auto">If you wish to expire the records, you can configure the <code>TTL</code> field in AWS admin for these tables. The <code>TTL</code> field is set based on the <code>ResultsExpireIn</code> value in the Server's config. See <a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/howitworks-ttl.html" rel="nofollow">https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/howitworks-ttl.html</a> for more information.</p> <div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">Redis</h4><a id="user-content-redis-3" class="anchor" aria-label="Permalink: Redis" href="#redis-3"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">Redis related configuration. Not necessary if you are using other backend.</p> <p dir="auto">See: <a href="/RichardKnop/machinery/blob/master/v1/config/config.go">config</a> (TODO)</p> <div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">GCPPubSub</h4><a id="user-content-gcppubsub" class="anchor" aria-label="Permalink: GCPPubSub" href="#gcppubsub"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">GCPPubSub related configuration. Not necessary if you are using other backend.</p> <p dir="auto">See: <a href="/RichardKnop/machinery/blob/master/v1/config/config.go">config</a> (TODO)</p> <div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">Custom Logger</h3><a id="user-content-custom-logger" class="anchor" aria-label="Permalink: Custom Logger" href="#custom-logger"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">You can define a custom logger by implementing the following interface:</p> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="type Interface interface { Print(...interface{}) Printf(string, ...interface{}) Println(...interface{}) Fatal(...interface{}) Fatalf(string, ...interface{}) Fatalln(...interface{}) Panic(...interface{}) Panicf(string, ...interface{}) Panicln(...interface{}) }"><pre><span class="pl-k">type</span> <span class="pl-smi">Interface</span> <span class="pl-k">interface</span> { <span class="pl-c1">Print</span>(<span class="pl-c1">...</span><span class="pl-k">interface</span>{}) <span class="pl-c1">Printf</span>(<span class="pl-smi">string</span>, <span class="pl-c1">...</span><span class="pl-k">interface</span>{}) <span class="pl-c1">Println</span>(<span class="pl-c1">...</span><span class="pl-k">interface</span>{}) <span class="pl-c1">Fatal</span>(<span class="pl-c1">...</span><span class="pl-k">interface</span>{}) <span class="pl-c1">Fatalf</span>(<span class="pl-smi">string</span>, <span class="pl-c1">...</span><span class="pl-k">interface</span>{}) <span class="pl-c1">Fatalln</span>(<span class="pl-c1">...</span><span class="pl-k">interface</span>{}) <span class="pl-c1">Panic</span>(<span class="pl-c1">...</span><span class="pl-k">interface</span>{}) <span class="pl-c1">Panicf</span>(<span class="pl-smi">string</span>, <span class="pl-c1">...</span><span class="pl-k">interface</span>{}) <span class="pl-c1">Panicln</span>(<span class="pl-c1">...</span><span class="pl-k">interface</span>{}) }</pre></div> <p dir="auto">Then just set the logger in your setup code by calling <code>Set</code> function exported by <code>github.com/RichardKnop/machinery/v1/log</code> package:</p> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="log.Set(myCustomLogger)"><pre><span class="pl-s1">log</span>.<span class="pl-c1">Set</span>(<span class="pl-s1">myCustomLogger</span>)</pre></div> <div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">Server</h3><a id="user-content-server" class="anchor" aria-label="Permalink: Server" href="#server"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">A Machinery library must be instantiated before use. The way this is done is by creating a <code>Server</code> instance. <code>Server</code> is a base object which stores Machinery configuration and registered tasks. E.g.:</p> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import ( "github.com/RichardKnop/machinery/v1/config" "github.com/RichardKnop/machinery/v1" ) var cnf = &config.Config{ Broker: "amqp://guest:guest@localhost:5672/", DefaultQueue: "machinery_tasks", ResultBackend: "amqp://guest:guest@localhost:5672/", AMQP: &config.AMQPConfig{ Exchange: "machinery_exchange", ExchangeType: "direct", BindingKey: "machinery_task", }, } server, err := machinery.NewServer(cnf) if err != nil { // do something with the error }"><pre><span class="pl-k">import</span> ( <span class="pl-s">"github.com/RichardKnop/machinery/v1/config"</span> <span class="pl-s">"github.com/RichardKnop/machinery/v1"</span> ) <span class="pl-k">var</span> <span class="pl-s1">cnf</span> <span class="pl-c1">=</span> <span class="pl-c1">&</span>config.<span class="pl-smi">Config</span>{ <span class="pl-s1">Broker</span>: <span class="pl-s">"amqp://guest:guest@localhost:5672/"</span>, <span class="pl-s1">DefaultQueue</span>: <span class="pl-s">"machinery_tasks"</span>, <span class="pl-s1">ResultBackend</span>: <span class="pl-s">"amqp://guest:guest@localhost:5672/"</span>, <span class="pl-s1">AMQP</span>: <span class="pl-c1">&</span>config.<span class="pl-smi">AMQPConfig</span>{ <span class="pl-s1">Exchange</span>: <span class="pl-s">"machinery_exchange"</span>, <span class="pl-s1">ExchangeType</span>: <span class="pl-s">"direct"</span>, <span class="pl-s1">BindingKey</span>: <span class="pl-s">"machinery_task"</span>, }, } <span class="pl-s1">server</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">machinery</span>.<span class="pl-c1">NewServer</span>(<span class="pl-s1">cnf</span>) <span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> { <span class="pl-c">// do something with the error</span> }</pre></div> <div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">Workers</h3><a id="user-content-workers" class="anchor" aria-label="Permalink: Workers" href="#workers"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">In order to consume tasks, you need to have one or more workers running. All you need to run a worker is a <code>Server</code> instance with registered tasks. E.g.:</p> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="worker := server.NewWorker("worker_name", 10) err := worker.Launch() if err != nil { // do something with the error }"><pre><span class="pl-s1">worker</span> <span class="pl-c1">:=</span> <span class="pl-s1">server</span>.<span class="pl-c1">NewWorker</span>(<span class="pl-s">"worker_name"</span>, <span class="pl-c1">10</span>) <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">worker</span>.<span class="pl-c1">Launch</span>() <span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> { <span class="pl-c">// do something with the error</span> }</pre></div> <p dir="auto">Each worker will only consume registered tasks. For each task on the queue the Worker.Process() method will be run in a goroutine. Use the second parameter of <code>server.NewWorker</code> to limit the number of concurrently running Worker.Process() calls (per worker). Example: 1 will serialize task execution while 0 makes the number of concurrently executed tasks unlimited (default).</p> <div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">Tasks</h3><a id="user-content-tasks" class="anchor" aria-label="Permalink: Tasks" href="#tasks"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">Tasks are a building block of Machinery applications. A task is a function which defines what happens when a worker receives a message.</p> <p dir="auto">Each task needs to return an error as a last return value. In addition to error tasks can now return any number of arguments.</p> <p dir="auto">Examples of valid tasks:</p> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="func Add(args ...int64) (int64, error) { sum := int64(0) for _, arg := range args { sum += arg } return sum, nil } func Multiply(args ...int64) (int64, error) { sum := int64(1) for _, arg := range args { sum *= arg } return sum, nil } // You can use context.Context as first argument to tasks, useful for open tracing func TaskWithContext(ctx context.Context, arg Arg) error { // ... use ctx ... return nil } // Tasks need to return at least error as a minimal requirement func DummyTask(arg string) error { return errors.New(arg) } // You can also return multiple results from the task func DummyTask2(arg1, arg2 string) (string, string, error) { return arg1, arg2, nil }"><pre><span class="pl-k">func</span> <span class="pl-s1">Add</span>(<span class="pl-s1">args</span> <span class="pl-c1">...</span><span class="pl-smi">int64</span>) (<span class="pl-smi">int64</span>, <span class="pl-smi">error</span>) { <span class="pl-s1">sum</span> <span class="pl-c1">:=</span> <span class="pl-s1">int64</span>(<span class="pl-c1">0</span>) <span class="pl-k">for</span> <span class="pl-s1">_</span>, <span class="pl-s1">arg</span> <span class="pl-c1">:=</span> <span class="pl-k">range</span> <span class="pl-s1">args</span> { <span class="pl-s1">sum</span> <span class="pl-c1">+=</span> <span class="pl-s1">arg</span> } <span class="pl-k">return</span> <span class="pl-s1">sum</span>, <span class="pl-c1">nil</span> } <span class="pl-k">func</span> <span class="pl-s1">Multiply</span>(<span class="pl-s1">args</span> <span class="pl-c1">...</span><span class="pl-smi">int64</span>) (<span class="pl-smi">int64</span>, <span class="pl-smi">error</span>) { <span class="pl-s1">sum</span> <span class="pl-c1">:=</span> <span class="pl-s1">int64</span>(<span class="pl-c1">1</span>) <span class="pl-k">for</span> <span class="pl-s1">_</span>, <span class="pl-s1">arg</span> <span class="pl-c1">:=</span> <span class="pl-k">range</span> <span class="pl-s1">args</span> { <span class="pl-s1">sum</span> <span class="pl-c1">*=</span> <span class="pl-s1">arg</span> } <span class="pl-k">return</span> <span class="pl-s1">sum</span>, <span class="pl-c1">nil</span> } <span class="pl-c">// You can use context.Context as first argument to tasks, useful for open tracing</span> <span class="pl-k">func</span> <span class="pl-s1">TaskWithContext</span>(<span class="pl-s1">ctx</span> context.<span class="pl-smi">Context</span>, <span class="pl-s1">arg</span> <span class="pl-smi">Arg</span>) <span class="pl-smi">error</span> { <span class="pl-c">// ... use ctx ...</span> <span class="pl-k">return</span> <span class="pl-c1">nil</span> } <span class="pl-c">// Tasks need to return at least error as a minimal requirement</span> <span class="pl-k">func</span> <span class="pl-s1">DummyTask</span>(<span class="pl-s1">arg</span> <span class="pl-smi">string</span>) <span class="pl-smi">error</span> { <span class="pl-k">return</span> <span class="pl-s1">errors</span>.<span class="pl-c1">New</span>(<span class="pl-s1">arg</span>) } <span class="pl-c">// You can also return multiple results from the task</span> <span class="pl-k">func</span> <span class="pl-s1">DummyTask2</span>(<span class="pl-s1">arg1</span>, <span class="pl-s1">arg2</span> <span class="pl-smi">string</span>) (<span class="pl-smi">string</span>, <span class="pl-smi">string</span>, <span class="pl-smi">error</span>) { <span class="pl-k">return</span> <span class="pl-s1">arg1</span>, <span class="pl-s1">arg2</span>, <span class="pl-c1">nil</span> }</pre></div> <div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">Registering Tasks</h4><a id="user-content-registering-tasks" class="anchor" aria-label="Permalink: Registering Tasks" href="#registering-tasks"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">Before your workers can consume a task, you need to register it with the server. This is done by assigning a task a unique name:</p> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="server.RegisterTasks(map[string]interface{}{ "add": Add, "multiply": Multiply, })"><pre><span class="pl-s1">server</span>.<span class="pl-c1">RegisterTasks</span>(<span class="pl-k">map</span>[<span class="pl-smi">string</span>]<span class="pl-k">interface</span>{}{ <span class="pl-s">"add"</span>: <span class="pl-s1">Add</span>, <span class="pl-s">"multiply"</span>: <span class="pl-s1">Multiply</span>, })</pre></div> <p dir="auto">Tasks can also be registered one by one:</p> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="server.RegisterTask("add", Add) server.RegisterTask("multiply", Multiply)"><pre><span class="pl-s1">server</span>.<span class="pl-c1">RegisterTask</span>(<span class="pl-s">"add"</span>, <span class="pl-s1">Add</span>) <span class="pl-s1">server</span>.<span class="pl-c1">RegisterTask</span>(<span class="pl-s">"multiply"</span>, <span class="pl-s1">Multiply</span>)</pre></div> <p dir="auto">Simply put, when a worker receives a message like this:</p> <div class="highlight highlight-source-json notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="{ "UUID": "48760a1a-8576-4536-973b-da09048c2ac5", "Name": "add", "RoutingKey": "", "ETA": null, "GroupUUID": "", "GroupTaskCount": 0, "Args": [ { "Type": "int64", "Value": 1, }, { "Type": "int64", "Value": 1, } ], "Immutable": false, "RetryCount": 0, "RetryTimeout": 0, "OnSuccess": null, "OnError": null, "ChordCallback": null }"><pre>{ <span class="pl-ent">"UUID"</span>: <span class="pl-s"><span class="pl-pds">"</span>48760a1a-8576-4536-973b-da09048c2ac5<span class="pl-pds">"</span></span>, <span class="pl-ent">"Name"</span>: <span class="pl-s"><span class="pl-pds">"</span>add<span class="pl-pds">"</span></span>, <span class="pl-ent">"RoutingKey"</span>: <span class="pl-s"><span class="pl-pds">"</span><span class="pl-pds">"</span></span>, <span class="pl-ent">"ETA"</span>: <span class="pl-c1">null</span>, <span class="pl-ent">"GroupUUID"</span>: <span class="pl-s"><span class="pl-pds">"</span><span class="pl-pds">"</span></span>, <span class="pl-ent">"GroupTaskCount"</span>: <span class="pl-c1">0</span>, <span class="pl-ent">"Args"</span>: [ { <span class="pl-ent">"Type"</span>: <span class="pl-s"><span class="pl-pds">"</span>int64<span class="pl-pds">"</span></span>, <span class="pl-ent">"Value"</span>: <span class="pl-c1">1</span>, }, { <span class="pl-ent">"Type"</span>: <span class="pl-s"><span class="pl-pds">"</span>int64<span class="pl-pds">"</span></span>, <span class="pl-ent">"Value"</span>: <span class="pl-c1">1</span>, } ], <span class="pl-ent">"Immutable"</span>: <span class="pl-c1">false</span>, <span class="pl-ent">"RetryCount"</span>: <span class="pl-c1">0</span>, <span class="pl-ent">"RetryTimeout"</span>: <span class="pl-c1">0</span>, <span class="pl-ent">"OnSuccess"</span>: <span class="pl-c1">null</span>, <span class="pl-ent">"OnError"</span>: <span class="pl-c1">null</span>, <span class="pl-ent">"ChordCallback"</span>: <span class="pl-c1">null</span> }</pre></div> <p dir="auto">It will call Add(1, 1). Each task should return an error as well so we can handle failures.</p> <p dir="auto">Ideally, tasks should be idempotent which means there will be no unintended consequences when a task is called multiple times with the same arguments.</p> <div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">Signatures</h4><a id="user-content-signatures" class="anchor" aria-label="Permalink: Signatures" href="#signatures"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">A signature wraps calling arguments, execution options (such as immutability) and success/error callbacks of a task so it can be sent across the wire to workers. Task signatures implement a simple interface:</p> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// Arg represents a single argument passed to invocation fo a task type Arg struct { Type string Value interface{} } // Headers represents the headers which should be used to direct the task type Headers map[string]interface{} // Signature represents a single task invocation type Signature struct { UUID string Name string RoutingKey string ETA *time.Time GroupUUID string GroupTaskCount int Args []Arg Headers Headers Immutable bool RetryCount int RetryTimeout int OnSuccess []*Signature OnError []*Signature ChordCallback *Signature }"><pre><span class="pl-c">// Arg represents a single argument passed to invocation fo a task</span> <span class="pl-k">type</span> <span class="pl-smi">Arg</span> <span class="pl-k">struct</span> { <span class="pl-c1">Type</span> <span class="pl-smi">string</span> <span class="pl-c1">Value</span> <span class="pl-k">interface</span>{} } <span class="pl-c">// Headers represents the headers which should be used to direct the task</span> <span class="pl-k">type</span> <span class="pl-smi">Headers</span> <span class="pl-k">map</span>[<span class="pl-smi">string</span>]<span class="pl-k">interface</span>{} <span class="pl-c">// Signature represents a single task invocation</span> <span class="pl-k">type</span> <span class="pl-smi">Signature</span> <span class="pl-k">struct</span> { <span class="pl-c1">UUID</span> <span class="pl-smi">string</span> <span class="pl-c1">Name</span> <span class="pl-smi">string</span> <span class="pl-c1">RoutingKey</span> <span class="pl-smi">string</span> <span class="pl-c1">ETA</span> <span class="pl-c1">*</span>time.<span class="pl-smi">Time</span> <span class="pl-c1">GroupUUID</span> <span class="pl-smi">string</span> <span class="pl-c1">GroupTaskCount</span> <span class="pl-smi">int</span> <span class="pl-c1">Args</span> []<span class="pl-smi">Arg</span> <span class="pl-c1">Headers</span> <span class="pl-smi">Headers</span> <span class="pl-c1">Immutable</span> <span class="pl-smi">bool</span> <span class="pl-c1">RetryCount</span> <span class="pl-smi">int</span> <span class="pl-c1">RetryTimeout</span> <span class="pl-smi">int</span> <span class="pl-c1">OnSuccess</span> []<span class="pl-c1">*</span><span class="pl-smi">Signature</span> <span class="pl-c1">OnError</span> []<span class="pl-c1">*</span><span class="pl-smi">Signature</span> <span class="pl-c1">ChordCallback</span> <span class="pl-c1">*</span><span class="pl-smi">Signature</span> }</pre></div> <p dir="auto"><code>UUID</code> is a unique ID of a task. You can either set it yourself or it will be automatically generated.</p> <p dir="auto"><code>Name</code> is the unique task name by which it is registered against a Server instance.</p> <p dir="auto"><code>RoutingKey</code> is used for routing a task to correct queue. If you leave it empty, the default behaviour will be to set it to the default queue's binding key for direct exchange type and to the default queue name for other exchange types.</p> <p dir="auto"><code>ETA</code> is a timestamp used for delaying a task. if it's nil, the task will be published for workers to consume immediately. If it is set, the task will be delayed until the ETA timestamp.</p> <p dir="auto"><code>GroupUUID</code>, <code>GroupTaskCount</code> are useful for creating groups of tasks.</p> <p dir="auto"><code>Args</code> is a list of arguments that will be passed to the task when it is executed by a worker.</p> <p dir="auto"><code>Headers</code> is a list of headers that will be used when publishing the task to AMQP queue.</p> <p dir="auto"><code>Immutable</code> is a flag which defines whether a result of the executed task can be modified or not. This is important with <code>OnSuccess</code> callbacks. Immutable task will not pass its result to its success callbacks while a mutable task will prepend its result to args sent to callback tasks. Long story short, set Immutable to false if you want to pass result of the first task in a chain to the second task.</p> <p dir="auto"><code>RetryCount</code> specifies how many times a failed task should be retried (defaults to 0). Retry attempts will be spaced out in time, after each failure another attempt will be scheduled further to the future.</p> <p dir="auto"><code>RetryTimeout</code> specifies how long to wait before resending task to the queue for retry attempt. Default behaviour is to use fibonacci sequence to increase the timeout after each failed retry attempt.</p> <p dir="auto"><code>OnSuccess</code> defines tasks which will be called after the task has executed successfully. It is a slice of task signature structs.</p> <p dir="auto"><code>OnError</code> defines tasks which will be called after the task execution fails. The first argument passed to error callbacks will be the error string returned from the failed task.</p> <p dir="auto"><code>ChordCallback</code> is used to create a callback to a group of tasks.</p> <div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">Supported Types</h4><a id="user-content-supported-types" class="anchor" aria-label="Permalink: Supported Types" href="#supported-types"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">Machinery encodes tasks to JSON before sending them to the broker. Task results are also stored in the backend as JSON encoded strings. Therefor only types with native JSON representation can be supported. Currently supported types are:</p> <ul dir="auto"> <li><code>bool</code></li> <li><code>int</code></li> <li><code>int8</code></li> <li><code>int16</code></li> <li><code>int32</code></li> <li><code>int64</code></li> <li><code>uint</code></li> <li><code>uint8</code></li> <li><code>uint16</code></li> <li><code>uint32</code></li> <li><code>uint64</code></li> <li><code>float32</code></li> <li><code>float64</code></li> <li><code>string</code></li> <li><code>[]bool</code></li> <li><code>[]int</code></li> <li><code>[]int8</code></li> <li><code>[]int16</code></li> <li><code>[]int32</code></li> <li><code>[]int64</code></li> <li><code>[]uint</code></li> <li><code>[]uint8</code></li> <li><code>[]uint16</code></li> <li><code>[]uint32</code></li> <li><code>[]uint64</code></li> <li><code>[]float32</code></li> <li><code>[]float64</code></li> <li><code>[]string</code></li> </ul> <div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">Sending Tasks</h4><a id="user-content-sending-tasks" class="anchor" aria-label="Permalink: Sending Tasks" href="#sending-tasks"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">Tasks can be called by passing an instance of <code>Signature</code> to an <code>Server</code> instance. E.g:</p> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import ( "github.com/RichardKnop/machinery/v1/tasks" ) signature := &tasks.Signature{ Name: "add", Args: []tasks.Arg{ { Type: "int64", Value: 1, }, { Type: "int64", Value: 1, }, }, } asyncResult, err := server.SendTask(signature) if err != nil { // failed to send the task // do something with the error }"><pre><span class="pl-k">import</span> ( <span class="pl-s">"github.com/RichardKnop/machinery/v1/tasks"</span> ) <span class="pl-s1">signature</span> <span class="pl-c1">:=</span> <span class="pl-c1">&</span>tasks.<span class="pl-smi">Signature</span>{ <span class="pl-s1">Name</span>: <span class="pl-s">"add"</span>, <span class="pl-s1">Args</span>: []tasks.<span class="pl-smi">Arg</span>{ { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">1</span>, }, { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">1</span>, }, }, } <span class="pl-s1">asyncResult</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">server</span>.<span class="pl-c1">SendTask</span>(<span class="pl-s1">signature</span>) <span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> { <span class="pl-c">// failed to send the task</span> <span class="pl-c">// do something with the error</span> }</pre></div> <div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">Delayed Tasks</h4><a id="user-content-delayed-tasks" class="anchor" aria-label="Permalink: Delayed Tasks" href="#delayed-tasks"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">You can delay a task by setting the <code>ETA</code> timestamp field on the task signature.</p> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// Delay the task by 5 seconds eta := time.Now().UTC().Add(time.Second * 5) signature.ETA = &eta"><pre><span class="pl-c">// Delay the task by 5 seconds</span> <span class="pl-s1">eta</span> <span class="pl-c1">:=</span> <span class="pl-s1">time</span>.<span class="pl-c1">Now</span>().<span class="pl-c1">UTC</span>().<span class="pl-c1">Add</span>(<span class="pl-s1">time</span>.<span class="pl-c1">Second</span> <span class="pl-c1">*</span> <span class="pl-c1">5</span>) <span class="pl-s1">signature</span>.<span class="pl-c1">ETA</span> <span class="pl-c1">=</span> <span class="pl-c1">&</span><span class="pl-s1">eta</span></pre></div> <div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">Retry Tasks</h4><a id="user-content-retry-tasks" class="anchor" aria-label="Permalink: Retry Tasks" href="#retry-tasks"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">You can set a number of retry attempts before declaring task as failed. Fibonacci sequence will be used to space out retry requests over time. (See <code>RetryTimeout</code> for details.)</p> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// If the task fails, retry it up to 3 times signature.RetryCount = 3"><pre><span class="pl-c">// If the task fails, retry it up to 3 times</span> <span class="pl-s1">signature</span>.<span class="pl-c1">RetryCount</span> <span class="pl-c1">=</span> <span class="pl-c1">3</span></pre></div> <p dir="auto">Alternatively, you can return <code>tasks.ErrRetryTaskLater</code> from your task and specify duration after which the task should be retried, e.g.:</p> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="return tasks.NewErrRetryTaskLater("some error", 4 * time.Hour)"><pre><span class="pl-k">return</span> <span class="pl-s1">tasks</span>.<span class="pl-c1">NewErrRetryTaskLater</span>(<span class="pl-s">"some error"</span>, <span class="pl-c1">4</span> <span class="pl-c1">*</span> <span class="pl-s1">time</span>.<span class="pl-c1">Hour</span>)</pre></div> <div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">Get Pending Tasks</h4><a id="user-content-get-pending-tasks" class="anchor" aria-label="Permalink: Get Pending Tasks" href="#get-pending-tasks"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">Tasks currently waiting in the queue to be consumed by workers can be inspected, e.g.:</p> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="server.GetBroker().GetPendingTasks("some_queue")"><pre><span class="pl-s1">server</span>.<span class="pl-c1">GetBroker</span>().<span class="pl-c1">GetPendingTasks</span>(<span class="pl-s">"some_queue"</span>)</pre></div> <blockquote> <p dir="auto">Currently only supported by Redis broker.</p> </blockquote> <div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">Keeping Results</h4><a id="user-content-keeping-results" class="anchor" aria-label="Permalink: Keeping Results" href="#keeping-results"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">If you configure a result backend, the task states and results will be persisted. Possible states:</p> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="const ( // StatePending - initial state of a task StatePending = "PENDING" // StateReceived - when task is received by a worker StateReceived = "RECEIVED" // StateStarted - when the worker starts processing the task StateStarted = "STARTED" // StateRetry - when failed task has been scheduled for retry StateRetry = "RETRY" // StateSuccess - when the task is processed successfully StateSuccess = "SUCCESS" // StateFailure - when processing of the task fails StateFailure = "FAILURE" )"><pre><span class="pl-k">const</span> ( <span class="pl-c">// StatePending - initial state of a task</span> <span class="pl-s1">StatePending</span> <span class="pl-c1">=</span> <span class="pl-s">"PENDING"</span> <span class="pl-c">// StateReceived - when task is received by a worker</span> <span class="pl-s1">StateReceived</span> <span class="pl-c1">=</span> <span class="pl-s">"RECEIVED"</span> <span class="pl-c">// StateStarted - when the worker starts processing the task</span> <span class="pl-s1">StateStarted</span> <span class="pl-c1">=</span> <span class="pl-s">"STARTED"</span> <span class="pl-c">// StateRetry - when failed task has been scheduled for retry</span> <span class="pl-s1">StateRetry</span> <span class="pl-c1">=</span> <span class="pl-s">"RETRY"</span> <span class="pl-c">// StateSuccess - when the task is processed successfully</span> <span class="pl-s1">StateSuccess</span> <span class="pl-c1">=</span> <span class="pl-s">"SUCCESS"</span> <span class="pl-c">// StateFailure - when processing of the task fails</span> <span class="pl-s1">StateFailure</span> <span class="pl-c1">=</span> <span class="pl-s">"FAILURE"</span> )</pre></div> <blockquote> <p dir="auto">When using AMQP as a result backend, task states will be persisted in separate queues for each task. Although RabbitMQ can scale up to thousands of queues, it is strongly advised to use a better suited result backend (e.g. Memcache) when you are expecting to run a large number of parallel tasks.</p> </blockquote> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// TaskResult represents an actual return value of a processed task type TaskResult struct { Type string `bson:"type"` Value interface{} `bson:"value"` } // TaskState represents a state of a task type TaskState struct { TaskUUID string `bson:"_id"` State string `bson:"state"` Results []*TaskResult `bson:"results"` Error string `bson:"error"` } // GroupMeta stores useful metadata about tasks within the same group // E.g. UUIDs of all tasks which are used in order to check if all tasks // completed successfully or not and thus whether to trigger chord callback type GroupMeta struct { GroupUUID string `bson:"_id"` TaskUUIDs []string `bson:"task_uuids"` ChordTriggered bool `bson:"chord_triggered"` Lock bool `bson:"lock"` }"><pre><span class="pl-c">// TaskResult represents an actual return value of a processed task</span> <span class="pl-k">type</span> <span class="pl-smi">TaskResult</span> <span class="pl-k">struct</span> { <span class="pl-c1">Type</span> <span class="pl-smi">string</span> <span class="pl-s">`bson:"type"`</span> <span class="pl-c1">Value</span> <span class="pl-k">interface</span>{} <span class="pl-s">`bson:"value"`</span> } <span class="pl-c">// TaskState represents a state of a task</span> <span class="pl-k">type</span> <span class="pl-smi">TaskState</span> <span class="pl-k">struct</span> { <span class="pl-c1">TaskUUID</span> <span class="pl-smi">string</span> <span class="pl-s">`bson:"_id"`</span> <span class="pl-c1">State</span> <span class="pl-smi">string</span> <span class="pl-s">`bson:"state"`</span> <span class="pl-c1">Results</span> []<span class="pl-c1">*</span><span class="pl-smi">TaskResult</span> <span class="pl-s">`bson:"results"`</span> <span class="pl-c1">Error</span> <span class="pl-smi">string</span> <span class="pl-s">`bson:"error"`</span> } <span class="pl-c">// GroupMeta stores useful metadata about tasks within the same group</span> <span class="pl-c">// E.g. UUIDs of all tasks which are used in order to check if all tasks</span> <span class="pl-c">// completed successfully or not and thus whether to trigger chord callback</span> <span class="pl-k">type</span> <span class="pl-smi">GroupMeta</span> <span class="pl-k">struct</span> { <span class="pl-c1">GroupUUID</span> <span class="pl-smi">string</span> <span class="pl-s">`bson:"_id"`</span> <span class="pl-c1">TaskUUIDs</span> []<span class="pl-smi">string</span> <span class="pl-s">`bson:"task_uuids"`</span> <span class="pl-c1">ChordTriggered</span> <span class="pl-smi">bool</span> <span class="pl-s">`bson:"chord_triggered"`</span> <span class="pl-c1">Lock</span> <span class="pl-smi">bool</span> <span class="pl-s">`bson:"lock"`</span> }</pre></div> <p dir="auto"><code>TaskResult</code> represents a slice of return values of a processed task.</p> <p dir="auto"><code>TaskState</code> struct will be serialized and stored every time a task state changes.</p> <p dir="auto"><code>GroupMeta</code> stores useful metadata about tasks within the same group. E.g. UUIDs of all tasks which are used in order to check if all tasks completed successfully or not and thus whether to trigger chord callback.</p> <p dir="auto"><code>AsyncResult</code> object allows you to check for the state of a task:</p> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="taskState := asyncResult.GetState() fmt.Printf("Current state of %v task is:\n", taskState.TaskUUID) fmt.Println(taskState.State)"><pre><span class="pl-s1">taskState</span> <span class="pl-c1">:=</span> <span class="pl-s1">asyncResult</span>.<span class="pl-c1">GetState</span>() <span class="pl-s1">fmt</span>.<span class="pl-c1">Printf</span>(<span class="pl-s">"Current state of %v task is:<span class="pl-cce">\n</span>"</span>, <span class="pl-s1">taskState</span>.<span class="pl-c1">TaskUUID</span>) <span class="pl-s1">fmt</span>.<span class="pl-c1">Println</span>(<span class="pl-s1">taskState</span>.<span class="pl-c1">State</span>)</pre></div> <p dir="auto">There are couple of convenient methods to inspect the task status:</p> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="asyncResult.GetState().IsCompleted() asyncResult.GetState().IsSuccess() asyncResult.GetState().IsFailure()"><pre><span class="pl-s1">asyncResult</span>.<span class="pl-c1">GetState</span>().<span class="pl-c1">IsCompleted</span>() <span class="pl-s1">asyncResult</span>.<span class="pl-c1">GetState</span>().<span class="pl-c1">IsSuccess</span>() <span class="pl-s1">asyncResult</span>.<span class="pl-c1">GetState</span>().<span class="pl-c1">IsFailure</span>()</pre></div> <p dir="auto">You can also do a synchronous blocking call to wait for a task result:</p> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="results, err := asyncResult.Get(time.Duration(time.Millisecond * 5)) if err != nil { // getting result of a task failed // do something with the error } for _, result := range results { fmt.Println(result.Interface()) }"><pre><span class="pl-s1">results</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">asyncResult</span>.<span class="pl-c1">Get</span>(<span class="pl-s1">time</span>.<span class="pl-c1">Duration</span>(<span class="pl-s1">time</span>.<span class="pl-c1">Millisecond</span> <span class="pl-c1">*</span> <span class="pl-c1">5</span>)) <span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> { <span class="pl-c">// getting result of a task failed</span> <span class="pl-c">// do something with the error</span> } <span class="pl-k">for</span> <span class="pl-s1">_</span>, <span class="pl-s1">result</span> <span class="pl-c1">:=</span> <span class="pl-k">range</span> <span class="pl-s1">results</span> { <span class="pl-s1">fmt</span>.<span class="pl-c1">Println</span>(<span class="pl-s1">result</span>.<span class="pl-c1">Interface</span>()) }</pre></div> <div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">Error Handling</h4><a id="user-content-error-handling" class="anchor" aria-label="Permalink: Error Handling" href="#error-handling"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">When a task returns with an error, the default behavior is to first attempty to retry the task if it's retriable, otherwise log the error and then eventually call any error callbacks.</p> <p dir="auto">To customize this, you can set a custom error handler on the worker which can do more than just logging after retries fail and error callbacks are trigerred:</p> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="worker.SetErrorHandler(func (err error) { customHandler(err) })"><pre><span class="pl-s1">worker</span>.<span class="pl-c1">SetErrorHandler</span>(<span class="pl-k">func</span> (<span class="pl-s1">err</span> <span class="pl-smi">error</span>) { <span class="pl-s1">customHandler</span>(<span class="pl-s1">err</span>) })</pre></div> <div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">Workflows</h3><a id="user-content-workflows" class="anchor" aria-label="Permalink: Workflows" href="#workflows"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">Running a single asynchronous task is fine but often you will want to design a workflow of tasks to be executed in an orchestrated way. There are couple of useful functions to help you design workflows.</p> <div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">Groups</h4><a id="user-content-groups" class="anchor" aria-label="Permalink: Groups" href="#groups"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto"><code>Group</code> is a set of tasks which will be executed in parallel, independent of each other. E.g.:</p> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import ( "github.com/RichardKnop/machinery/v1/tasks" "github.com/RichardKnop/machinery/v1" ) signature1 := tasks.Signature{ Name: "add", Args: []tasks.Arg{ { Type: "int64", Value: 1, }, { Type: "int64", Value: 1, }, }, } signature2 := tasks.Signature{ Name: "add", Args: []tasks.Arg{ { Type: "int64", Value: 5, }, { Type: "int64", Value: 5, }, }, } group, _ := tasks.NewGroup(&signature1, &signature2) asyncResults, err := server.SendGroup(group, 0) //The second parameter specifies the number of concurrent sending tasks. 0 means unlimited. if err != nil { // failed to send the group // do something with the error }"><pre><span class="pl-k">import</span> ( <span class="pl-s">"github.com/RichardKnop/machinery/v1/tasks"</span> <span class="pl-s">"github.com/RichardKnop/machinery/v1"</span> ) <span class="pl-s1">signature1</span> <span class="pl-c1">:=</span> tasks.<span class="pl-smi">Signature</span>{ <span class="pl-s1">Name</span>: <span class="pl-s">"add"</span>, <span class="pl-s1">Args</span>: []tasks.<span class="pl-smi">Arg</span>{ { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">1</span>, }, { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">1</span>, }, }, } <span class="pl-s1">signature2</span> <span class="pl-c1">:=</span> tasks.<span class="pl-smi">Signature</span>{ <span class="pl-s1">Name</span>: <span class="pl-s">"add"</span>, <span class="pl-s1">Args</span>: []tasks.<span class="pl-smi">Arg</span>{ { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">5</span>, }, { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">5</span>, }, }, } <span class="pl-s1">group</span>, <span class="pl-s1">_</span> <span class="pl-c1">:=</span> <span class="pl-s1">tasks</span>.<span class="pl-c1">NewGroup</span>(<span class="pl-c1">&</span><span class="pl-s1">signature1</span>, <span class="pl-c1">&</span><span class="pl-s1">signature2</span>) <span class="pl-s1">asyncResults</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">server</span>.<span class="pl-c1">SendGroup</span>(<span class="pl-s1">group</span>, <span class="pl-c1">0</span>) <span class="pl-c">//The second parameter specifies the number of concurrent sending tasks. 0 means unlimited.</span> <span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> { <span class="pl-c">// failed to send the group</span> <span class="pl-c">// do something with the error</span> }</pre></div> <p dir="auto"><code>SendGroup</code> returns a slice of <code>AsyncResult</code> objects. So you can do a blocking call and wait for the result of groups tasks:</p> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="for _, asyncResult := range asyncResults { results, err := asyncResult.Get(time.Duration(time.Millisecond * 5)) if err != nil { // getting result of a task failed // do something with the error } for _, result := range results { fmt.Println(result.Interface()) } }"><pre><span class="pl-k">for</span> <span class="pl-s1">_</span>, <span class="pl-s1">asyncResult</span> <span class="pl-c1">:=</span> <span class="pl-k">range</span> <span class="pl-s1">asyncResults</span> { <span class="pl-s1">results</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">asyncResult</span>.<span class="pl-c1">Get</span>(<span class="pl-s1">time</span>.<span class="pl-c1">Duration</span>(<span class="pl-s1">time</span>.<span class="pl-c1">Millisecond</span> <span class="pl-c1">*</span> <span class="pl-c1">5</span>)) <span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> { <span class="pl-c">// getting result of a task failed</span> <span class="pl-c">// do something with the error</span> } <span class="pl-k">for</span> <span class="pl-s1">_</span>, <span class="pl-s1">result</span> <span class="pl-c1">:=</span> <span class="pl-k">range</span> <span class="pl-s1">results</span> { <span class="pl-s1">fmt</span>.<span class="pl-c1">Println</span>(<span class="pl-s1">result</span>.<span class="pl-c1">Interface</span>()) } }</pre></div> <div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">Chords</h4><a id="user-content-chords" class="anchor" aria-label="Permalink: Chords" href="#chords"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto"><code>Chord</code> allows you to define a callback to be executed after all tasks in a group finished processing, e.g.:</p> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import ( "github.com/RichardKnop/machinery/v1/tasks" "github.com/RichardKnop/machinery/v1" ) signature1 := tasks.Signature{ Name: "add", Args: []tasks.Arg{ { Type: "int64", Value: 1, }, { Type: "int64", Value: 1, }, }, } signature2 := tasks.Signature{ Name: "add", Args: []tasks.Arg{ { Type: "int64", Value: 5, }, { Type: "int64", Value: 5, }, }, } signature3 := tasks.Signature{ Name: "multiply", } group := tasks.NewGroup(&signature1, &signature2) chord, _ := tasks.NewChord(group, &signature3) chordAsyncResult, err := server.SendChord(chord, 0) //The second parameter specifies the number of concurrent sending tasks. 0 means unlimited. if err != nil { // failed to send the chord // do something with the error }"><pre><span class="pl-k">import</span> ( <span class="pl-s">"github.com/RichardKnop/machinery/v1/tasks"</span> <span class="pl-s">"github.com/RichardKnop/machinery/v1"</span> ) <span class="pl-s1">signature1</span> <span class="pl-c1">:=</span> tasks.<span class="pl-smi">Signature</span>{ <span class="pl-s1">Name</span>: <span class="pl-s">"add"</span>, <span class="pl-s1">Args</span>: []tasks.<span class="pl-smi">Arg</span>{ { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">1</span>, }, { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">1</span>, }, }, } <span class="pl-s1">signature2</span> <span class="pl-c1">:=</span> tasks.<span class="pl-smi">Signature</span>{ <span class="pl-s1">Name</span>: <span class="pl-s">"add"</span>, <span class="pl-s1">Args</span>: []tasks.<span class="pl-smi">Arg</span>{ { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">5</span>, }, { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">5</span>, }, }, } <span class="pl-s1">signature3</span> <span class="pl-c1">:=</span> tasks.<span class="pl-smi">Signature</span>{ <span class="pl-s1">Name</span>: <span class="pl-s">"multiply"</span>, } <span class="pl-s1">group</span> <span class="pl-c1">:=</span> <span class="pl-s1">tasks</span>.<span class="pl-c1">NewGroup</span>(<span class="pl-c1">&</span><span class="pl-s1">signature1</span>, <span class="pl-c1">&</span><span class="pl-s1">signature2</span>) <span class="pl-s1">chord</span>, <span class="pl-s1">_</span> <span class="pl-c1">:=</span> <span class="pl-s1">tasks</span>.<span class="pl-c1">NewChord</span>(<span class="pl-s1">group</span>, <span class="pl-c1">&</span><span class="pl-s1">signature3</span>) <span class="pl-s1">chordAsyncResult</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">server</span>.<span class="pl-c1">SendChord</span>(<span class="pl-s1">chord</span>, <span class="pl-c1">0</span>) <span class="pl-c">//The second parameter specifies the number of concurrent sending tasks. 0 means unlimited.</span> <span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> { <span class="pl-c">// failed to send the chord</span> <span class="pl-c">// do something with the error</span> }</pre></div> <p dir="auto">The above example executes task1 and task2 in parallel, aggregates their results and passes them to task3. Therefore what would end up happening is:</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="multiply(add(1, 1), add(5, 5))"><pre class="notranslate"><code>multiply(add(1, 1), add(5, 5)) </code></pre></div> <p dir="auto">More explicitly:</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="(1 + 1) * (5 + 5) = 2 * 10 = 20"><pre class="notranslate"><code>(1 + 1) * (5 + 5) = 2 * 10 = 20 </code></pre></div> <p dir="auto"><code>SendChord</code> returns <code>ChordAsyncResult</code> which follows AsyncResult's interface. So you can do a blocking call and wait for the result of the callback:</p> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="results, err := chordAsyncResult.Get(time.Duration(time.Millisecond * 5)) if err != nil { // getting result of a chord failed // do something with the error } for _, result := range results { fmt.Println(result.Interface()) }"><pre><span class="pl-s1">results</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">chordAsyncResult</span>.<span class="pl-c1">Get</span>(<span class="pl-s1">time</span>.<span class="pl-c1">Duration</span>(<span class="pl-s1">time</span>.<span class="pl-c1">Millisecond</span> <span class="pl-c1">*</span> <span class="pl-c1">5</span>)) <span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> { <span class="pl-c">// getting result of a chord failed</span> <span class="pl-c">// do something with the error</span> } <span class="pl-k">for</span> <span class="pl-s1">_</span>, <span class="pl-s1">result</span> <span class="pl-c1">:=</span> <span class="pl-k">range</span> <span class="pl-s1">results</span> { <span class="pl-s1">fmt</span>.<span class="pl-c1">Println</span>(<span class="pl-s1">result</span>.<span class="pl-c1">Interface</span>()) }</pre></div> <div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">Chains</h4><a id="user-content-chains" class="anchor" aria-label="Permalink: Chains" href="#chains"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto"><code>Chain</code> is simply a set of tasks which will be executed one by one, each successful task triggering the next task in the chain. E.g.:</p> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import ( "github.com/RichardKnop/machinery/v1/tasks" "github.com/RichardKnop/machinery/v1" ) signature1 := tasks.Signature{ Name: "add", Args: []tasks.Arg{ { Type: "int64", Value: 1, }, { Type: "int64", Value: 1, }, }, } signature2 := tasks.Signature{ Name: "add", Args: []tasks.Arg{ { Type: "int64", Value: 5, }, { Type: "int64", Value: 5, }, }, } signature3 := tasks.Signature{ Name: "multiply", Args: []tasks.Arg{ { Type: "int64", Value: 4, }, }, } chain, _ := tasks.NewChain(&signature1, &signature2, &signature3) chainAsyncResult, err := server.SendChain(chain) if err != nil { // failed to send the chain // do something with the error }"><pre><span class="pl-k">import</span> ( <span class="pl-s">"github.com/RichardKnop/machinery/v1/tasks"</span> <span class="pl-s">"github.com/RichardKnop/machinery/v1"</span> ) <span class="pl-s1">signature1</span> <span class="pl-c1">:=</span> tasks.<span class="pl-smi">Signature</span>{ <span class="pl-s1">Name</span>: <span class="pl-s">"add"</span>, <span class="pl-s1">Args</span>: []tasks.<span class="pl-smi">Arg</span>{ { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">1</span>, }, { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">1</span>, }, }, } <span class="pl-s1">signature2</span> <span class="pl-c1">:=</span> tasks.<span class="pl-smi">Signature</span>{ <span class="pl-s1">Name</span>: <span class="pl-s">"add"</span>, <span class="pl-s1">Args</span>: []tasks.<span class="pl-smi">Arg</span>{ { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">5</span>, }, { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">5</span>, }, }, } <span class="pl-s1">signature3</span> <span class="pl-c1">:=</span> tasks.<span class="pl-smi">Signature</span>{ <span class="pl-s1">Name</span>: <span class="pl-s">"multiply"</span>, <span class="pl-s1">Args</span>: []tasks.<span class="pl-smi">Arg</span>{ { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">4</span>, }, }, } <span class="pl-s1">chain</span>, <span class="pl-s1">_</span> <span class="pl-c1">:=</span> <span class="pl-s1">tasks</span>.<span class="pl-c1">NewChain</span>(<span class="pl-c1">&</span><span class="pl-s1">signature1</span>, <span class="pl-c1">&</span><span class="pl-s1">signature2</span>, <span class="pl-c1">&</span><span class="pl-s1">signature3</span>) <span class="pl-s1">chainAsyncResult</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">server</span>.<span class="pl-c1">SendChain</span>(<span class="pl-s1">chain</span>) <span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> { <span class="pl-c">// failed to send the chain</span> <span class="pl-c">// do something with the error</span> }</pre></div> <p dir="auto">The above example executes task1, then task2 and then task3. When a task is completed successfully, the result is appended to the end of list of arguments for the next task in the chain. Therefore what would end up happening is:</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="multiply(4, add(5, 5, add(1, 1)))"><pre class="notranslate"><code>multiply(4, add(5, 5, add(1, 1))) </code></pre></div> <p dir="auto">More explicitly:</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content=" 4 * (5 + 5 + (1 + 1)) # task1: add(1, 1) returns 2 = 4 * (5 + 5 + 2) # task2: add(5, 5, 2) returns 12 = 4 * (12) # task3: multiply(4, 12) returns 48 = 48"><pre class="notranslate"><code> 4 * (5 + 5 + (1 + 1)) # task1: add(1, 1) returns 2 = 4 * (5 + 5 + 2) # task2: add(5, 5, 2) returns 12 = 4 * (12) # task3: multiply(4, 12) returns 48 = 48 </code></pre></div> <p dir="auto"><code>SendChain</code> returns <code>ChainAsyncResult</code> which follows AsyncResult's interface. So you can do a blocking call and wait for the result of the whole chain:</p> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="results, err := chainAsyncResult.Get(time.Duration(time.Millisecond * 5)) if err != nil { // getting result of a chain failed // do something with the error } for _, result := range results { fmt.Println(result.Interface()) }"><pre><span class="pl-s1">results</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">chainAsyncResult</span>.<span class="pl-c1">Get</span>(<span class="pl-s1">time</span>.<span class="pl-c1">Duration</span>(<span class="pl-s1">time</span>.<span class="pl-c1">Millisecond</span> <span class="pl-c1">*</span> <span class="pl-c1">5</span>)) <span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> { <span class="pl-c">// getting result of a chain failed</span> <span class="pl-c">// do something with the error</span> } <span class="pl-k">for</span> <span class="pl-s1">_</span>, <span class="pl-s1">result</span> <span class="pl-c1">:=</span> <span class="pl-k">range</span> <span class="pl-s1">results</span> { <span class="pl-s1">fmt</span>.<span class="pl-c1">Println</span>(<span class="pl-s1">result</span>.<span class="pl-c1">Interface</span>()) }</pre></div> <div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">Periodic Tasks & Workflows</h3><a id="user-content-periodic-tasks--workflows" class="anchor" aria-label="Permalink: Periodic Tasks & Workflows" href="#periodic-tasks--workflows"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">Machinery now supports scheduling periodic tasks and workflows. See examples bellow.</p> <div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">Periodic Tasks</h4><a id="user-content-periodic-tasks" class="anchor" aria-label="Permalink: Periodic Tasks" href="#periodic-tasks"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import ( "github.com/RichardKnop/machinery/v1/tasks" ) signature := &tasks.Signature{ Name: "add", Args: []tasks.Arg{ { Type: "int64", Value: 1, }, { Type: "int64", Value: 1, }, }, } err := server.RegisterPeriodicTask("0 6 * * ?", "periodic-task", signature) if err != nil { // failed to register periodic task }"><pre><span class="pl-k">import</span> ( <span class="pl-s">"github.com/RichardKnop/machinery/v1/tasks"</span> ) <span class="pl-s1">signature</span> <span class="pl-c1">:=</span> <span class="pl-c1">&</span>tasks.<span class="pl-smi">Signature</span>{ <span class="pl-s1">Name</span>: <span class="pl-s">"add"</span>, <span class="pl-s1">Args</span>: []tasks.<span class="pl-smi">Arg</span>{ { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">1</span>, }, { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">1</span>, }, }, } <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">server</span>.<span class="pl-c1">RegisterPeriodicTask</span>(<span class="pl-s">"0 6 * * ?"</span>, <span class="pl-s">"periodic-task"</span>, <span class="pl-s1">signature</span>) <span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> { <span class="pl-c">// failed to register periodic task</span> }</pre></div> <div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">Periodic Groups</h4><a id="user-content-periodic-groups" class="anchor" aria-label="Permalink: Periodic Groups" href="#periodic-groups"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import ( "github.com/RichardKnop/machinery/v1/tasks" "github.com/RichardKnop/machinery/v1" ) signature1 := tasks.Signature{ Name: "add", Args: []tasks.Arg{ { Type: "int64", Value: 1, }, { Type: "int64", Value: 1, }, }, } signature2 := tasks.Signature{ Name: "add", Args: []tasks.Arg{ { Type: "int64", Value: 5, }, { Type: "int64", Value: 5, }, }, } group, _ := tasks.NewGroup(&signature1, &signature2) err := server.RegisterPeriodicGroup("0 6 * * ?", "periodic-group", group) if err != nil { // failed to register periodic group }"><pre><span class="pl-k">import</span> ( <span class="pl-s">"github.com/RichardKnop/machinery/v1/tasks"</span> <span class="pl-s">"github.com/RichardKnop/machinery/v1"</span> ) <span class="pl-s1">signature1</span> <span class="pl-c1">:=</span> tasks.<span class="pl-smi">Signature</span>{ <span class="pl-s1">Name</span>: <span class="pl-s">"add"</span>, <span class="pl-s1">Args</span>: []tasks.<span class="pl-smi">Arg</span>{ { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">1</span>, }, { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">1</span>, }, }, } <span class="pl-s1">signature2</span> <span class="pl-c1">:=</span> tasks.<span class="pl-smi">Signature</span>{ <span class="pl-s1">Name</span>: <span class="pl-s">"add"</span>, <span class="pl-s1">Args</span>: []tasks.<span class="pl-smi">Arg</span>{ { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">5</span>, }, { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">5</span>, }, }, } <span class="pl-s1">group</span>, <span class="pl-s1">_</span> <span class="pl-c1">:=</span> <span class="pl-s1">tasks</span>.<span class="pl-c1">NewGroup</span>(<span class="pl-c1">&</span><span class="pl-s1">signature1</span>, <span class="pl-c1">&</span><span class="pl-s1">signature2</span>) <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">server</span>.<span class="pl-c1">RegisterPeriodicGroup</span>(<span class="pl-s">"0 6 * * ?"</span>, <span class="pl-s">"periodic-group"</span>, <span class="pl-s1">group</span>) <span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> { <span class="pl-c">// failed to register periodic group</span> }</pre></div> <div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">Periodic Chains</h4><a id="user-content-periodic-chains" class="anchor" aria-label="Permalink: Periodic Chains" href="#periodic-chains"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import ( "github.com/RichardKnop/machinery/v1/tasks" "github.com/RichardKnop/machinery/v1" ) signature1 := tasks.Signature{ Name: "add", Args: []tasks.Arg{ { Type: "int64", Value: 1, }, { Type: "int64", Value: 1, }, }, } signature2 := tasks.Signature{ Name: "add", Args: []tasks.Arg{ { Type: "int64", Value: 5, }, { Type: "int64", Value: 5, }, }, } signature3 := tasks.Signature{ Name: "multiply", Args: []tasks.Arg{ { Type: "int64", Value: 4, }, }, } chain, _ := tasks.NewChain(&signature1, &signature2, &signature3) err := server.RegisterPeriodicChain("0 6 * * ?", "periodic-chain", chain) if err != nil { // failed to register periodic chain }"><pre><span class="pl-k">import</span> ( <span class="pl-s">"github.com/RichardKnop/machinery/v1/tasks"</span> <span class="pl-s">"github.com/RichardKnop/machinery/v1"</span> ) <span class="pl-s1">signature1</span> <span class="pl-c1">:=</span> tasks.<span class="pl-smi">Signature</span>{ <span class="pl-s1">Name</span>: <span class="pl-s">"add"</span>, <span class="pl-s1">Args</span>: []tasks.<span class="pl-smi">Arg</span>{ { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">1</span>, }, { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">1</span>, }, }, } <span class="pl-s1">signature2</span> <span class="pl-c1">:=</span> tasks.<span class="pl-smi">Signature</span>{ <span class="pl-s1">Name</span>: <span class="pl-s">"add"</span>, <span class="pl-s1">Args</span>: []tasks.<span class="pl-smi">Arg</span>{ { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">5</span>, }, { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">5</span>, }, }, } <span class="pl-s1">signature3</span> <span class="pl-c1">:=</span> tasks.<span class="pl-smi">Signature</span>{ <span class="pl-s1">Name</span>: <span class="pl-s">"multiply"</span>, <span class="pl-s1">Args</span>: []tasks.<span class="pl-smi">Arg</span>{ { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">4</span>, }, }, } <span class="pl-s1">chain</span>, <span class="pl-s1">_</span> <span class="pl-c1">:=</span> <span class="pl-s1">tasks</span>.<span class="pl-c1">NewChain</span>(<span class="pl-c1">&</span><span class="pl-s1">signature1</span>, <span class="pl-c1">&</span><span class="pl-s1">signature2</span>, <span class="pl-c1">&</span><span class="pl-s1">signature3</span>) <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">server</span>.<span class="pl-c1">RegisterPeriodicChain</span>(<span class="pl-s">"0 6 * * ?"</span>, <span class="pl-s">"periodic-chain"</span>, <span class="pl-s1">chain</span>) <span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> { <span class="pl-c">// failed to register periodic chain</span> }</pre></div> <div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">Chord</h4><a id="user-content-chord" class="anchor" aria-label="Permalink: Chord" href="#chord"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import ( "github.com/RichardKnop/machinery/v1/tasks" "github.com/RichardKnop/machinery/v1" ) signature1 := tasks.Signature{ Name: "add", Args: []tasks.Arg{ { Type: "int64", Value: 1, }, { Type: "int64", Value: 1, }, }, } signature2 := tasks.Signature{ Name: "add", Args: []tasks.Arg{ { Type: "int64", Value: 5, }, { Type: "int64", Value: 5, }, }, } signature3 := tasks.Signature{ Name: "multiply", } group := tasks.NewGroup(&signature1, &signature2) chord, _ := tasks.NewChord(group, &signature3) err := server.RegisterPeriodicChord("0 6 * * ?", "periodic-chord", chord) if err != nil { // failed to register periodic chord }"><pre><span class="pl-k">import</span> ( <span class="pl-s">"github.com/RichardKnop/machinery/v1/tasks"</span> <span class="pl-s">"github.com/RichardKnop/machinery/v1"</span> ) <span class="pl-s1">signature1</span> <span class="pl-c1">:=</span> tasks.<span class="pl-smi">Signature</span>{ <span class="pl-s1">Name</span>: <span class="pl-s">"add"</span>, <span class="pl-s1">Args</span>: []tasks.<span class="pl-smi">Arg</span>{ { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">1</span>, }, { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">1</span>, }, }, } <span class="pl-s1">signature2</span> <span class="pl-c1">:=</span> tasks.<span class="pl-smi">Signature</span>{ <span class="pl-s1">Name</span>: <span class="pl-s">"add"</span>, <span class="pl-s1">Args</span>: []tasks.<span class="pl-smi">Arg</span>{ { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">5</span>, }, { <span class="pl-s1">Type</span>: <span class="pl-s">"int64"</span>, <span class="pl-s1">Value</span>: <span class="pl-c1">5</span>, }, }, } <span class="pl-s1">signature3</span> <span class="pl-c1">:=</span> tasks.<span class="pl-smi">Signature</span>{ <span class="pl-s1">Name</span>: <span class="pl-s">"multiply"</span>, } <span class="pl-s1">group</span> <span class="pl-c1">:=</span> <span class="pl-s1">tasks</span>.<span class="pl-c1">NewGroup</span>(<span class="pl-c1">&</span><span class="pl-s1">signature1</span>, <span class="pl-c1">&</span><span class="pl-s1">signature2</span>) <span class="pl-s1">chord</span>, <span class="pl-s1">_</span> <span class="pl-c1">:=</span> <span class="pl-s1">tasks</span>.<span class="pl-c1">NewChord</span>(<span class="pl-s1">group</span>, <span class="pl-c1">&</span><span class="pl-s1">signature3</span>) <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">server</span>.<span class="pl-c1">RegisterPeriodicChord</span>(<span class="pl-s">"0 6 * * ?"</span>, <span class="pl-s">"periodic-chord"</span>, <span class="pl-s1">chord</span>) <span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> { <span class="pl-c">// failed to register periodic chord</span> }</pre></div> <div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">Development</h3><a id="user-content-development" class="anchor" aria-label="Permalink: Development" href="#development"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">Requirements</h4><a id="user-content-requirements" class="anchor" aria-label="Permalink: Requirements" href="#requirements"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <ul dir="auto"> <li>Go</li> <li>RabbitMQ (optional)</li> <li>Redis</li> <li>Memcached (optional)</li> <li>MongoDB (optional)</li> </ul> <p dir="auto">On OS X systems, you can install requirements using <a href="http://brew.sh/" rel="nofollow">Homebrew</a>:</p> <div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="brew install go brew install rabbitmq brew install redis brew install memcached brew install mongodb"><pre>brew install go brew install rabbitmq brew install redis brew install memcached brew install mongodb</pre></div> <p dir="auto">Or optionally use the corresponding <a href="http://docker.io/" rel="nofollow">Docker</a> containers:</p> <div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="docker run -d -p 5672:5672 rabbitmq docker run -d -p 6379:6379 redis docker run -d -p 11211:11211 memcached docker run -d -p 27017:27017 mongo docker run -d -p 6831:6831/udp -p 16686:16686 jaegertracing/all-in-one:latest"><pre class="notranslate"><code>docker run -d -p 5672:5672 rabbitmq docker run -d -p 6379:6379 redis docker run -d -p 11211:11211 memcached docker run -d -p 27017:27017 mongo docker run -d -p 6831:6831/udp -p 16686:16686 jaegertracing/all-in-one:latest </code></pre></div> <div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">Dependencies</h4><a id="user-content-dependencies" class="anchor" aria-label="Permalink: Dependencies" href="#dependencies"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">Since Go 1.11, a new recommended dependency management system is via <a href="https://github.com/golang/go/wiki/Modules">modules</a>.</p> <p dir="auto">This is one of slight weaknesses of Go as dependency management is not a solved problem. Previously Go was officially recommending to use the <a href="https://github.com/golang/dep">dep tool</a> but that has been abandoned now in favor of modules.</p> <div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">Testing</h4><a id="user-content-testing" class="anchor" aria-label="Permalink: Testing" href="#testing"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div> <p dir="auto">Easiest (and platform agnostic) way to run tests is via <code>docker-compose</code>:</p> <div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="make ci"><pre>make ci</pre></div> <p dir="auto">This will basically run docker-compose command:</p> <div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="(docker-compose -f docker-compose.test.yml -p machinery_ci up --build -d) && (docker logs -f machinery_sut &) && (docker wait machinery_sut)"><pre>(docker-compose -f docker-compose.test.yml -p machinery_ci up --build -d) <span class="pl-k">&&</span> (docker logs -f machinery_sut <span class="pl-k">&</span>) <span class="pl-k">&&</span> (docker <span class="pl-c1">wait</span> machinery_sut)</pre></div> <p dir="auto">Alternative approach is to setup a development environment on your machine.</p> <p dir="auto">In order to enable integration tests, you will need to install all required services (RabbitMQ, Redis, Memcache, MongoDB) and export these environment variables:</p> <div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="export AMQP_URL=amqp://guest:guest@localhost:5672/ export REDIS_URL=localhost:6379 export MEMCACHE_URL=localhost:11211 export MONGODB_URL=localhost:27017"><pre><span class="pl-k">export</span> AMQP_URL=amqp://guest:guest@localhost:5672/ <span class="pl-k">export</span> REDIS_URL=localhost:6379 <span class="pl-k">export</span> MEMCACHE_URL=localhost:11211 <span class="pl-k">export</span> MONGODB_URL=localhost:27017</pre></div> <p dir="auto">To run integration tests against an SQS instance, you will need to create a "test_queue" in SQS and export these environment variables:</p> <div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="export SQS_URL=https://YOUR_SQS_URL export AWS_ACCESS_KEY_ID=YOUR_AWS_ACCESS_KEY_ID export AWS_SECRET_ACCESS_KEY=YOUR_AWS_SECRET_ACCESS_KEY export AWS_DEFAULT_REGION=YOUR_AWS_DEFAULT_REGION"><pre><span class="pl-k">export</span> SQS_URL=https://YOUR_SQS_URL <span class="pl-k">export</span> AWS_ACCESS_KEY_ID=YOUR_AWS_ACCESS_KEY_ID <span class="pl-k">export</span> AWS_SECRET_ACCESS_KEY=YOUR_AWS_SECRET_ACCESS_KEY <span class="pl-k">export</span> AWS_DEFAULT_REGION=YOUR_AWS_DEFAULT_REGION</pre></div> <p dir="auto">Then just run:</p> <div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="make test"><pre>make <span class="pl-c1">test</span></pre></div> <p dir="auto">If the environment variables are not exported, <code>make test</code> will only run unit tests.</p> </article></div></div></div></div></div> <!-- --> <!-- --> <script type="application/json" id="__PRIMER_DATA_:R0:__">{"resolvedServerColorMode":"day"}</script></div> </react-partial> <input type="hidden" data-csrf="true" value="+Ukj786Ozno3ekzcKvQayYA4wTyAPvlhP3ltTCS12efiWf73TQw0hxk9qG8KyixCxydY2B8whx9kLYsfDSSnWA==" /> </div> <div data-view-component="true" class="Layout-sidebar"> <div class="BorderGrid about-margin" data-pjax> <div class="BorderGrid-row"> <div class="BorderGrid-cell"> <div class="hide-sm hide-md"> <h2 class="mb-3 h4">About</h2> <p class="f4 my-3"> Machinery is an asynchronous task queue/job queue based on distributed message passing. </p> <h3 class="sr-only">Topics</h3> <div class="my-3"> <div class="f6"> <a href="/topics/go" title="Topic: go" data-view-component="true" class="topic-tag topic-tag-link"> go </a> <a href="/topics/redis" title="Topic: redis" data-view-component="true" class="topic-tag topic-tag-link"> redis </a> <a href="/topics/golang" title="Topic: golang" data-view-component="true" class="topic-tag topic-tag-link"> golang </a> <a href="/topics/memcached" title="Topic: memcached" data-view-component="true" class="topic-tag topic-tag-link"> memcached </a> <a href="/topics/task" title="Topic: task" data-view-component="true" class="topic-tag topic-tag-link"> task </a> <a href="/topics/mongodb" title="Topic: mongodb" data-view-component="true" class="topic-tag topic-tag-link"> mongodb </a> <a href="/topics/queue" title="Topic: queue" data-view-component="true" class="topic-tag topic-tag-link"> queue </a> <a href="/topics/rabbitmq" title="Topic: rabbitmq" data-view-component="true" class="topic-tag topic-tag-link"> rabbitmq </a> <a href="/topics/amqp" title="Topic: amqp" data-view-component="true" class="topic-tag topic-tag-link"> amqp </a> <a href="/topics/aws-sqs" title="Topic: aws-sqs" data-view-component="true" class="topic-tag topic-tag-link"> aws-sqs </a> <a href="/topics/task-scheduler" title="Topic: task-scheduler" data-view-component="true" class="topic-tag topic-tag-link"> task-scheduler </a> </div> </div> <h3 class="sr-only">Resources</h3> <div class="mt-2"> <a class="Link--muted" data-analytics-event="{"category":"Repository Overview","action":"click","label":"location:sidebar;file:readme"}" href="#readme-ov-file"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-book mr-2"> <path d="M0 1.75A.75.75 0 0 1 .75 1h4.253c1.227 0 2.317.59 3 1.501A3.743 3.743 0 0 1 11.006 1h4.245a.75.75 0 0 1 .75.75v10.5a.75.75 0 0 1-.75.75h-4.507a2.25 2.25 0 0 0-1.591.659l-.622.621a.75.75 0 0 1-1.06 0l-.622-.621A2.25 2.25 0 0 0 5.258 13H.75a.75.75 0 0 1-.75-.75Zm7.251 10.324.004-5.073-.002-2.253A2.25 2.25 0 0 0 5.003 2.5H1.5v9h3.757a3.75 3.75 0 0 1 1.994.574ZM8.755 4.75l-.004 7.322a3.752 3.752 0 0 1 1.992-.572H14.5v-9h-3.495a2.25 2.25 0 0 0-2.25 2.25Z"></path> </svg> Readme </a> </div> <h3 class="sr-only">License</h3> <div class="mt-2"> <a href="#MPL-2.0-1-ov-file" class="Link--muted" data-analytics-event="{"category":"Repository Overview","action":"click","label":"location:sidebar;file:license"}" > <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-law mr-2"> <path d="M8.75.75V2h.985c.304 0 .603.08.867.231l1.29.736c.038.022.08.033.124.033h2.234a.75.75 0 0 1 0 1.5h-.427l2.111 4.692a.75.75 0 0 1-.154.838l-.53-.53.529.531-.001.002-.002.002-.006.006-.006.005-.01.01-.045.04c-.21.176-.441.327-.686.45C14.556 10.78 13.88 11 13 11a4.498 4.498 0 0 1-2.023-.454 3.544 3.544 0 0 1-.686-.45l-.045-.04-.016-.015-.006-.006-.004-.004v-.001a.75.75 0 0 1-.154-.838L12.178 4.5h-.162c-.305 0-.604-.079-.868-.231l-1.29-.736a.245.245 0 0 0-.124-.033H8.75V13h2.5a.75.75 0 0 1 0 1.5h-6.5a.75.75 0 0 1 0-1.5h2.5V3.5h-.984a.245.245 0 0 0-.124.033l-1.289.737c-.265.15-.564.23-.869.23h-.162l2.112 4.692a.75.75 0 0 1-.154.838l-.53-.53.529.531-.001.002-.002.002-.006.006-.016.015-.045.04c-.21.176-.441.327-.686.45C4.556 10.78 3.88 11 3 11a4.498 4.498 0 0 1-2.023-.454 3.544 3.544 0 0 1-.686-.45l-.045-.04-.016-.015-.006-.006-.004-.004v-.001a.75.75 0 0 1-.154-.838L2.178 4.5H1.75a.75.75 0 0 1 0-1.5h2.234a.249.249 0 0 0 .125-.033l1.288-.737c.265-.15.564-.23.869-.23h.984V.75a.75.75 0 0 1 1.5 0Zm2.945 8.477c.285.135.718.273 1.305.273s1.02-.138 1.305-.273L13 6.327Zm-10 0c.285.135.718.273 1.305.273s1.02-.138 1.305-.273L3 6.327Z"></path> </svg> MPL-2.0 license </a> </div> <include-fragment src="/RichardKnop/machinery/hovercards/citation/sidebar_partial?tree_name=master"> </include-fragment> <div class="mt-2"> <a href="/RichardKnop/machinery/activity" data-view-component="true" class="Link Link--muted"><svg text="gray" aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-pulse mr-2"> <path d="M6 2c.306 0 .582.187.696.471L10 10.731l1.304-3.26A.751.751 0 0 1 12 7h3.25a.75.75 0 0 1 0 1.5h-2.742l-1.812 4.528a.751.751 0 0 1-1.392 0L6 4.77 4.696 8.03A.75.75 0 0 1 4 8.5H.75a.75.75 0 0 1 0-1.5h2.742l1.812-4.529A.751.751 0 0 1 6 2Z"></path> </svg> <span class="color-fg-muted">Activity</span></a> </div> <h3 class="sr-only">Stars</h3> <div class="mt-2"> <a href="/RichardKnop/machinery/stargazers" data-view-component="true" class="Link Link--muted"><svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-star mr-2"> <path d="M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Zm0 2.445L6.615 5.5a.75.75 0 0 1-.564.41l-3.097.45 2.24 2.184a.75.75 0 0 1 .216.664l-.528 3.084 2.769-1.456a.75.75 0 0 1 .698 0l2.77 1.456-.53-3.084a.75.75 0 0 1 .216-.664l2.24-2.183-3.096-.45a.75.75 0 0 1-.564-.41L8 2.694Z"></path> </svg> <strong>7.7k</strong> stars</a> </div> <h3 class="sr-only">Watchers</h3> <div class="mt-2"> <a href="/RichardKnop/machinery/watchers" data-view-component="true" class="Link Link--muted"><svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-eye mr-2"> <path d="M8 2c1.981 0 3.671.992 4.933 2.078 1.27 1.091 2.187 2.345 2.637 3.023a1.62 1.62 0 0 1 0 1.798c-.45.678-1.367 1.932-2.637 3.023C11.67 13.008 9.981 14 8 14c-1.981 0-3.671-.992-4.933-2.078C1.797 10.83.88 9.576.43 8.898a1.62 1.62 0 0 1 0-1.798c.45-.677 1.367-1.931 2.637-3.022C4.33 2.992 6.019 2 8 2ZM1.679 7.932a.12.12 0 0 0 0 .136c.411.622 1.241 1.75 2.366 2.717C5.176 11.758 6.527 12.5 8 12.5c1.473 0 2.825-.742 3.955-1.715 1.124-.967 1.954-2.096 2.366-2.717a.12.12 0 0 0 0-.136c-.412-.621-1.242-1.75-2.366-2.717C10.824 4.242 9.473 3.5 8 3.5c-1.473 0-2.825.742-3.955 1.715-1.124.967-1.954 2.096-2.366 2.717ZM8 10a2 2 0 1 1-.001-3.999A2 2 0 0 1 8 10Z"></path> </svg> <strong>162</strong> watching</a> </div> <h3 class="sr-only">Forks</h3> <div class="mt-2"> <a href="/RichardKnop/machinery/forks" data-view-component="true" class="Link Link--muted"><svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-repo-forked mr-2"> <path d="M5 5.372v.878c0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75v-.878a2.25 2.25 0 1 1 1.5 0v.878a2.25 2.25 0 0 1-2.25 2.25h-1.5v2.128a2.251 2.251 0 1 1-1.5 0V8.5h-1.5A2.25 2.25 0 0 1 3.5 6.25v-.878a2.25 2.25 0 1 1 1.5 0ZM5 3.25a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Zm6.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm-3 8.75a.75.75 0 1 0-1.5 0 .75.75 0 0 0 1.5 0Z"></path> </svg> <strong>929</strong> forks</a> </div> <div class="mt-2"> <a class="Link--muted" href="/contact/report-content?content_url=https%3A%2F%2Fgithub.com%2FRichardKnop%2Fmachinery&report=RichardKnop+%28user%29"> Report repository </a> </div> </div> </div> </div> <div class="BorderGrid-row"> <div class="BorderGrid-cell"> <h2 class="h4 mb-3" data-pjax="#repo-content-pjax-container" data-turbo-frame="repo-content-turbo-frame"> <a href="/RichardKnop/machinery/releases" data-view-component="true" class="Link--primary no-underline Link">Releases <span title="101" data-view-component="true" class="Counter">101</span></a></h2> <a class="Link--primary d-flex no-underline" data-pjax="#repo-content-pjax-container" data-turbo-frame="repo-content-turbo-frame" href="/RichardKnop/machinery/releases/tag/v2.0.13"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-tag flex-shrink-0 mt-1 color-fg-success"> <path d="M1 7.775V2.75C1 1.784 1.784 1 2.75 1h5.025c.464 0 .91.184 1.238.513l6.25 6.25a1.75 1.75 0 0 1 0 2.474l-5.026 5.026a1.75 1.75 0 0 1-2.474 0l-6.25-6.25A1.752 1.752 0 0 1 1 7.775Zm1.5 0c0 .066.026.13.073.177l6.25 6.25a.25.25 0 0 0 .354 0l5.025-5.025a.25.25 0 0 0 0-.354l-6.25-6.25a.25.25 0 0 0-.177-.073H2.75a.25.25 0 0 0-.25.25ZM6 5a1 1 0 1 1 0 2 1 1 0 0 1 0-2Z"></path> </svg> <div class="ml-2 min-width-0"> <div class="d-flex"> <span class="css-truncate css-truncate-target text-bold mr-2" style="max-width: none;">v2.0.13</span> <span title="Label: Latest" data-view-component="true" class="Label Label--success flex-shrink-0"> Latest </span> </div> <div class="text-small color-fg-muted"><relative-time datetime="2024-03-19T09:53:10Z" class="no-wrap">Mar 19, 2024</relative-time></div> </div> </a> <div data-view-component="true" class="mt-3"> <a text="small" data-pjax="#repo-content-pjax-container" data-turbo-frame="repo-content-turbo-frame" href="/RichardKnop/machinery/releases" data-view-component="true" class="Link">+ 100 releases</a></div> </div> </div> <div class="BorderGrid-row"> <div class="BorderGrid-cell"> <h2 class="h4 mb-3"> <a href="/users/RichardKnop/packages?repo_name=machinery" data-view-component="true" class="Link--primary no-underline Link d-flex flex-items-center">Packages <span title="0" hidden="hidden" data-view-component="true" class="Counter ml-1">0</span></a></h2> <div class="text-small color-fg-muted" > No packages published <br> </div> </div> </div> <div class="BorderGrid-row" > <div class="BorderGrid-cell"> <h2 class="h4 mb-3"> <a href="/RichardKnop/machinery/network/dependents" data-view-component="true" class="Link--primary no-underline Link">Used by <span title="446" data-view-component="true" class="Counter">446</span></a> </h2> <a class="d-flex flex-items-center" href="/RichardKnop/machinery/network/dependents"> <ul class="hx_flex-avatar-stack list-style-none min-width-0"> <li class="hx_flex-avatar-stack-item"> <img class="avatar avatar-user" src="https://avatars.githubusercontent.com/u/185935032?s=64&v=4" width="32" height="32" alt="@dineshit27" /> </li> <li class="hx_flex-avatar-stack-item"> <img class="avatar avatar-user" src="https://avatars.githubusercontent.com/u/37145046?s=64&v=4" width="32" height="32" alt="@aaaAlexanderaaa" /> </li> <li class="hx_flex-avatar-stack-item"> <img class="avatar avatar-user" src="https://avatars.githubusercontent.com/u/199821527?s=64&v=4" width="32" height="32" alt="@monikasb16" /> </li> <li class="hx_flex-avatar-stack-item"> <img class="avatar avatar-user" src="https://avatars.githubusercontent.com/u/6177511?s=64&v=4" width="32" height="32" alt="@rp01" /> </li> <li class="hx_flex-avatar-stack-item"> <img class="avatar" src="https://avatars.githubusercontent.com/u/191473063?s=64&v=4" width="32" height="32" alt="@zenanet-network" /> </li> <li class="hx_flex-avatar-stack-item"> <img class="avatar avatar-user" src="https://avatars.githubusercontent.com/u/199118120?s=64&v=4" width="32" height="32" alt="@viswak17" /> </li> <li class="hx_flex-avatar-stack-item"> <img class="avatar avatar-user" src="https://avatars.githubusercontent.com/u/192694322?s=64&v=4" width="32" height="32" alt="@Naheed-Rayhan" /> </li> <li class="hx_flex-avatar-stack-item"> <img class="avatar avatar-user" src="https://avatars.githubusercontent.com/u/114429894?s=64&v=4" width="32" height="32" alt="@salahhazaea" /> </li> </ul> <span class="px-2 text-bold text-small no-wrap"> + 438 </span> </a> </div> </div> <div class="BorderGrid-row"> <div class="BorderGrid-cell"> <h2 class="h4 mb-3"> <a href="/RichardKnop/machinery/graphs/contributors" data-view-component="true" class="Link--primary no-underline Link d-flex flex-items-center">Contributors <span title="121" data-view-component="true" class="Counter ml-1">121</span></a></h2> <ul class="list-style-none d-flex flex-wrap mb-n2"> <li class="mb-2 mr-2" > <a href="https://github.com/RichardKnop" class="" data-hovercard-type="user" data-hovercard-url="/users/RichardKnop/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" > <img src="https://avatars.githubusercontent.com/u/2325888?s=64&v=4" alt="@RichardKnop" size="32" height="32" width="32" data-view-component="true" class="avatar circle" /> </a> </li> <li class="mb-2 mr-2" > <a href="https://github.com/ZhenhangTung" class="" data-hovercard-type="user" data-hovercard-url="/users/ZhenhangTung/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" > <img src="https://avatars.githubusercontent.com/u/8170159?s=64&v=4" alt="@ZhenhangTung" size="32" height="32" width="32" data-view-component="true" class="avatar circle" /> </a> </li> <li class="mb-2 mr-2" > <a href="https://github.com/leefernandes" class="" data-hovercard-type="user" data-hovercard-url="/users/leefernandes/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" > <img src="https://avatars.githubusercontent.com/u/1720194?s=64&v=4" alt="@leefernandes" size="32" height="32" width="32" data-view-component="true" class="avatar circle" /> </a> </li> <li class="mb-2 mr-2" > <a href="https://github.com/mjetpax" class="" data-hovercard-type="user" data-hovercard-url="/users/mjetpax/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" > <img src="https://avatars.githubusercontent.com/u/9419898?s=64&v=4" alt="@mjetpax" size="32" height="32" width="32" data-view-component="true" class="avatar circle" /> </a> </li> <li class="mb-2 mr-2" > <a href="https://github.com/charleswhchan" class="" data-hovercard-type="user" data-hovercard-url="/users/charleswhchan/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" > <img src="https://avatars.githubusercontent.com/u/679475?s=64&v=4" alt="@charleswhchan" size="32" height="32" width="32" data-view-component="true" class="avatar circle" /> </a> </li> <li class="mb-2 mr-2" > <a href="https://github.com/surendratiwari3" class="" data-hovercard-type="user" data-hovercard-url="/users/surendratiwari3/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" > <img src="https://avatars.githubusercontent.com/u/6749521?s=64&v=4" alt="@surendratiwari3" size="32" height="32" width="32" data-view-component="true" class="avatar circle" /> </a> </li> <li class="mb-2 mr-2" > <a href="https://github.com/rio" class="" data-hovercard-type="user" data-hovercard-url="/users/rio/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" > <img src="https://avatars.githubusercontent.com/u/883894?s=64&v=4" alt="@rio" size="32" height="32" width="32" data-view-component="true" class="avatar circle" /> </a> </li> <li class="mb-2 mr-2" > <a href="https://github.com/PyYoshi" class="" data-hovercard-type="user" data-hovercard-url="/users/PyYoshi/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" > <img src="https://avatars.githubusercontent.com/u/467255?s=64&v=4" alt="@PyYoshi" size="32" height="32" width="32" data-view-component="true" class="avatar circle" /> </a> </li> <li class="mb-2 mr-2" > <a href="https://github.com/owenhaynes" class="" data-hovercard-type="user" data-hovercard-url="/users/owenhaynes/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" > <img src="https://avatars.githubusercontent.com/u/1472893?s=64&v=4" alt="@owenhaynes" size="32" height="32" width="32" data-view-component="true" class="avatar circle" /> </a> </li> <li class="mb-2 mr-2" > <a href="https://github.com/dmibor" class="" data-hovercard-type="user" data-hovercard-url="/users/dmibor/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" > <img src="https://avatars.githubusercontent.com/u/18489372?s=64&v=4" alt="@dmibor" size="32" height="32" width="32" data-view-component="true" class="avatar circle" /> </a> </li> <li class="mb-2 mr-2" > <a href="https://github.com/gow" class="" data-hovercard-type="user" data-hovercard-url="/users/gow/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" > <img src="https://avatars.githubusercontent.com/u/1923995?s=64&v=4" alt="@gow" size="32" height="32" width="32" data-view-component="true" class="avatar circle" /> </a> </li> <li class="mb-2 mr-2" > <a href="https://github.com/shivanshgaur" class="" data-hovercard-type="user" data-hovercard-url="/users/shivanshgaur/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" > <img src="https://avatars.githubusercontent.com/u/3778943?s=64&v=4" alt="@shivanshgaur" size="32" height="32" width="32" data-view-component="true" class="avatar circle" /> </a> </li> <li class="mb-2 mr-2" > <a href="https://github.com/WillAbides" class="" data-hovercard-type="user" data-hovercard-url="/users/WillAbides/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" > <img src="https://avatars.githubusercontent.com/u/233500?s=64&v=4" alt="@WillAbides" size="32" height="32" width="32" data-view-component="true" class="avatar circle" /> </a> </li> <li class="mb-2 mr-2" > <a href="https://github.com/ConnorDoyle" class="" data-hovercard-type="user" data-hovercard-url="/users/ConnorDoyle/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" > <img src="https://avatars.githubusercontent.com/u/379372?s=64&v=4" alt="@ConnorDoyle" size="32" height="32" width="32" data-view-component="true" class="avatar circle" /> </a> </li> </ul> <div data-view-component="true" class="mt-3"> <a text="small" href="/RichardKnop/machinery/graphs/contributors" data-view-component="true" class="Link--inTextBlock Link">+ 107 contributors</a></div> </div> </div> <div class="BorderGrid-row"> <div class="BorderGrid-cell"> <h2 class="h4 mb-3">Languages</h2> <div class="mb-2"> <span data-view-component="true" class="Progress"> <span style="background-color:#00ADD8 !important;;width: 99.4%;" itemprop="keywords" data-view-component="true" class="Progress-item color-bg-success-emphasis"></span> <span style="background-color:#ededed !important;;width: 0.6%;" itemprop="keywords" data-view-component="true" class="Progress-item color-bg-success-emphasis"></span> </span></div> <ul class="list-style-none"> <li class="d-inline"> <a class="d-inline-flex flex-items-center flex-nowrap Link--secondary no-underline text-small mr-3" href="/RichardKnop/machinery/search?l=go" data-ga-click="Repository, language stats search click, location:repo overview"> <svg style="color:#00ADD8;" aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-dot-fill mr-2"> <path d="M8 4a4 4 0 1 1 0 8 4 4 0 0 1 0-8Z"></path> </svg> <span class="color-fg-default text-bold mr-1">Go</span> <span>99.4%</span> </a> </li> <li class="d-inline"> <span class="d-inline-flex flex-items-center flex-nowrap text-small mr-3"> <svg style="color:#ededed;" aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-dot-fill mr-2"> <path d="M8 4a4 4 0 1 1 0 8 4 4 0 0 1 0-8Z"></path> </svg> <span class="color-fg-default text-bold mr-1">Other</span> <span>0.6%</span> </span> </li> </ul> </div> </div> </div> </div> </div></div> </div> </div> </turbo-frame> </main> </div> </div> <footer class="footer pt-8 pb-6 f6 color-fg-muted p-responsive" role="contentinfo" > <h2 class='sr-only'>Footer</h2> <div class="d-flex flex-justify-center flex-items-center flex-column-reverse flex-lg-row flex-wrap flex-lg-nowrap"> <div class="d-flex flex-items-center flex-shrink-0 mx-2"> <a aria-label="Homepage" title="GitHub" class="footer-octicon mr-2" href="https://github.com"> <svg aria-hidden="true" height="24" viewBox="0 0 24 24" version="1.1" width="24" data-view-component="true" class="octicon octicon-mark-github"> <path d="M12 1C5.9225 1 1 5.9225 1 12C1 16.8675 4.14875 20.9787 8.52125 22.4362C9.07125 22.5325 9.2775 22.2025 9.2775 21.9137C9.2775 21.6525 9.26375 20.7862 9.26375 19.865C6.5 20.3737 5.785 19.1912 5.565 18.5725C5.44125 18.2562 4.905 17.28 4.4375 17.0187C4.0525 16.8125 3.5025 16.3037 4.42375 16.29C5.29 16.2762 5.90875 17.0875 6.115 17.4175C7.105 19.0812 8.68625 18.6137 9.31875 18.325C9.415 17.61 9.70375 17.1287 10.02 16.8537C7.5725 16.5787 5.015 15.63 5.015 11.4225C5.015 10.2262 5.44125 9.23625 6.1425 8.46625C6.0325 8.19125 5.6475 7.06375 6.2525 5.55125C6.2525 5.55125 7.17375 5.2625 9.2775 6.67875C10.1575 6.43125 11.0925 6.3075 12.0275 6.3075C12.9625 6.3075 13.8975 6.43125 14.7775 6.67875C16.8813 5.24875 17.8025 5.55125 17.8025 5.55125C18.4075 7.06375 18.0225 8.19125 17.9125 8.46625C18.6138 9.23625 19.04 10.2125 19.04 11.4225C19.04 15.6437 16.4688 16.5787 14.0213 16.8537C14.42 17.1975 14.7638 17.8575 14.7638 18.8887C14.7638 20.36 14.75 21.5425 14.75 21.9137C14.75 22.2025 14.9563 22.5462 15.5063 22.4362C19.8513 20.9787 23 16.8537 23 12C23 5.9225 18.0775 1 12 1Z"></path> </svg> </a> <span> © 2025 GitHub, Inc. </span> </div> <nav aria-label="Footer"> <h3 class="sr-only" id="sr-footer-heading">Footer navigation</h3> <ul class="list-style-none d-flex flex-justify-center flex-wrap mb-2 mb-lg-0" aria-labelledby="sr-footer-heading"> <li class="mx-2"> <a data-analytics-event="{"category":"Footer","action":"go to Terms","label":"text:terms"}" href="https://docs.github.com/site-policy/github-terms/github-terms-of-service" data-view-component="true" class="Link--secondary Link">Terms</a> </li> <li class="mx-2"> <a data-analytics-event="{"category":"Footer","action":"go to privacy","label":"text:privacy"}" href="https://docs.github.com/site-policy/privacy-policies/github-privacy-statement" data-view-component="true" class="Link--secondary Link">Privacy</a> </li> <li class="mx-2"> <a data-analytics-event="{"category":"Footer","action":"go to security","label":"text:security"}" href="https://github.com/security" data-view-component="true" class="Link--secondary Link">Security</a> </li> <li class="mx-2"> <a data-analytics-event="{"category":"Footer","action":"go to status","label":"text:status"}" href="https://www.githubstatus.com/" data-view-component="true" class="Link--secondary Link">Status</a> </li> <li class="mx-2"> <a data-analytics-event="{"category":"Footer","action":"go to docs","label":"text:docs"}" href="https://docs.github.com/" data-view-component="true" class="Link--secondary Link">Docs</a> </li> <li class="mx-2"> <a data-analytics-event="{"category":"Footer","action":"go to contact","label":"text:contact"}" href="https://support.github.com?tags=dotcom-footer" data-view-component="true" class="Link--secondary Link">Contact</a> </li> <li class="mx-2" > <cookie-consent-link> <button type="button" class="Link--secondary underline-on-hover border-0 p-0 color-bg-transparent" data-action="click:cookie-consent-link#showConsentManagement" data-analytics-event="{"location":"footer","action":"cookies","context":"subfooter","tag":"link","label":"cookies_link_subfooter_footer"}" > Manage cookies </button> </cookie-consent-link> </li> <li class="mx-2"> <cookie-consent-link> <button type="button" class="Link--secondary underline-on-hover border-0 p-0 color-bg-transparent" data-action="click:cookie-consent-link#showConsentManagement" data-analytics-event="{"location":"footer","action":"dont_share_info","context":"subfooter","tag":"link","label":"dont_share_info_link_subfooter_footer"}" > Do not share my personal information </button> </cookie-consent-link> </li> </ul> </nav> </div> </footer> <ghcc-consent id="ghcc" class="position-fixed bottom-0 left-0" style="z-index: 999999" data-initial-cookie-consent-allowed="" data-cookie-consent-required="false"></ghcc-consent> <div id="ajax-error-message" class="ajax-error-message flash flash-error" hidden> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-alert"> <path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path> </svg> <button type="button" class="flash-close js-ajax-error-dismiss" aria-label="Dismiss error"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x"> <path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"></path> </svg> </button> You can’t perform that action at this time. </div> <template id="site-details-dialog"> <details class="details-reset details-overlay details-overlay-dark lh-default color-fg-default hx_rsm" open> <summary role="button" aria-label="Close dialog"></summary> <details-dialog class="Box Box--overlay d-flex flex-column anim-fade-in fast hx_rsm-dialog hx_rsm-modal"> <button class="Box-btn-octicon m-0 btn-octicon position-absolute right-0 top-0" type="button" aria-label="Close dialog" data-close-dialog> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x"> <path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"></path> </svg> </button> <div class="octocat-spinner my-6 js-details-dialog-spinner"></div> </details-dialog> </details> </template> <div class="Popover js-hovercard-content position-absolute" style="display: none; outline: none;"> <div class="Popover-message Popover-message--bottom-left Popover-message--large Box color-shadow-large" style="width:360px;"> </div> </div> <template id="snippet-clipboard-copy-button"> <div class="zeroclipboard-container position-absolute right-0 top-0"> <clipboard-copy aria-label="Copy" class="ClipboardButton btn js-clipboard-copy m-2 p-0" data-copy-feedback="Copied!" data-tooltip-direction="w"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-copy js-clipboard-copy-icon m-2"> <path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path> </svg> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-check js-clipboard-check-icon color-fg-success d-none m-2"> <path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"></path> </svg> </clipboard-copy> </div> </template> <template id="snippet-clipboard-copy-button-unpositioned"> <div class="zeroclipboard-container"> <clipboard-copy aria-label="Copy" class="ClipboardButton btn btn-invisible js-clipboard-copy m-2 p-0 d-flex flex-justify-center flex-items-center" data-copy-feedback="Copied!" data-tooltip-direction="w"> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-copy js-clipboard-copy-icon"> <path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path> </svg> <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-check js-clipboard-check-icon color-fg-success d-none"> <path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"></path> </svg> </clipboard-copy> </div> </template> </div> <div id="js-global-screen-reader-notice" class="sr-only mt-n1" aria-live="polite" aria-atomic="true" ></div> <div id="js-global-screen-reader-notice-assertive" class="sr-only mt-n1" aria-live="assertive" aria-atomic="true"></div> </body> </html>