{"id":17464496,"url":"https://github.com/overbryd/nodex","last_synced_at":"2026-03-06T19:35:23.484Z","repository":{"id":62430092,"uuid":"106942293","full_name":"Overbryd/nodex","owner":"Overbryd","description":"A set of helper modules that enable you to work with distributed elixir and c-nodes.","archived":false,"fork":false,"pushed_at":"2020-09-11T14:23:47.000Z","size":79,"stargazers_count":34,"open_issues_count":2,"forks_count":3,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-03-15T06:24:19.125Z","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":"lgpl-2.1","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Overbryd.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":"2017-10-14T16:11:42.000Z","updated_at":"2023-09-14T06:52:56.000Z","dependencies_parsed_at":"2022-11-01T19:47:26.165Z","dependency_job_id":null,"html_url":"https://github.com/Overbryd/nodex","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Overbryd%2Fnodex","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Overbryd%2Fnodex/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Overbryd%2Fnodex/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Overbryd%2Fnodex/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Overbryd","download_url":"https://codeload.github.com/Overbryd/nodex/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249762471,"owners_count":21321942,"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":[],"created_at":"2024-10-18T10:46:00.943Z","updated_at":"2026-03-06T19:35:23.437Z","avatar_url":"https://github.com/Overbryd.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Nodex\n\n![](https://github.com/Overbryd/nodex/blob/master/nodex.png?raw=true)\n\nA set of helper modules that enable you to work with **distributed elixir** and **c-nodes**.\n\nAvailable as a hex-package:\n\n```elixir\n{:nodex, \"~\u003e 0.1.1\"}\n```\n\n## Documentation\n\nThe docs can be found at [https://hexdocs.pm/nodex](https://hexdocs.pm/nodex).\n\n## Nodex.Distributed\n\nA module to setup a distributed environment programmatically.\nIt takes care of starting **epmd**, starts **:net_kernel** for you and can start and maintain **child**-nodes.\n\n```elixir\niex\u003e Nodex.Distributed.up\niex\u003e Node.alive?\ntrue\niex\u003e Node.self()\n:\"master@127.0.0.1\"\niex\u003e Nodex.Distributed.spawn_slaves(2)\n[:\"slave1@127.0.0.1\", :\"slave2@127.0.0.1\"]\n```\n\n## Nodex.Cnode\n\nHelper module to simplify working with a C-Node. It is also allows you to start and monitor\nan external C-Node process within a supervision tree.\n\n### What is a \"Cnode\"?\n\nC-Nodes are external os-processes that communicate with the Erlang VM through erlang messaging.\nThat way you can implement native code and call into it from Elixir in a safe predictable way.\nThe Erlang VM stays unaffected by crashes of the external process.\n\n\u003e In my opinion C-Nodes are the **best** option if you need to call into native code.\n\u003e The calling overhead is **small**, and on par with the calling overhead of remote node communication.\n\u003e And you get **monitoring** abilities through `Node.monitor(:node@host.example)`.\n\u003e You can **scale** your C-Nodes onto multiple machines.\n\u003e\n\u003e So instead of exposing your application to the risks that come with NIFs, you can enclose them in\n\u003e an external OS-process. And even better than port drivers, you gain all of the benefits including\n\u003e scalability.\n\nThe repository includes a benchmark comparing local, remote and cnode calling performance:\n\n```console\n## VmVsCnodeBench\n[20:16:07] 1/4: cnode\n[20:16:09] 2/4: cnode direct\n[20:16:12] 3/4: local\n[20:16:14] 4/4: remote\n\nFinished in 11.49 seconds\n\n## VmVsCnodeBench\nbenchmark nam iterations   average time \nlocal            1000000   1.90 µs/op\ncnode direct       50000   37.30 µs/op\ncnode              50000   41.43 µs/op\nremote             50000   48.64 µs/op\n```\n\nExecuted on a MacBook Pro 2,5GHz.\n\n### Mount a C-Node inside your supervision tree\n\nMount Cnodex as a worker, and reference it by name:\n```elixir\nchildren = [\n  worker(Cnodex, [%{exec_path: \"priv/example_client\"}], name: :ExampleClient)\n]\nSupervisor.init(children, strategy: :one_for_one)\n```\n\nLater call into your C-node through Cnodex:\n\n```elixir\n{:ok, reply} = Cnodex.call(:ExampleClient, {:ping, \"hello world\"})\n```\n\n### Start and call into a C-Node\n\n```elixir\n{:ok, pid} = Cnodex.start_link(%{exec_path: \"priv/example_client\"})\n{:ok, reply} = Cnodex.call(pid, {:ping, \"hello world\"})\n```\n\n### `Nodex.Cnode` Implementation details\n\nYour supervisor spawns a `GenServer` worker process that is using a node monitor on the C-Node.\n\nYou can provide a running C-Node to connect to. You must provide an executable with startup arguments that implement a suitable C-Node.\n\nA suitable C-Node prints a `ready_line` to stdout when it is ready to accept messages.\nThis mechanism signals readyness to the monitoring process.\nThe C-Node startup and ready-handling is synchronous. After a configurable `spawn_inactive_timeout` the `init` procedure is interrupted, and again supervision can take care of the failued startup.\n\nWhen you shutdown `Nodex.Cnode`, it will issue a SIGTERM to the os-pid of the C-Node, to ensure you have no lingering processes.\nAlthough it is even better if you implement some mechanism in the C-Node to ensure it exits properly after it looses the connection to Erlang.\n\nThe safest option to call into the C-Node is going through the `Cnodex.call/2` or `Cnodex.call/3` function.\nIt will, using a configurable timeout, await a response from the C-Node within the calling process.\nIn case the C-Node is unavailable, you will be thrown a proper exception, because you are calling an unavailable GenServer.\n\nIf you don't want your process inbox to be hijacked waiting for the C-Node response, use a `Task` in combination with `Cnodex.call/2`.\n\nIn case the C-Node becomes unavailable, the Cnode GenServer terminates too.\nYour supervisor can then take care starting a new C-Node.\n\n### Writing a C-Node\n\nThis repository provides you with some good starting points for writing a project or package that\nencloses a C-Node.\n\nCheckout the following files of this repository:\n\n```\n# A proper Makefile is a great base for building C\n├── Makefile\n│\n├── bench\n│   │\n│   # How to benchmark a C-Node\n│   └── vm_vs_cnode_bench.exs\n│\n# Directory with C source files\n├── c_src\n│   │\n│   # An example C-Node client that connects back to your node\n│   └── example_client.c\n│\n# A mix file that defines a custom task for handling Makefile builds\n├── mix.exs\n│\n# The priv directory should contain your C build artifacts\n├── priv\n│   │\n│   # This is the example C-Node client that gets build\n│   └── example_client\n│\n└── test\n    └── nodex\n            │\n            # An example test case on how to test C-Nodes and C-Node communication\n            └── cnode_test.exs\n```\n\nIn particular [`c_src/example_client.c`](https://github.com/Overbryd/nodex/blob/master/c_src/example_client.c) contains a nice boilerplate for writing your own C-Node client that\nconnects back to your node. It is fully annotated, so have a look to find out what is going on there.\n\nThe general idea behind this is, that you start up a C-program, and in the startup arguments you provide the\nconnection information to your Elixir node. If everything goes well, your C-program prints a shared message\nto STDOUT.\nThe Elixir side that started the C-program will read all the STDOUT lines written by the C-program.\nIf the correct shared message appears, it assumes the C-program is now ready to accept messages.\nAnd it will establish a node monitor on the C-program, so that if the connection goes down, the Elixir side\nis notified of the loss.\n\nIt is best to write the C-program in a way that is exits cleanly as soon as it looses the connection or something goes wrong.\nThat aligns the C-program with the idea that it can be controlled by a supervisor.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foverbryd%2Fnodex","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foverbryd%2Fnodex","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foverbryd%2Fnodex/lists"}