{"id":32164475,"url":"https://github.com/nshkrdotcom/foundation","last_synced_at":"2026-02-21T03:03:12.157Z","repository":{"id":297478963,"uuid":"996920193","full_name":"nshkrdotcom/foundation","owner":"nshkrdotcom","description":"Elixir infrastructure and Observability Library","archived":false,"fork":false,"pushed_at":"2025-12-29T10:58:03.000Z","size":5247,"stargazers_count":11,"open_issues_count":2,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-01T10:36:50.217Z","etag":null,"topics":["beam","elixir","erlang-vm","functional-programming","infrastructure","infrastructure-library","instrumentation","logging","metrics","monitoring","nshkr-observability","observability","otp","production-ready","telemetry"],"latest_commit_sha":null,"homepage":"","language":"Elixir","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/nshkrdotcom.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-06-05T17:03:56.000Z","updated_at":"2025-12-29T10:58:08.000Z","dependencies_parsed_at":"2025-06-05T18:18:51.273Z","dependency_job_id":"1bb77ada-943a-409e-a1b8-b53a5804b3f6","html_url":"https://github.com/nshkrdotcom/foundation","commit_stats":null,"previous_names":["nshkrdotcom/foundation"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/nshkrdotcom/foundation","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nshkrdotcom%2Ffoundation","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nshkrdotcom%2Ffoundation/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nshkrdotcom%2Ffoundation/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nshkrdotcom%2Ffoundation/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nshkrdotcom","download_url":"https://codeload.github.com/nshkrdotcom/foundation/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nshkrdotcom%2Ffoundation/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29672273,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-21T00:11:43.526Z","status":"online","status_checked_at":"2026-02-21T02:00:07.432Z","response_time":107,"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":["beam","elixir","erlang-vm","functional-programming","infrastructure","infrastructure-library","instrumentation","logging","metrics","monitoring","nshkr-observability","observability","otp","production-ready","telemetry"],"created_at":"2025-10-21T14:46:35.786Z","updated_at":"2026-02-21T03:03:12.152Z","avatar_url":"https://github.com/nshkrdotcom.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Foundation\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"assets/foundation.svg\" alt=\"Foundation logo\" width=\"200\" height=\"200\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eLightweight resilience primitives for Elixir\u003c/strong\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://hex.pm/packages/foundation\"\u003e\u003cimg src=\"https://img.shields.io/hexpm/v/foundation.svg\" alt=\"Hex version\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://hexdocs.pm/foundation\"\u003e\u003cimg src=\"https://img.shields.io/badge/hex-docs-blue.svg\" alt=\"Hex Docs\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/nshkrdotcom/foundation/blob/main/LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/hexpm/l/foundation.svg\" alt=\"License\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\nFoundation provides composable building blocks for resilient Elixir applications:\nbackoff policies, retry loops, rate-limit windows, circuit breakers, semaphores,\nand telemetry helpers.\n\n## Features\n\n- **Backoff** - Exponential, linear, and constant strategies with jitter\n- **Retry** - Configurable retry loops with timeout and progress tracking\n- **Polling** - Long-running workflow polling with backoff and cancellation\n- **Rate Limiting** - Shared backoff windows for API rate limits\n- **Circuit Breaker** - Protect downstream services with automatic recovery\n- **Semaphores** - Counting and weighted semaphores for concurrency control\n- **Dispatch** - Layered limiter combining concurrency, throttling, and byte budgets\n- **Telemetry** - Lightweight helpers with optional reporter integration\n\n## Requirements\n\n- Elixir 1.15+\n- OTP 26+\n\n## Installation\n\nAdd `foundation` to your dependencies in `mix.exs`:\n\n```elixir\ndef deps do\n  [\n    {:foundation, \"~\u003e 0.2\"}\n  ]\nend\n```\n\n## Usage\n\n### Backoff and Retry\n\n```elixir\nalias Foundation.Backoff\nalias Foundation.Retry\n\n# Create a backoff policy\nbackoff = Backoff.Policy.new(\n  strategy: :exponential,\n  base_ms: 100,\n  max_ms: 5_000,\n  jitter_strategy: :factor,\n  jitter: 0.1\n)\n\n# Create a retry policy\npolicy = Retry.Policy.new(\n  max_attempts: 5,\n  backoff: backoff,\n  retry_on: fn\n    {:error, :timeout} -\u003e true\n    {:error, :rate_limited} -\u003e true\n    _ -\u003e false\n  end\n)\n\n# Run with retry\n{result, _state} = Retry.run(fn -\u003e fetch_data() end, policy)\n```\n\n### Retry Runner with Telemetry\n\n```elixir\nalias Foundation.Retry.{Config, Handler, Runner}\n\nconfig = Config.new(max_retries: 3, base_delay_ms: 100)\nhandler = Handler.from_config(config)\n\n{:ok, result} = Runner.run(\n  fn -\u003e call_api() end,\n  handler: handler,\n  telemetry_events: %{\n    start: [:my_app, :api, :start],\n    stop: [:my_app, :api, :stop],\n    retry: [:my_app, :api, :retry]\n  }\n)\n```\n\n### HTTP Retry Helpers\n\n```elixir\nalias Foundation.Retry.HTTP\n\nHTTP.retryable_status?(429)  # true\nHTTP.retryable_status?(500)  # true\nHTTP.retryable_status?(400)  # false\n\n# Parse Retry-After header\nHTTP.parse_retry_after(\"120\")  # {:ok, 120}\nHTTP.parse_retry_after(\"Wed, 08 Jan 2026 12:00:00 GMT\")  # {:ok, seconds_until}\n```\n\n### Polling\n\n```elixir\nalias Foundation.Poller\nalias Foundation.Backoff\n\n{:ok, result} = Poller.run(\n  fn attempt -\u003e\n    case check_job_status(job_id) do\n      {:ok, :completed, data} -\u003e {:ok, data}\n      {:ok, :pending} -\u003e {:retry, :pending}\n      {:ok, :failed, reason} -\u003e {:error, reason}\n    end\n  end,\n  backoff: Backoff.Policy.new(strategy: :exponential, base_ms: 500, max_ms: 10_000),\n  timeout_ms: 60_000,\n  max_attempts: 20\n)\n```\n\n### Rate Limit Backoff Windows\n\n```elixir\nalias Foundation.RateLimit.BackoffWindow\n\n# Get or create a limiter for a key\nlimiter = BackoffWindow.for_key(:openai_api)\n\n# Set backoff after receiving 429\nBackoffWindow.set(limiter, 30_000)\n\n# Wait for backoff to clear before next request\nBackoffWindow.wait(limiter)\n```\n\n### Circuit Breaker\n\n```elixir\nalias Foundation.CircuitBreaker\n\n# Functional API (stateless)\ncb = CircuitBreaker.new(\"payment_service\", failure_threshold: 3, reset_timeout_ms: 30_000)\n\n{result, cb} = CircuitBreaker.call(cb, fn -\u003e\n  PaymentService.charge(amount)\nend)\n\n# Registry API (stateful, for shared circuit breakers)\nalias Foundation.CircuitBreaker.Registry\n\n{:ok, _pid} = Registry.start_link(name: MyApp.CircuitBreakers)\n\nresult = Registry.call(MyApp.CircuitBreakers, \"payment_service\", fn -\u003e\n  PaymentService.charge(amount)\nend)\n```\n\n### Semaphores\n\n```elixir\n# Counting semaphore (limit concurrent operations)\nalias Foundation.Semaphore.Counting\n\nregistry = Counting.default_registry()\n\n{:ok, result} = Counting.with_acquire(registry, :db_connections, 10, fn -\u003e\n  execute_query()\nend)\n\n# Weighted semaphore (byte budgets)\nalias Foundation.Semaphore.Weighted\n\n{:ok, sem} = Weighted.start_link(max_weight: 10_000_000)\n\nWeighted.with_acquire(sem, byte_size(payload), fn -\u003e\n  upload_data(payload)\nend)\n\n# Simple limiter\nalias Foundation.Semaphore.Limiter\n\nLimiter.with_semaphore(5, fn -\u003e\n  process_item()\nend)\n```\n\n### Layered Dispatch\n\n```elixir\nalias Foundation.Dispatch\nalias Foundation.RateLimit.BackoffWindow\n\n# Create a rate limiter\nlimiter = BackoffWindow.for_key(:api_dispatch)\n\n# Start dispatch with concurrency, throttling, and byte limits\n{:ok, dispatch} = Dispatch.start_link(\n  limiter: limiter,\n  key: :api,\n  concurrency: 100,\n  throttled_concurrency: 5,\n  byte_budget: 5_000_000\n)\n\n# Execute with rate limiting\nresult = Dispatch.with_rate_limit(dispatch, byte_size(request), fn -\u003e\n  send_request(request)\nend)\n\n# Signal backoff (e.g., after 429 response)\nDispatch.set_backoff(dispatch, 30_000)\n```\n\n### Telemetry\n\n```elixir\nalias Foundation.Telemetry\n\n# Emit events\nTelemetry.execute([:my_app, :request, :complete], %{count: 1}, %{status: 200})\n\n# Measure function duration\n{:ok, result} = Telemetry.measure([:my_app, :db, :query], %{table: :users}, fn -\u003e\n  Repo.all(User)\nend)\n\n# Optional: Start a reporter (requires telemetry_reporter dependency)\n{:ok, _pid} = Telemetry.start_reporter(name: :my_reporter, transport: MyTransport)\n\n{:ok, handler_id} = Telemetry.attach_reporter(\n  reporter: :my_reporter,\n  events: [[:my_app, :request, :complete]]\n)\n```\n\n## Documentation\n\nFull documentation is available at [HexDocs](https://hexdocs.pm/foundation).\n\n## License\n\nFoundation is released under the MIT License. See [LICENSE](LICENSE) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnshkrdotcom%2Ffoundation","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnshkrdotcom%2Ffoundation","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnshkrdotcom%2Ffoundation/lists"}