{"id":18104101,"url":"https://github.com/tuwukee/jiggler","last_synced_at":"2026-03-07T08:02:37.517Z","repository":{"id":60499133,"uuid":"541691261","full_name":"tuwukee/jiggler","owner":"tuwukee","description":"Ruby Async background job processor","archived":false,"fork":false,"pushed_at":"2023-02-13T21:06:51.000Z","size":237,"stargazers_count":97,"open_issues_count":1,"forks_count":1,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-08-15T16:16:48.190Z","etag":null,"topics":["redis","ruby"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tuwukee.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-09-26T16:54:37.000Z","updated_at":"2025-05-10T23:43:38.000Z","dependencies_parsed_at":"2022-09-30T12:50:14.252Z","dependency_job_id":null,"html_url":"https://github.com/tuwukee/jiggler","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/tuwukee/jiggler","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tuwukee%2Fjiggler","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tuwukee%2Fjiggler/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tuwukee%2Fjiggler/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tuwukee%2Fjiggler/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tuwukee","download_url":"https://codeload.github.com/tuwukee/jiggler/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tuwukee%2Fjiggler/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271278787,"owners_count":24731900,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-20T02:00:09.606Z","response_time":69,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["redis","ruby"],"created_at":"2024-10-31T22:13:55.460Z","updated_at":"2026-03-07T08:02:32.486Z","avatar_url":"https://github.com/tuwukee.png","language":"Ruby","readme":"# jiggler\n[![Gem Version](https://badge.fury.io/rb/jiggler.svg)](https://badge.fury.io/rb/jiggler)\n\nBackground job processor based on Socketry Async\n\nJiggler is a [Sidekiq](https://github.com/mperham/sidekiq)-inspired background job processor using [Socketry Async](https://github.com/socketry/async) and [Optimized JSON](https://github.com/ohler55/oj). It uses fibers to processes jobs, making context switching lightweight. Requires Ruby 3+, Redis 6+.\n\n*Jiggler is based on Sidekiq implementation, and re-uses most of its concepts and ideas.*\n\n**NOTE**: Jiggler is a small gem made purely for fun and to gain some hand-on experience with async and fibers. It isn't tested with production projects and might have not-yet-discovered issues. Use at your own risk. \\\nHowever, it's good to play around and/or to try it in the name of science.\n\n### Installation\n\nInstall the gem:\n```\ngem install jiggler\n```\n\nStart Jiggler server as a separate process with bin command:\n```\njiggler -r \u003cFILE_PATH\u003e\n```\n`-r` specifies a file with loading instructions. \\\nFor Rails apps the command'll be `jiggler -r ./config/environment.rb`\n\nRun `jiggler --help` to see the list of command line arguments.\n\n### Performance\n\n[Jiggler 0.1.0rc4 performance results (at most once delivery) against sidekiq 7.0.3](/docs/perf_results_0.1.0rc4.md) \\\n[Jiggler 0.1.0 performance results (at least once delivery) against (at most once delivery)](/docs/perf_results_0.1.0.md)\n\n### Getting Started\n\nConceptually Jiggler consists of two parts: the `client` and the `server`. \\\nThe `client` is responsible for pushing jobs to `Redis` and allows to read stats, while the `server` reads jobs from `Redis`, processes them, and writes stats.\n\nThe `server` uses async `Redis` connections. \\\nThe `client` on default is `sync`. It's possible to configure the client to be async as well via setting `client_async` to `true`. \\\nClient settings are:\n- `client_concurrency`\n- `client_async`\n- `redis_url` (this one is shared with the `server`)\n\nThe rest of the settings are `server` specific. \n\n**NOTE**: `require \"jiggler\"` loads only client classes. It doesn't include `async` lib, this dependency is being required only within the `server` part.\n\n```ruby\nrequire \"jiggler\"\n\nJiggler.configure do |config|\n  config[:client_concurrency] = 12        # Should equal to the number of threads/fibers in the client app. Defaults to 10\n  config[:concurrency] = 12               # The number of running workers on the server. Defaults to 10\n  config[:timeout]     = 12               # Seconds Jiggler wait for jobs to finish before shutdown. Defaults to 25\n  config[:environment] = \"myenv\"          # On default fetches the value ENV[\"APP_ENV\"] and fallbacks to \"development\"\n  config[:require]     = \"./jobs.rb\"      # Path to file with jobs/app initializer\n  config[:redis_url]   = ENV[\"REDIS_URL\"] # On default fetches the value from ENV[\"REDIS_URL\"]\n  config[:queues]      = [\"shippers\"]     # An array of queue names the server is going to listen to. On default uses [\"default\"]\n  config[:config_file] = \"./jiggler.yml\"  # .yml file with Jiggler settings\n  config[:mode]        = :at_most_once    # at_most_once and at_least_once modes supported. Defaults to :at_least_once\nend\n```\n\n`at_least_once` mode grants reliability for the regular enqueued jobs which are going to be executed by workers. The scheduled jobs (the ones planned to be executed at a specific time, or which failed and going to be retried) still support only `at_most_once` strategy. The support for them is going to be added in the upcoming versions.\n\nOn default all queues have the same priority (equals to 0). Higher number means higher prio. \\\nIt's possible to specify custom priorities as follows:\n\n```ruby\nJiggler.configure do |config|\n  config[:queues] = [[\"shippers\", 0], [\"shipments\", 1], [\"delivery\", 2]]\nend\n```\n\n#### IO Event selector\n\n`IO_EVENT_SELECTOR` is an env variable which allows to specify the event selector used by the Ruby scheduler. \\\nOn default it uses `Epoll` (`IO_EVENT_SELECTOR=EPoll`). \\\nAnother available option is `URing` (`IO_EVENT_SELECTOR=URing`). Underneath it uses `io_uring` library. It is a Linux kernel library that provides a high-performance interface for asynchronous I/O operations. It was introduced in Linux kernel version 5.1 and aims to address some of the limitations and scalability issues of the existing AIO (Asynchronous I/O) interface.\nIn the future it might bring a lot of performance boost into Ruby fibers world (once `async` project fully adopts it), but at the moment in the most cases its performance is similar to `EPoll`, yet it could give some boost with File IO.\n\n#### Socketry stack\n\nThe gem allows to use libs/calls from `socketry` stack (https://github.com/socketry) within workers. \\\nSample:\n\n```ruby\ndef perform(ids)\n  resources = Resource.where(id: ids)\n  Async do\n    resources.each do |resource|\n      Async do\n        result = api_client.get(resource)\n        resource.update(data: result) if result\n      rescue =\u003e err\n        logger.error(err)\n      end\n    end\n  end\nend\n```\n\n#### Core components\n\nInternally Jiggler `server` among others includes the next entities: `Manager`, `Poller`, `Monitor`. \\\n`Manager` is responsible for workers. \\\n`Poller` fetches data for retries and scheduled jobs. \\\n`Monitor` periodically loads stats data into redis. \\\n`Manager` and `Monitor` are mandatory, while `Poller` can be disabled in case there's no need for retries/scheduled jobs.\n\n```ruby\nJiggler.configure do |config|\n  config[:stats_interval] = 12   # Defaults to 10\n  config[:poller_enabled] = true # Defaults to true\n  config[:poll_interval]  = 12   # Defaults to 5\nend\n```\n\n`Jiggler::Web.new` is a rack application. It can be run on its own or be mounted in app routes, f.e. with Rails:\n\n```ruby\nrequire \"jiggler/web\"\n\nRails.application.routes.draw do\n  mount Jiggler::Web.new =\u003e \"/jiggler\"\n\n  # ...\nend\n```\n\nTo get the available stats run:\n```ruby\nirb(main)\u003e Jiggler.summary\n=\u003e \n{\"retry_jobs_count\"=\u003e0,\n \"dead_jobs_count\"=\u003e0,\n \"scheduled_jobs_count\"=\u003e0,\n \"failures_count\"=\u003e6,\n \"processed_count\"=\u003e0,\n \"processes\"=\u003e\n  {\"jiggler:svr:3513d56f7ed2:10:25:default:1:1673875240:83568:JulijaA-MBP.local\"=\u003e\n    {\"heartbeat\"=\u003e1673875270.551845,\n     \"rss\"=\u003e32928,\n     \"current_jobs\"=\u003e{},\n     \"name\"=\u003e\"jiggler:svr:3513d56f7ed2\",\n     \"concurrency\"=\u003e\"10\",\n     \"timeout\"=\u003e\"25\",\n     \"queues\"=\u003e\"default\",\n     \"poller_enabled\"=\u003etrue,\n     \"started_at\"=\u003e\"1673875240\",\n     \"pid\"=\u003e\"83568\",\n     \"hostname\"=\u003e\"JulijaA-MBP.local\"}},\n \"queues\"=\u003e{\"mine\"=\u003e1, \"unknown\"=\u003e1, \"test\"=\u003e1}}\n```\nNote: Jiggler summary shows only queues which have enqueued jobs. \n\nJob classes should include `Jiggler::Job` and implement `perform` method.\n\n```ruby\nclass MyJob\n  include Jiggler::Job\n\n  def perform\n    puts \"Performing...\"\n  end\nend\n```\n\nThe job can be enqued with:\n```ruby\nMyJob.enqueue\n```\n\nSpecify custom job options:\n```ruby\nclass AnotherJob\n  include Jiggler::Job\n  job_options queue: \"custom\", retries: 10, retry_queue: \"custom_retries\"\n\n  def perform(num1, num2)\n    puts num1 + num2\n  end\nend\n```\n\nTo override the options for a specific job:\n```ruby\nAnotherJob.with_options(queue: \"default\").enqueue(num1, num2)\n```\n\nIt's possible to enqueue multiple jobs at once with:\n```ruby\narr = [[num1, num2], [num3, num4], [num5, num6]]\nAnotherJob.enqueue_bulk(arr)\n```\n\nFor the cases when you want to enqueue jobs with a delay or at a specific time run:\n```ruby\nseconds = 100\nAnotherJob.enqueue_in(seconds, num1, num2)\n```\n\nTo cleanup the data from Redis you can run one of these:\n```ruby\n# prune data for a specific queue\nJiggler.config.cleaner.prune_queue(queue_name)\n\n# prune all queues data\nJiggler.config.cleaner.prune_all_queues\n\n# prune all Jiggler data from Redis including all enqued jobs, stats, etc.\nJiggler.config.cleaner.prune_all\n```\n\nOn default `client` uses synchronous `Redis` connections.  \\\nIn case the client is being used in async app (f.e. with [Falcon](https://github.com/socketry/falcon) web server, etc.), then it's possible to set a custom redis pool capable of sending async requests into redis. \\\nThe pool should be compatible with `Async::Pool` - support `acquire` method.\n\n```ruby\nJiggler.configure_client do |config|\n  config[:client_redis_pool] = my_async_redis_pool\nend\n\n# or use built-in async pool with\nrequire \"async/pool\"\n\nJiggler.configure_client do |config|\n  config[:client_async] = true\nend\n```\n\nThen, the client methods could be called with something like:\n```ruby\nSync { Jiggler.config.cleaner.prune_all }\nAsync { MyJob.enqueue }\n```\n\n### Local development\n\nDocker! You can spin up a local development environment without the need to install dependencies directly on your local machine.\n\nTo get started, make sure you have Docker installed on your system. Then, simply run the following command to build the Docker image and start a development server:\n```\ndocker-compose up --build\n```\n\nDebug:\n```\ndocker-compose up -d \u0026\u0026 docker attach jiggler_app\n```\n\nStart irb:\n```\ndocker-compose exec app bundle exec irb\n```\n\nRun tests: \n```\ndocker-compose run --rm web -- bundle exec rspec\n```\n\nTo run the load tests modify the `docker-compose.yml` to point to `bin/jigglerload`\n\n### Contributing\n\nFork \u0026 Pull Request.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftuwukee%2Fjiggler","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftuwukee%2Fjiggler","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftuwukee%2Fjiggler/lists"}