SkillAgentSearch skills...

Constellation

Distributed programming for Rust.

Install / Use

/learn @constellation-rs/Constellation
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<p align="center"> <img alt="Constellation" src="https://raw.githubusercontent.com/alecmocatta/constellation/master/logo.svg?sanitize=true" width="550" /> </p> <p align="center"> A project to make Rust the cutting edge of distributed computing. </p> <p align="center"> <a href="https://crates.io/crates/constellation-rs"><img src="https://img.shields.io/crates/v/constellation-rs.svg?maxAge=86400" alt="Crates.io" /></a> <a href="LICENSE.txt"><img src="https://img.shields.io/crates/l/constellation-rs.svg?maxAge=2592000" alt="Apache-2.0 licensed" /></a> <a href="https://dev.azure.com/alecmocatta/constellation/_build"><img src="https://dev.azure.com/alecmocatta/constellation/_apis/build/status/tests?branchName=master" alt="Build Status" /></a> </p> <p align="center"> <a href="https://docs.rs/constellation-rs">Docs</a> </p>

Constellation is a framework for Rust (nightly) that aides in the writing, debugging and deployment of distributed programs. It draws heavily from Erlang/OTP, MPI, and CSP; and leverages the Rust ecosystem where it can including serde + bincode for network serialization, and mio and futures-rs for asynchronous channels over TCP.

Most users will leverage Constellation through higher-level libraries, such as:

  • Amadeus: Harmonious distributed data analysis in Rust. Inspired by Rayon, it provides a distributed process pool and built-in data science tools to leverage it.
  • With more in the pipeline!

For leveraging Constellation directly, read on.

Constellation framework

  • Constellation is a framework that's initialised with a call to init() at the beginning of your program.
  • You can spawn(closure) new processes, which run closure.
  • spawn(closure) returns the Pid of the new process.
  • You can communicate between processes by creating channels with Sender::new(remote_pid) and Receiver::new(remote_pid).
  • Channels can be used asynchronously with sender.send(value).await and receiver.recv().await.
  • futures-rs provides useful functions and adapters including select() and join() for working with channels.
  • You can also block on channels with the .block() convenience method: sender.send().block() and receiver.recv().block().
  • For more information on asynchronous programming in Rust check out the Async Book!

Here's a simple example recursively spawning processes to distribute the task of finding Fibonacci numbers:

<details> <summary>Click to show Cargo.toml.</summary>
[dependencies]

# The core APIs, including init(), spawn(), Sender, Receiver and select().
# Always required when using Constellation.
constellation-rs = "0.1"

# Support for FnOnce!(), FnMut!() and Fn!() macros to create Serde serializable
# closures. Required to pass a closure to spawn().
serde_closure = "0.1"
</details> <p></p>
use constellation::*;
use serde_closure::FnOnce;

fn fibonacci(x: usize) -> usize {
    if x <= 1 {
        return x;
    }
    let left_pid = spawn(
        Resources::default(),
        FnOnce!(move |parent_pid| {
            println!("Left process with {}", x);
            Sender::<usize>::new(parent_pid)
                .send(fibonacci(x - 1))
                .block()
        }),
    )
    .block()
    .unwrap();

    let right_pid = spawn(
        Resources::default(),
        FnOnce!(move |parent_pid| {
            println!("Right process with {}", x);
            Sender::<usize>::new(parent_pid)
                .send(fibonacci(x - 2))
                .block()
        }),
    )
    .block()
    .unwrap();

    Receiver::<usize>::new(left_pid).recv().block().unwrap()
        + Receiver::<usize>::new(right_pid).recv().block().unwrap()
}

fn main() {
    init(Resources::default());

    println!("11th Fibonacci number is {}!", fibonacci(10));
}
<details> <summary>Click to show output.</summary>

** TODO! This is the wrong screencap! ** Screencap of constellation being used

</details> <p></p>

Check out a more realistic version of this example, including async and error-handling, here!

Running distributed

There are two components to Constellation:

  • a library of functions that enable you to spawn() processes, and send() and recv() between them
  • for when you want to run across multiple servers, a distributed execution fabric, plus the deploy command added to cargo to deploy programs to it.

Both output to the command line as show above – the only difference is the latter has been forwarded across the network.

Constellation is still nascent – development and testing is ongoing to bring support to Windows (currently it's Linux and macOS only) and reach a greater level of maturity.

The primary efforts right now are on testing, documentation, refining the API (specifically error messages and async primitives), and porting to Windows.

Features

Constellation takes care of:

  • spawn() to distribute processes with defined memory and CPU resource requirements to servers with available resources
  • TODO: Best-effort enforcement of those memory and resource requirements to avoid buggy/greedy processes starving others
  • Channels between processes over TCP, with automatic setup and teardown
  • Asynchronous (de)serialisation of values sent/received over channels (leveraging serde, bincode and optionally libfringe to avoid allocations)
  • Channels implement std::future::Future, futures::stream::Stream and futures::sink::Sink, enabling the useful functions and adapters including select() and join() from futures-rs to be used, as well as compatibility with tokio and runtime.
  • Powered by a background thread running an efficient edge-triggered epoll loop
  • Ensuring data is sent and acked before process exit to avoid connection resets and lost data (leveraging atexit and TIOCOUTQ)
  • Addressing: all channels are between cluster-wide Pids, rather than (ip,port)s
  • Performant: designed to bring minimal overhead above the underlying OS

What's it for

Constellation makes it easier to write a distributed program. Like MPI, it abstracts away sockets, letting you focus on the business logic rather than the addressing, connecting, multiplexing, asynchrony, eventing and teardown. Unlike MPI, it has a modern, concise interface, that handles (de)serialisation using serde, offers powerful async building blocks like select(), and integrates with the Rust async ecosystem.

How it works

There are two execution modes: running normally with cargo run and deploying to a cluster with cargo deploy. We'll discuss the first, and then cover what differs in the second.

Monitor process

Every process has a monitor process that captures the process's output, and calls waitpid on it to capture the exit status (be it exit code or signal). This is set up by forking upon process initialisation, parent being the monitor and the child going on to run the user's program. It captures the output by replacing file descriptors 0,1,2 (which correspond to stdin, stdout and stderr) with pipes, such that when the user's process writes to e.g. fd 1, it's writing to a pipe that the monitor process then reads from and forwards to the bridge.

Bridge

The bridge is what collects the output from the various monitor processes and outputs it formatted at the terminal. It is started inside init(), with the process forking such that the parent becomes the bridge, while the child goes on to run the user's program.

Spawning

spawn() takes a function, an argument, and resource constraints, and spawns a new process with them. This works by invoking a clean copy of the current binary with execve("/proc/self/exe",argv,envp), which, in its invocation of init(), acts slightly differently: it connects back to the preexisting bridge, and rather than returning control flow back up, it invokes the specified user function with the user argument, before exiting normally. The function pointer is adjusted relative to a fixed base in the text section.

Channels

Communication happens by creating Sender<T>s and Receiver<T>s. Creation takes a Pid, and does quite a bit of bookkeeping behind the scenes to ensure th

View on GitHub
GitHub Stars599
CategoryDevelopment
Updated14d ago
Forks24

Languages

Rust

Security Score

95/100

Audited on Mar 15, 2026

No findings