Processes

Fugue is a new kind of operating system, designed to operate the distributed computer that we colloquially refer to as “the cloud.” The most obvious operating system feature of Fugue is the way it controls the execution of processes, which we refer to as Fugue’s “process management model.”

Understanding the Fugue Process Management Model

In summary, Fugue does all work in the form of processes.

What is a Fugue process?

A process is an instance of an infrastructure composition that the Conductor is working to build and maintain. Generally, a process can be thought of as a composition that will be “made so.” The Conductor takes a variety of actions to ensure that items in the composition exist and have the configuration declared therein. It does this both when the process is first run, and continuously while it is running.

In other words, a Ludwig composition is the code, whereas the process represents the workload. A given composition may be run more than once, resulting in multiple processes.

Processes and jobs

Processes consume Conductor compute time during jobs, where process data is reviewed and acted upon as necessary at regular intervals. Each job can potentially represent some change in the managed infrastructure, although most jobs are simply inspection and require no action. Jobs for multiple processes may be running at any given time.

How and whether jobs are emitted is dependent on process state. Valid process states are Running, Suspended, and Killing. Running is the most common state, where the process may be building architecture, monitoring configuration, or otherwise doing what it’s intended to do. Processes which are Killing are being cleaned up by the current job, and processes which are Suspended do not emit jobs until they are reactivated with a resume command.

Process states and transitions

The states and transitions in the Fugue process management model are summarized in the table below.

Commanded state transitions for Fugue processes

  ...to Running ...to Suspended ...to Killing
From nothing... run
From Running... suspend kill
From Suspended... resume kill
From Killing... suspend

Note that once a kill completes, a process cannot be restored and is permanently deleted. However, you can simply run the same composition again in a new process.

As you can see, there’s a pretty limited list of state transitions. The Fugue CLI Reference has some detail on how to make these transitions happen, but we’ll briefly review them as far as the internal impact they have.

From nothing to Running (fugue run)

The essential Fugue process state transition, this is what you’ll do to build things. This starts a new process, which creates jobs. The gist is that processes in this state create infrastructure and enforce its configuration.

From Running to Killing (fugue kill)

The inverse of a run. The gist of a kill state transition is that it results in the deletion of all infrastructure managed in the process.

From Suspended to Killing (fugue kill)

This state transition works the same as the Running to Killing transition, and has identical results: the deletion of all infrastructure managed in the process.

Just remember this: A kill command always kills the process, whether its current state is Running or Suspended.

From Running to Suspended (fugue suspend)

This state transition keeps the process in “memory” in Fugue, but halts emitting new jobs. As such, no work is done on the process at all. This means nothing is created or destroyed, and all configuration enforcement stops while in this state.

Note that Running to Suspended is also the only uncommanded state transition that may occur in Fugue. If a process encounters runtime errors that require human review, decision, or intervention, the Fugue runtime makes this transition. See the Fugue CLI Reference chapter on the fugue status command to learn how to get more information about a suspended process.

From Killing to Suspended (fugue suspend)

This state transition is similar to the transition from Running to Suspended and typically occurs when a user wants to pause a kill. (From here, the user can cancel the kill by executing resume, or they may continue the kill by executing kill again.)

From Suspended to Running (fugue resume)

This state transition restores the process to the Running state, when it again emits jobs. If configuration has been altered while in the suspended state, but no update to the composition has occurred, any such alterations are reverted.

You might intentionally make changes to a Suspended process in a reliability testing scenario to simulate application response to component failure, or even in production to mitigate an unanticipated problem. When the dust has settled, you can then resume the process to return it to its normal, declared state.

You may also have gotten here by executing suspend on a process in a Killing state. From here, resume enables you to cancel the kill and return to Running.

How many processes can a Fugue Conductor manage?

The upper bound on this is not yet determined, but the important bit of the answer is “more than one.” Like modern operating systems, Fugue handles more than one process at the same time.

How does Fugue handle the order of processes?

Fugue does not adhere to a particular order for processes and instead chooses to follow a natural order of AWS operations when evaluating and deciding how to process requests. For example: a subnet is never created before the VPC it belongs to. In short, Fugue completes all requests for a specific resource type before moving on to the next type for each process.

How does Fugue perform process work?

For each process, the Fugue Conductor performs all the steps required to make sure that the infrastructure exists (or is destroyed, in some cases) and is faithful to the configuration declared for it in a composition. To explain how this happens, let’s walk through a job.

First, there is a job.

Every thirty seconds, the Conductor’s scheduler emits a job for each process. This job prompts other components to begin work on the process.

Next, process and environment data are evaluated to construct a plan.

Conductor components perform work only for processes in the Running or Killing states since those are the only two states where processes emit jobs. Orchestrated by the Conductor’s manager, the components do work that roughly fits this sequence:

  • Load a compiled composition into memory for reference. This tells all the components what the state of infrastructure should be.
  • Examine the environment by interrogating infrastructure provider APIs. This tells all the components what the state of infrastructure actually is.
  • Identify what differences exist between what should be and what actually is.
  • Based on the information derived above, produce a plan of action for infrastructure changes, if they are required.

Finally, the plan is handled by the Conductor’s broker.

The plan of action produced during the job is handed off to the broker for execution. The broker ensures that the appropriate infrastructure changes are applied before reporting the job as a success to the scheduler.

Are there other ways work gets done in Fugue?

No. To remain easy to understand and reason about, Fugue does all work for a process in a job. This is even true for first-time builds of a brand-new process, as well as for tear-downs of long-running processes. Each is just a different case applied to the same function, in which Fugue works to close the difference between what you’ve said should be, and what is in the world. We can quickly step through the function and cases here.

The Fugue process as a Function

Consider the following as a crude definition of the work a Fugue process does, with a job representing invocation of this function.

Note

This advanced topic details how Fugue reasons about plans, and may be skipped by readers who want to jump right into building stuff with Fugue.

def fugueProcess (whatShouldBe, whatIs):
  execute(plan(compare(whatShouldBe, whatIs)))

There are several child functions in the “stack” that we should understand:

  • compare(a, b), which compares whatShouldBe with whatIs and returns a delta;
  • plan(d), which produces a plan to negate the delta, and;
  • execute(p), which performs I/O to execute a plan.

Keep in mind that fugueProcess would be invoked for each process on each job.

In the case of a new process:

Here, the inaugural job invokes fugueProcess where:

  • whatShouldBe is a composition defining infrastructure (A, B, C), and;
  • whatIs is a report of the current infrastructure environment, ().

Evaluation of compare shows that the difference between these sets is (A, B, C).

Evaluation of plan produces a plan to add A, B, and C to “What Is,” the infrastructure environment, (Make A, Make B, Make C).

Evaluation of execute adds A, B, and C to the infrastructure environment. Now, “What Is” is in harmony with “What Should Be.”

In the case of an established process:

Here, a job invokes fugueProcess where:

  • whatShouldBe is a composition defining infrastructure (A, B, C), and;
  • whatIs is a report of the current infrastructure environment, (A, B, C).

Evaluation of compare shows that the difference between these sets is ().

This is a fairly boring example, as the plan and execution evaluations for an empty set produce no meaningful answer. It is worth noting, however, that in a typical, static infrastructure environment, this is overwhelmingly the most common case.

In the case of process enforcement or update:

Enforcement of configuration – in other words, the reversal of configuration drift – and updates-in-place to a running process are functionally identical. Consider the case of configuration drift, so that we invoke fugueProcess where:

  • whatShouldBe is a composition defining infrastructure (A, B, C), and;
  • whatIs is a report of the current infrastructure environment, (A, B, Z).

To take the function out of the abstract, think of C as a security group, and Z as the same security group with a rule added allowing TCP port 22 ingress from CIDR 0.0.0.0/0.

Evaluation of compare shows that the difference between these sets is (C Became Z).

Evaluation of plan produces a plan to revert Z back to C in the infrastructure environment, (Z Change-to C).

Evaluation of execute reconfigures Z to the declared configuration, C. Your servers are no longer open to SSH from the world.

A fugue update action produces a very similar case, where:

  • whatShouldBe is a composition defining infrastructure (Q, B, C), and;
  • whatIs is a report of the current infrastructure environment, (A, B, C).

Following the same pattern as above, we expect the execution of the plan (A Change-to Q).

Depending on the resource that is being changed, (A Change-to Q) may be either a destructive update or a mutative update. A destructive update is one in which the resource properties cannot be changed, so instead the entire resource is destroyed and replaced with a new one. A mutative update, on the other hand, is an update-in-place for resource properties that can be changed – for example, VPC tags or security group rules.

Think of A and Q as VPC tags. Let’s say A is my-old-tag and Q is my-new-tag. A fugue update action resulting in (A Change-to Q) would be a mutative update. The VPC’s tags change, so the VPC itself is not destroyed.

Now, think of A and Q as VPC CIDR blocks. A is 10.0.0.0/16 and Q is 192.168.0.0/16. A fugue update action resulting in (A Change-to Q) would be a destructive update. The VPC’s CIDR block cannot change, so the VPC is destroyed and replaced with a new one.

Resource properties that may be mutated rather than destroyed/replaced include the following:

  • Virtual private cloud (VPC) tags
  • VPC security group rules and tags
  • Auto-scaling group (ASG) properties

Updates to any other resource are destructive.

In the case of a killed process:

When a process enters the Killing state, the job that is emitted prompts a sequence of actions to clean up the resources declared in the process composition. This is an inversion of the case of a new process, where:

  • whatShouldBe is a special null composition, (), and
  • whatIs is a report of the current infrastructure environment, (A, B, C).

Evaluation of compare shows that the difference between these sets is (A, B, C).

Evaluation of plan produces a plan to delete A, B, and C from the infrastructure environment, (Delete A, Delete B, Delete C).

Evaluation of execute removes A, B, and C from the infrastructure environment.

In the case of a suspended process:

As you’ve learned, a suspended process does not emit jobs, so the function is never invoked.