{"id":13409089,"url":"https://github.com/ananthakumaran/fdb","last_synced_at":"2025-07-22T04:04:42.677Z","repository":{"id":37759415,"uuid":"134298117","full_name":"ananthakumaran/fdb","owner":"ananthakumaran","description":"FoundationDB client for Elixir","archived":false,"fork":false,"pushed_at":"2023-05-27T13:48:15.000Z","size":352,"stargazers_count":51,"open_issues_count":0,"forks_count":5,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-04-12T22:51:42.185Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ananthakumaran.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}},"created_at":"2018-05-21T16:51:11.000Z","updated_at":"2025-02-24T17:17:37.000Z","dependencies_parsed_at":"2025-04-12T22:51:48.431Z","dependency_job_id":"e00e8506-5d45-4846-9c45-de5d20503a60","html_url":"https://github.com/ananthakumaran/fdb","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/ananthakumaran/fdb","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ananthakumaran%2Ffdb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ananthakumaran%2Ffdb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ananthakumaran%2Ffdb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ananthakumaran%2Ffdb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ananthakumaran","download_url":"https://codeload.github.com/ananthakumaran/fdb/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ananthakumaran%2Ffdb/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266424025,"owners_count":23926124,"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-07-22T02:00:09.085Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"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":[],"created_at":"2024-07-30T20:00:57.925Z","updated_at":"2025-07-22T04:04:42.656Z","avatar_url":"https://github.com/ananthakumaran.png","language":"Elixir","funding_links":[],"categories":["Bindings"],"sub_categories":[],"readme":"# FDB\n\n[![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/fdb/)\n\nFoundationDB client for Elixir\n\n## Status\n\nAPI is in alpha state and backward incompatible changes may be\nintroduced in subsequent versions.\n\n## Implementation Details\n\nAs there is no documented stable [wire\nprotocol](https://forums.foundationdb.org/t/how-difficult-would-it-be-to-implement-the-wire-protocol-in-other-languages/69)\nthe only practical option is to use the [C\nAPI](https://apple.github.io/foundationdb/api-c.html). NIF has some\nmajor [downsides](http://erlang.org/doc/man/erl_nif.html#WARNING)\n\n* pre-emptive scheduling\n\nThe FoundationDB C API uses event loop architecture. Nearly all the\nAPI functions are non blocking — blocking API functions are not used\nby FDB. The event loop runs on a seperate thread and the communication\nis done via callback functions. The callback function when invoked\nwill send a message to Process. This architecture makes sure the NIF\nfunctions return immediatly and gives the control back to VM\n\n* memory protection\n\nThis mostly comes down to careful coding. Currenly I am running the\ntests under valgrind locally. With some effort it could be integrated\nin travis. FDB also runs the [bindings\ntester](https://forums.foundationdb.org/t/creating-new-bindings/207)\n(used to test other language bindings) in travis CI.\n\n* concurrency\n\nThe FoundationDB C API functions are thread safe except for the\nnetwork intialization part. NIF implementation tries to avoid\nconcurrency problems by not mutating the values once created.\n\n\u003e Program testing can be used to show the presence of bugs, but never\n\u003e to show their absence!\n\u003e\n\u003e **Edsger W. Dijkstra**\n\nIt's still possible that there are bugs in C API or the NIF\nimplementation, which could lead to VM crash.\n\n## API Design\n\nIt's recommended to read the [Developer\nGuide](https://apple.github.io/foundationdb/developer-guide.html) and\n[Data\nModeling](https://apple.github.io/foundationdb/data-modeling.html) to\nget a good understanding of FoundationDB. Most of the ideas apply\nacross all the language bindings.\n\n### Async\n\nMost of the operations in FDB are async in nature. FDB provides two\nkinds of api\n\n* a sync api that will block the calling process till the operation is\n  done. In case of failure an exception will be raised.\n\n* an async api that will return `t:FDB.Future.t/0` immediatly. The caller can\n  later use `FDB.Future.await/1` to resolve the value, which will\n  block till the operation is done or will raise an exception in case\n  of failure.\n\nThe async api ends with `_q`, for example `FDB.Transaction.get/2` is\nthe sync version and `FDB.Transaction.get_q/2` is the async version of the same function.\n\n### Error Handling\n\nFoundationDB uses optimistic concurrency. When a transaction is\ncommitted, it could get cancelled if there are other conflicting\ntransactions. The common idiom is to retry the cancelled transaction\ntill it succeeds. `FDB.Database.transact/2` function automatically\nrescues and retries if the error is retriable. For this reason, the\napi is designed to raise exception instead of returning `{:error,\nerror}`\n\n## Installation\n\nFDB depends on FoundationDB [client\nbinary](https://apple.github.io/foundationdb/api-general.html#installing-foundationdb-client-binaries)\nto be installed. The version of the client binary should be `\u003e=` FDB\nlibrary version — patch and build part in the version can be\nignored. For example, if you want to use\n\n```elixir\n{:fdb, \"5.1.7-0\"}\n```\n\nthen you must have client binary `\u003e= 5.1`. Only patch versions are\nguaranteed to be protocol compatible.\n\n### Additional Steps on Windows\n\nTo compile the library in Windows you must have the Visual C++ Tools installed or VS 2017, if you don't use it probably you'll get a message telling you that `nmake` isn't installed.\n\n- With Visual C++ Tools: search for the file `vcvarsall.bat`, the Tools version 2017 are commonly located at `C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Professional\\VC\\Auxiliary\\Build` and run the command `vcvarsall amd64`.\n\n- With Visual Studio 2017 intalled: type `Developer Command` in the search box and you will get the cmd program as a result.\n\nThen move to your project directory and run `mix compile`\n\n## Getting Started\n\nBefore doing anything with the library, the API version has to be set\nand the network thread has to be started. `FDB.start/1` is a helper function\nwhich does all of these.\n\n```elixir\n:ok = FDB.start(710)\n```\n\nThis must be called only once. Calling it second time will result in\nexception. Once started, `t:FDB.Database.t/0` instance have to be created.\n\n```elixir\ndb = FDB.Database.create(cluster_file_path)\n```\n\nIt's recommended to use a single db instance everywhere unless\nmultiple db with different set of options are required. There are no\nperformance implications with using a single db instance as none of\nthe method calls are serialized either via locks or GenServer et\nal.\n\nAny kind of interaction with Database requires the usage of\n`t:FDB.Transaction.t/0`. There are two ways of using transaction\n\n```elixir\nFDB.Database.transact(db, fn transaction -\u003e\n  value = FDB.Transaction.get(transaction, key)\n  :ok = FDB.Transaction.set(transaction, key, value \u003c\u003e \"hello\")\nend)\n```\n\n```elixir\ntransaction = FDB.Transaction.create(db)\nvalue = FDB.Transaction.get(transaction, key)\n:ok = FDB.Transaction.set(transaction, key, value \u003c\u003e \"hello\")\n:ok = Transaction.commit(transaction)\n```\n\nThe first version is the preferred one. The transaction is\nautomatically committed after the callback returns. In case any\nexception is raised inside the callback or in the commit function\ncall, the transaction will be retried if the error is retriable. Various\noptions like `max_retry_delay`, `timeout`, `retry_limit` etc can be\nconfigured using `FDB.Transaction.set_option/3`\n\n### Coder\n\nMost of the language bindings implement the [tuple\nlayer](https://github.com/apple/foundationdb/blob/master/design/tuple.md). It\nspecifies how native types like integer, unicode string, bytes etc\nshould be encoded. The main advantage of the encoding over others is\nthat it preserves the natural ordering of the values, so the range\nfunction would work as expected.\n\n```elixir\nalias FDB.{Transaction, Database, KeySelectorRange}\nalias FDB.Coder.{Integer, Tuple, NestedTuple, ByteString, Subspace}\n\ncoder =\n  Transaction.Coder.new(\n    Subspace.new(\n      {\"ts\", ByteString.new()},\n      Tuple.new({\n        # date\n        NestedTuple.new({\n          # year, month, date\n          NestedTuple.new({Integer.new(), Integer.new(), Integer.new()}),\n          # hour, minute, second\n          NestedTuple.new({Integer.new(), Integer.new(), Integer.new()})\n        }),\n        # website\n        ByteString.new(),\n        # page\n        ByteString.new(),\n        # browser\n        ByteString.new()\n      })\n    ),\n    Integer.new()\n  )\ndb = Database.create(%{coder: coder})\n\nDatabase.transact(db, fn t -\u003e\n  m = Transaction.get(t, {{{2018, 03, 01}, {1, 0, 0}}, \"www.github.com\", \"/fdb\", \"mozilla\"})\n  c = Transaction.get(t, {{{2018, 03, 01}, {1, 0, 0}}, \"www.github.com\", \"/fdb\", \"chrome\"})\nend)\n\nrange = KeySelectorRange.starts_with({{{2018, 03, 01}}})\nresult =\n  Database.get_range_stream(db, range)\n  |\u003e Enum.to_list()\n\n```\n\nA `t:FDB.Transaction.Coder.t/0` specifies how the key and value should\nbe encoded. The coder could be set at database or transaction\nlevel. The transaction automatically inherits the coder from database\nif not set explicitly. Under the hood all the functions use the coder\ntransparently to encode and decode the values. Refer\n`FDB.Database.set_defaults/2` if you want to use multiple coders.\n\nSee the [documentation](https://hexdocs.pm/fdb) for more\ninformation.\n\n## Benchmark\n\nA simple, unreliable and non-scientific benchmark can be found [here](BENCHMARK.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fananthakumaran%2Ffdb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fananthakumaran%2Ffdb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fananthakumaran%2Ffdb/lists"}