{"id":13878635,"url":"https://github.com/bruno-/fiber_scheduler","last_synced_at":"2025-04-07T09:18:49.860Z","repository":{"id":38886755,"uuid":"458638590","full_name":"bruno-/fiber_scheduler","owner":"bruno-","description":"Ruby's missing Fiber Scheduler implementation.","archived":false,"fork":false,"pushed_at":"2022-12-29T08:17:00.000Z","size":165,"stargazers_count":123,"open_issues_count":7,"forks_count":4,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-03-31T07:07:03.814Z","etag":null,"topics":["async","async-programming","asynchronous-programming","concurrency","fiber","fiber-scheduler","ruby"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bruno-.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-02-12T21:01:43.000Z","updated_at":"2025-03-08T04:33:26.000Z","dependencies_parsed_at":"2023-01-31T08:15:15.361Z","dependency_job_id":null,"html_url":"https://github.com/bruno-/fiber_scheduler","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bruno-%2Ffiber_scheduler","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bruno-%2Ffiber_scheduler/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bruno-%2Ffiber_scheduler/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bruno-%2Ffiber_scheduler/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bruno-","download_url":"https://codeload.github.com/bruno-/fiber_scheduler/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247622983,"owners_count":20968575,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","async-programming","asynchronous-programming","concurrency","fiber","fiber-scheduler","ruby"],"created_at":"2024-08-06T08:01:55.366Z","updated_at":"2025-04-07T09:18:49.834Z","avatar_url":"https://github.com/bruno-.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"# Fiber Scheduler\n\nRuby 3 has\n[Fiber Scheduler hooks](https://docs.ruby-lang.org/en/3.1/Fiber/SchedulerInterface.html)\nthat enable asynchronous programming. In order to make this work you need a\nFiber Scheduler implementation, **but Ruby does not provide a default one**.\n\nThis gem aims to fill that void by providing a Fiber Scheduler class that makes\na great default. It's easy to use, performant, and can be used with\n**just built-in Ruby methods**.\n\n`fiber_scheduler`'s killer feature 💣 is **full compatibility with any other\nFiber Scheduler implementation**, including the\n[async gem](https://github.com/socketry/async). Write code using\n`fiber_scheduler` and it works seamlessly with `async`, `bsync` or whatever\nother `_sync` gem comes in the future.\n\nLearn more about the Ruby's\n[Fiber Scheduler feature](https://brunosutic.com/blog/ruby-fiber-scheduler).\n\n### Installation\n\n```\ngem install fiber_scheduler\n```\n\nRequires Ruby 3.1.\n\n### Highlights\n\n- Enables asynchronous (colorless) programming in Ruby.\n- Killer feature: full compatibility with any other Fiber Scheduler\n  implementation, including the [async gem](https://github.com/socketry/async).\n- Not a framework: no DSL or new APIs. Can be used with just built-in Ruby\n  methods:\n  [Fiber.set_scheduler](https://docs.ruby-lang.org/en/3.1/Fiber.html#method-c-set_scheduler)\n  and\n  [Fiber.schedule](https://docs.ruby-lang.org/en/3.1/Fiber.html#method-c-schedule).\n- ~500 LOC of pure Ruby, no C extensions.\n- No dependencies.\n\n### Setup\n\n1. With a block (recommended)\n2. Set `Fiber.scheduler` directly\n\n**With a block (recommended)**\n\n```ruby\nFiberScheduler do\n  # Your code here, e.g. Fiber.schedule { ... }\nend\n```\n\nRecommended because:\n\n- This approach has\n  [full compatibility with any other Fiber Scheduler](https://github.com/bruno-/fiber_scheduler#compatibility-with-other-fiber-schedulers).\n- `Fiber.scheduler` is automatically un-set outside the block.\n\n**Set Fiber.scheduler directly**\n\n```ruby\nFiber.set_scheduler(FiberScheduler.new)\n\n# Your code here, e.g. Fiber.schedule { ... }\n```\n\n`Fiber.scheduler` is set until the end of the current thread, unless manually\nunset with `Fiber.set_scheduler(nil)`.\n\nPros:\n\n- Uses only built-in Ruby methods `Fiber.set_scheduler` and `Fiber.schedule`.\n\nCons:\n\n- No compatibility with other fiber schedulers.\n\n### Examples\n\n#### Basic example\n\nThis example runs two HTTP requests in parallel:\n\n```ruby\nrequire \"fiber_scheduler\"\nrequire \"open-uri\"\n\nFiberScheduler do\n  Fiber.schedule do\n    URI.open(\"https://httpbin.org/delay/2\")\n  end\n\n  Fiber.schedule do\n    URI.open(\"https://httpbin.org/delay/2\")\n  end\nend\n```\n\n#### Advanced example\n\nThis example runs various operations in parallel. The example total running\ntime is slightly more than 2 seconds, which indicates all the operations ran in\nparallel.\n\nNote that all the operations used in `Fiber.schedule` blocks below are either\ncommon gems or built-in Ruby methods. They all work asynchronously with this\nlibrary, no monkey patching!\n\n```ruby\nrequire \"fiber_scheduler\"\nrequire \"httparty\"\nrequire \"open-uri\"\nrequire \"redis\"\nrequire \"sequel\"\n\nDB = Sequel.postgres\nSequel.extension(:fiber_concurrency)\n\nFiberScheduler do\n  Fiber.schedule do\n    # This HTTP request takes 2 seconds (slightly more because of the latency)\n    URI.open(\"https://httpbin.org/delay/2\")\n  end\n\n  Fiber.schedule do\n    # Use any HTTP library\n    HTTParty.get(\"https://httpbin.org/delay/2\")\n  end\n\n  Fiber.schedule do\n    # Works with any TCP protocol library\n    Redis.new.blpop(\"abc123\", 2)\n  end\n\n  Fiber.schedule do\n    # Make database queries\n    DB.run(\"SELECT pg_sleep(2)\")\n  end\n\n  Fiber.schedule do\n    sleep 2\n  end\n\n  Fiber.schedule do\n    # Run system commands\n    `sleep 2`\n  end\nend\n```\n\n#### Scaling example\n\nEasily run thousands and thousands of blocking operations in parallel. This\nprogram finishes in about 2.5 seconds.\n\n```ruby\nrequire \"fiber_scheduler\"\n\nFiberScheduler do\n  10_000.times do\n    Fiber.schedule do\n      sleep 2\n    end\n  end\nend\n```\n\nGotcha: be careful about the overheads when scaling things. The below snippet\nruns `sleep` which is an \"inexpensive\" operation. But, if we were to run\nthousands of network requests there would be more overhead (establishing\nTCP connections, SSL handshakes etc) which would prolong program running time.\n\n#### Nested Fiber.schedule example\n\nIt's possible to nest `Fiber.schedule` blocks arbitrarily deep.\n\nAll the `sleep` operations in this snippet run in parallel and the program\nfinishes in 2 seconds total.\n\n```ruby\nrequire \"fiber_scheduler\"\n\nFiberScheduler do\n  Fiber.schedule do\n    Fiber.schedule do\n      sleep 2\n    end\n\n    Fiber.schedule do\n      sleep 2\n    end\n\n    sleep 2\n  end\n\n  Fiber.schedule do\n    sleep 2\n  end\nend\n```\n\n#### Waiting Fiber.schedule example\n\nSometimes it's conventient for the parent to wait on the child fiber to\ncomplete. Use `Fiber.schedule(:waiting)` to achieve that.\n\nIn the below example fiber labeled `parent` will wait for the `child` fiber to\ncomplete. Note that only the `parent` fiber waits, other fibers run as usual.\n\nThis example takes 4 seconds to finish.\n\n```ruby\nrequire \"fiber_scheduler\"\n\nFiberScheduler do\n  Fiber.schedule do # parent\n    Fiber.schedule(:waiting) do # child\n      sleep 2\n    end\n    # The fiber stops here until the waiting child fiber completes.\n\n    sleep 2\n  end\n\n  Fiber.schedule do\n    sleep 2\n  end\nend\n```\n\n#### Blocking Fiber.schedule example\n\nBlocking fibers \"block\" all the other fibers from running until they're\nfinished.\n\nThis example takes 4 seconds to finish.\n\n```ruby\nrequire \"fiber_scheduler\"\n\nFiberScheduler do\n  Fiber.schedule do\n    Fiber.schedule(:blocking) do\n      sleep 2\n    end\n  end\n\n  Fiber.schedule do\n    sleep 2\n  end\nend\n```\n\n#### Volatile Fiber.schedule example\n\nVolatile fibers end when all the \"durable\" fibers finish.\nVolatile fibers (by design) may not complete all their work.\n\nThis is useful if you have a neverending task that performs some\ncleanup work that should finish when the rest of the program completes.\n\nThis example takes 2 seconds to finish.\n\n```ruby\nrequire \"fiber_scheduler\"\n\nFiberScheduler do\n  Fiber.schedule(:volatile) do\n    # This fiber will live for only 2 seconds.\n\n    loop do\n      cleanup_work # this method will run only once\n\n      sleep 10\n    end\n  end\n\n  Fiber.schedule do\n    sleep 2\n  end\nend\n```\n\n### Compatibility with other fiber schedulers\n\n#### [async gem](https://github.com/socketry/async)\n\n`async` is an awesome asynchronous programming library, if not a framework.\nIf `async` is like Rails, then `fiber_scheduler` is plain Ruby.\n\n`fiber_scheduler` is fully compatible with `async`:\n\n```ruby\nAsync do |task|\n  task.async do\n    # code ...\n  end\n\n  FiberScheduler do\n    Fiber.schedule do\n      # code ...\n    end\n  end\n\n  # ...\nend\n```\n\nNote that currently the opposite doesn't work:\n\n```ruby\nFiberScheduler do\n  Async do\n    # ...\n  end\n\n  Fiber.schedule do # No scheduler is available! (RuntimeError)\n    # ...\n  end\nend\n```\n\n#### Other Fiber Scheduler implementations\n\n`fiber_scheduler` gem works with any other Fiber Scheduler class (current and\nfuture ones). Example:\n\n```ruby\nFiber.set_scheduler(AnotherScheduler.new)\n\n# stuff\n\nFiberScheduler do\n  # works just fine\nend\n\n# more stuff\n```\n\n`fiber_scheduler` is like choosing pure Ruby: it's a safe choice because you\nknow it works and will continue working with everything else in Ruby's\nasynchronous eco-system.\n\n### Performance\n\nThis [basic perf benchmark](examples/performance.rb) looks promising.\n\nHINT: make sure to install `io-event` gem alongside `fiber_scheduler` for a\nperformance improvement.\n\n### Credits\n\nSamuel Williams for:\n\n- Implementing Ruby's Fiber Scheduler hooks.\n- The [default selector](lib/fiber_scheduler/selector.rb) used in this gem.\n\n### License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbruno-%2Ffiber_scheduler","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbruno-%2Ffiber_scheduler","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbruno-%2Ffiber_scheduler/lists"}