[Nix-dev] How to get rid of systemd

Ertugrul Söylemez ertesx at gmx.de
Sun Dec 28 21:09:06 CET 2014


Hi Ludovic,

> FWIW, Guix uses an approach along these lines: the ‘bitlbee-service’
> function, for instance, returns a ‘service’ object as a monadic value
> (see
> <http://www.gnu.org/software/guix/manual/guix.html#Defining-Services>
> for details.)

The way I understand the section on the store monad is that it's really
a state monad with a view on the current store (they should really show
the underlying types to make this easier to understand).  That's not
related to the way they define services, but rather just how the store
itself is used.

Nix has the store built into the language.  Side effects happen to build
store paths and, once built, they appear like regular immutable values
within the language.  It's convenient, but makes the assumption that
build outputs depend solely on their inputs, which unfortunately, as we
know, is not always true at this point.

However, the way they define services seems indeed a lot better than
what we currently have.  In part they already have what I think we
should have (functional services), but with no algebraic structure
behind it other than the store monad.


> Unlike in NixOS, the service implementation doesn’t have access to the
> rest of the system configuration, which I think is a good thing, as
> you note.

Absolutely.  Everything they depend on should be an argument.


> What’s unsatisfying (and thus subject to change) is that
> ‘operating-system’ objects (which are pure declarations) end up with
> monadic values in their ‘services’ field.  That makes it inconvenient
> to, say, filter items from that list, or to tweak their configuration,
> because one first needs to bind them.  That said, it’s probably not a
> problem for Nix, because every Nix function is really a function in
> what we call the “store monad”.

You could think of it that way, if you wanted.  I prefer to think of
what Nix does as the same magic that functional languages do with other
resources like memory (alloc and GC).


> In your example, what would ‘bitlbee’, ‘nginx’ etc. return exactly?
> An attribute set describing the service?

As a first approximation there would be a Service monoid, which would
combine certain typical attributes about a service, including startup
scripts, required bind-mounts, required system resources and if really
necessary shutdown scripts (most programs should shutdown properly when
the container goes down).

    Service : Mon

Its identity idS would be the empty service, which you can think of as
an empty group of processes with no mounts, no resources, etc.

The Bitlbee service would be constructed through a regular function that
takes the typical bitlbee options.  Nothing special here:

    bitlbee : BitlbeeConf -> Service

The nginx function is more interesting.  It involves a second monoid,
the monoid of web services (identity idW):

    WebService : Mon

Then nginx is a monoid morphism,

    nginx : WebService -> Service

that is a function with additional structure, in particular:

    nginx x ◇ nginx y ≡ nginx (x ◇ y)
    nginx idW ≡ idS

The most important part is to get the equivalence `≡` relation right,
i.e. when do we regard two services as equivalent?  This requires a lot
of careful thought, because services in the real world are messy and
effectful.  Example:

 1. One nginx instance serves two websites x and y.

 2. Load balancer scenario:  Three nginx instances.  One serves x, one
    serves y, the other redirects to the correct instance depending on
    the request.

 3. Mapping identities:  A hundred load-balanced nginx instances, one of
    them serving x, a second one serving y and all others serving
    nothing at all (returning 404 unconditionally).

These three examples really describe the same service.  One may be a
more efficient implementation, but they should be equivalent with
respect to `≡`, because they are indistinguishable from the outside
other than in exceptional cases (case 3 is more likely to run out of
memory than case 1).  That enables us to use equational reasoning for
correctness.  It also enables us to apply automatic semantics-preserving
optimisations.

Example: The host may be serving two sites. At the same time a system
monitoring daemon might generate health reports periodically that are
also served via HTTP.  Such a configuration might look like:

    nginxStatic "site1.host" "/var/www/site1" <>
    nginxStatic "site2.host" "/var/www/site2" <>
    systemHealth "health.host"

A semantics-preserving optimistation (which is really not an
optimistation, but the way web services are combined) would collapse all
of that to a single instance of nginx and one entry in the crontab for
the health reporter.

I hope this gives a first impression of how everything would fit
together.


Greets,
Ertugrul


More information about the nix-dev mailing list