Deployment and resource modeling

This chapter describes how to model non-functional properties of systems: code deployment on varying numbers and kinds of machines, and the effects of code locality and different resource types such as CPU speeds, interconnection bandwidth, etc. on the performance of a system.

To simulate the effects of deployment decisions on the cost and performance of running a distributed system, the following aspects need to be added to a model:

  • A notion of location to describe where different parts of the system will run

  • A notion of cost of executing code on such locations; and finally

  • A notion of time that allows us to observe the effects of varying the location and cost.

In ABS, the following language elements implement these aspects:

Deployment Components

ABS cogs can be created on a deployment component (see Deployment components). Deployment components have a limited number of resources available.

Resource Annotations

ABS statements can be annotated with resource costs (see Resource types). Executing such annotated statements incurs resource costs.

Timed ABS

The time model of Section Timed ABS naturally extends to observations on resource usage.

These elements work together as in Figure Resource Consumption:

Resource Consumption

Resource Consumption

The figure shows a process with a skip statement with an associated cost of 5, followed by a println statement with a cost of 10. The deployment component DC has 10 resources available in each time interval.

At time t, the skip statement consumes the five needed resources and is successfully executed. The println statement consumes the remaining five resources but is not executed yet.

At time t+1, the deployment component’s resources are refilled. The five remaining resources of the println statement are consumed and the statement is executed. The five remaining resources of DC are not used.

At time t+2, the deployment component’s resources are again refilled to 10.

The rest of this chapter explains these language elements concerning resource modeling in detail. All identifiers introduced in this section reside in the ABS.DC module; they are not imported into other modules by default.

Deployment components

In ABS, processes run inside cogs. Deployment components are used to provide a location to cogs. Cogs residing on the same deployment component share the resources provided by the deployment component.

Deployment Components are first-class constructs in the ABS language. References to deployment components can be stored in variables of type DeploymentComponent, and the methods documented in this section can be called via asynchronous method calls.

Deployment Components are usually created by a cloud provider instance (see The CloudProvider API), but can also be created using the new expression. A new cog is created on a given deployment component by using a DC annotation to the new statement.

If no DC annotation is given for a new statement, the fresh cog (and its first object) are created on the same deployment component as the current cog.

Note

It is an error to try to create a deployment component via new local.

Example:

DeploymentComponent dc = await provider!launchInstance(map[Pair(Speed, 10)]); ①
[DC: dc] Worker w = new CWorker(); ②
① The cloud provider provider creates a new deployment component dc with 10 Speed resources
② A new cog containing a CWorker object is created on the new deployment component dc

Note

All objects of a cog must reside on the same deployment component, i.e., [DC: x] new local C() is an error.

As seen above, each deployment component “carries” some amount of resources for each resource type. This is expressed as a map from resource type to a number, for example map[Pair(Speed, 10), Pair(Bandwidth, 20)]. When no amount is given for some resource type, it is infinite. See Resource types for a description of the available resource types.

Methods

load

Return the load (0-100) for the given resource type rtype over the last \(n\) periods. If the deployment component was created with infinite resources for the given resource type, load returns 0.

[Atomic] Rat load(Resourcetype rtype, Int periods)

total

Return the total available amount for the given resourcetype. If the deployment component was created with infinite resources for the given resource type, total returns InfRat, otherwise Fin(value).

[Atomic] InfRat total(Resourcetype rtype)

decrementResources

Decrease the total available amount for the resourcetype rtype by amount from the next time interval onwards. Trying to decrement infinite resources has no effect. Trying to decrement by more than available will decrement the maximum amount possible but not below zero. Returns the amount by which the resource was decreased.

[Atomic] Rat decrementResources(Rat amount, Resourcetype rtype)

incrementResources

Increase the total available amount for the resourcetype rtype by amount from the next time interval onwards. Trying to increment infinite resources has no effect. Returns the amount by which the resource was increased.

[Atomic] Rat incrementResources(Rat amount, Resourcetype rtype)

transfer

Transfer amount resources of type rtype from the current deployment component to target. Takes effect on the next time period. Returns the amount transferred, which will be lower than amount in case the deployment component has less resources available.

(This method is implemented via decrementResources and incrementResources.)

[Atomic] Rat transfer(DeploymentComponent target, Rat amount, Resourcetype rtype)

getName

Returns the name of the deployment component. Deployment components created via a CloudProvider are guaranteed to have a unique name if no two cloud providers have the same name.

[Atomic] String getName()

getCreationTime

Get the creation time of the deployment component, i.e., the value of now() at the time of creation of the component.

[Atomic] Time getCreationTime()

getStartupDuration

Get the specified startup duration, or 0 if none specified.

[Atomic] Rat getStartupDuration()

getShutdownDuration

Get the specified shutdown duration, or 0 if none specified.

[Atomic] Rat getShutdownDuration()

getPaymentInterval

Get the specified payment interval, or 1 if none specified.

[Atomic] Int getPaymentInterval()

getCostPerInterval

Get the specified cost (price) per interval, or 0 if none specified.

[Atomic] Rat getCostPerInterval()

shutdown

Shut down the deployment component. It is an error to create a new object on a deployment component that has been shutdown, or to invoke a method on an object residing on a deployment component that has been shut down.

Bool shutdown()

getProvider

Get the cloud provider that manages this deployment component. Returns null if the deployment component was not created by a cloud provider. See The CloudProvider API for a discussion of cloud providers.

[Atomic] CloudProvider getProvider()

acquire

Convenience method for calling acquireInstance of the associated cloud provider. If no cloud provider is set, returns True. See The CloudProvider API for a discussion of cloud providers.

Bool acquire()

release

Convenience method for calling releaseInstance of the associated cloud provider. If no cloud provider is set, returns True. See The CloudProvider API for a discussion of cloud providers.

Bool release()

Resource types

The term “Resource” can be understood in different ways. In ABS, we define “Resource” to be a countable, measurable property of a deployment component. Some resources stay constant throughout the life of a deployment component (e.g., the number of cores), some others are influenced by program execution (e.g., the available bandwidth in the current time slot).

The resource types currently supported by the ABS language are defined in the ABS.DC module as follows:

data Resourcetype = Speed | Bandwidth | Memory | Cores ;

When a deployment component is created without explicitly giving a value for a resource type, it is assumed to have an infinite amount of that resource. E.g., for the purpose of modeling a denial of service attack, the deployment component running the attacker code will have infinite speed and bandwidth.

Speed

The Speed resource type models execution speed. Intuitively, a deployment component with twice the number of Speed resources will execute twice as fast. Speed resources are consumed when execution in the current process reaches a statement that is annotated with a Cost annotation:

Time t1 = now();
[Cost: 5] skip;
Time t2 = now();

Executing the above skip statement will consume 5 Speed resources from the deployment component where the cog was deployed. If the deployment component does not have infinite Speed resources, executing the skip statement might take an observable amount of time, i.e., t1 and t2 might be different.

Bandwidth

Bandwidth is a measure of transmission speed. Bandwidth resources are consumed during method invocation and return statements. No bandwidth is consumed if sender and receiver reside on the same deployment component.

Bandwidth consumption is expressed via a DataSize annotation:

Time t1 = now();
[DataSize: 2 * length(datalist)] o!process(datalist);
Time t2 = now();

Executing the above method invocation statement will consume bandwidth resources proportional to the length of list datalist.

Memory

The Memory resource type abstracts from the size of main memory, as a measure of how many and which cogs can be created on a deployment component. In contrast to bandwidth and speed, memory does not influence the timed behavior of the simulation of an ABS model; it is used for static deployment modeling.

It is planned to implement memory semantics by annotating methods with the amount of memory required to start one process running that method; under simulated memory pressure, creation of processes from incoming method calls would be delayed until another process terminated and freed enough memory to create a new process.

Cores

The Cores resource type expresses the number of CPU cores on a deployment component. It is used for static deployment decisions and does not have influence on the timing behavior of simulations (use the Speed resource type for this purpose).

Modeling resource usage

As described above, resource information is added to statements of an ABS model using Cost and DataSize annotations. Executing such annotated statements causes observable changes in the simulated time and deployment component load during simulation.

Example:

module Test;
import * from ABS.DC; ①
interface I {
  Unit process();
}
class C implements I {
  Unit process() {
    [Cost: 10] skip; ②
}

{
  DeploymentComponent dc = new DeploymentComponent("Server",
    map[Pair(Speed, 5), Pair(Bandwidth, 10)]);
  [DC: dc] I i = new C();
  [DataSize: 5] i!process(); ③
}
① Make all necessary identifiers accessible in the current module
② Executing this statement costs 10 Speed units; the time needed depends on the capacity of the deployment component, and on other cogs executing in parallel on the same deployment component.
③ Executing this method call consumes 5 Bandwidth resources. Since dc has 10 bandwidth resources per time unit, the message will be transported instantly. Executing the skip statement in the method body will not finish instantaneously because dc only has 5 Speed resources in total.

The CloudProvider API

Deployment components can be managed by a Cloud Provider instance. The Cloud Provider implements the life cycle shown in Figure Deployment Component lifecycle. A deployment component is managed by a cloud provider if it was created via one of the launchInstance methods.

Modeling deployment container allocation and deallocation using cloud provider instances is useful for modeling and simulating the cost (as in money) of running a system involving machines of various sizes and costs.

Deployment Component lifecycle

Deployment Component lifecycle

The operations supported by deployment components during their lifecycle are summarized in Table Deployment Component Lifecycle.

Deployment Component Lifecycle

State

Starting

Running

Terminating

Terminated

Create objects

delayed

yes

no

no

Invoke methods

delayed

yes

no

no

Process keep running

yes

yes/no

Using the cloud provider

To use deployment components via a cloud provider, follow these steps:

  1. Create a CloudProvider instance

  2. Set the instance descriptions via the setInstanceDescriptions method

  3. Create deployment components using the launchInstanceNamed method

  4. (optional) Manage access via releaseInstance / acquireInstance

  5. (optional) Release deployment components via shutdownInstance

module ProviderDemo;
import * from ABS.DC;

{
    CloudProvider p = new CloudProvider("Amazon");
    await p!setInstanceDescriptions(
        map[Pair("T2_MICRO", map[Pair(Memory,1), Pair(Speed,1)]),
            Pair("T2_SMALL", map[Pair(Memory,2), Pair(Speed,1)]),
            Pair("T2_MEDIUM", map[Pair(Memory,4), Pair(Speed,2)]),
            Pair("M4_LARGE", map[Pair(Memory,8), Pair(Speed,2)])]);
    DeploymentComponent dc = await p!launchInstanceNamed("T2_SMALL");
    // ... use the deployment component ...
}

Datatypes and constructors

The type for cloud provider instances is ABS.DC.CloudProvider.

Cloud provider instances are created with a new CloudProvider(String name) expression. It is not mandatory but recommended that each cloud provider instance has a unique name.

It is recommended to call setInstanceDescriptions once after creating a cloud provider to set the list of named instance types that this cloud provider offers.

Methods

setInstanceDescriptions

This method sets the named instance configurations that the cloud provider instance should support. These names are used in the methods launchInstanceNamed and prelaunchInstanceNamed.

[Atomic] Unit setInstanceDescriptions(Map<String, Map<Resourcetype, Rat>> instanceDescriptions);

getInstanceDescriptions

This method returns the map of named instance configurations.

[Atomic] Map<String, Map<Resourcetype, Rat>> getInstanceDescriptions();

launchInstanceNamed

This method creates and returns a new deployment component with a resource configuration corresponding to instancename, as set by the setInstanceDescriptions method. If no description for instancename exists, launchInstanceNamed returns null.

DeploymentComponent launchInstanceNamed(String instancename);

The name of the new deployment component will be "<Cloud provider name>-<instancename>-<Counter>", i.e., a concatenation of the name of the cloud provider itself, the instance name, and a unique integer as suffix.

If the instance description specifies a startup duration, launchInstanceNamed will only return after that amount of simulated time has elapsed.

The returned deployment component will be acquired (as per acquireInstance) and can be used immediately.

prelaunchInstanceNamed

This method creates and returns a new deployment component with a resource configuration corresponding to instancename, as set by the setInstanceDescriptions method. If no description for instancename exists, prelaunchInstanceNamed returns null.

DeploymentComponent prelaunchInstanceNamed(String instancename);

As with launchInstance, the name of the new deployment component will be "<Cloud provider name>-<instancename>-<Counter>", i.e., a concatenation of the name of the cloud provider itself, the instance name, and a unique integer as suffix.

The method prelaunchInstanceNamed returns immediately, but the method acquireInstance, when called on the returned deployment component, will not return before its startup duration (if specified) has elapsed.

The returned deployment component needs to be acquired (as per acquireInstance) before it can be used.

launchInstance

The launchInstance method creates and returns a new deployment component with the specified resource configuration. It can be used when, for whatever reason, the resource configuration should not be registered with the cloud provider, but the deployment component should still be managed by it.

DeploymentComponent launchInstance(Map<Resourcetype, Rat> description);

The name of the new deployment component will be "<Cloud provider name>-<Counter>", i.e., a concatenation of the name of the cloud provider itself and a unique integer as suffix.

If the resource configuration specifies a startup duration, launchInstanceNamed will only return after that amount of simulated time has elapsed.

The returned deployment component will be acquired (as per acquireInstance) and can be used immediately.

prelaunchInstance

This method creates and returns a new deployment component with the specified resource configuration. As with launchInstance, this method can be used when, for whatever reason, the resource configuration should not be registered with the cloud provider, but the deployment component should still be managed by it.

DeploymentComponent prelaunchInstance(Map<Resourcetype, Rat> d)

The name of the new deployment component will be "<Cloud provider name>-<Counter>", i.e., a concatenation of the name of the cloud provider itself and a unique integer as suffix.

The method prelaunchInstance returns immediately, but the method acquireInstance, when called on the returned deployment component, will not return before its startup duration (if specified) has elapsed.

The returned deployment component needs to be acquired (as per acquireInstance) before it can be used.

acquireInstance

This method, together with releaseInstance, implements exclusive access to a deployment component. After acquireInstance returns true, all further invocations will return false until releaseInstance is called for the deployment component.

Bool acquireInstance(DeploymentComponent instance);

If the deployment component passed as argument was not created by the cloud provider, the method returns false.

Note

The methods acquireInstance and releaseInstance are used to implement exclusive access in a cooperative manner. Attempting to create a cog on a deployment component without having acquired it beforehand will not lead to a runtime error; ensuring exclusive access to deployment components is the responsibility of the modeler.

releaseInstance

This method releases the deployment component, such that the next call to acquireInstance will return true.

Bool releaseInstance(DeploymentComponent instance);

This method returns true if the deployment component was successfully released. It returns false if the deployment component was already not acquired.

If the deployment component passed as argument was not created by the cloud provider, the method returns false.

shutdownInstance

This method shuts down a deployment component. The effect on the cogs, objects and running tasks deployed on that deployment component are backend-specific.

Bool shutdownInstance(DeploymentComponent instance);
[Atomic] Rat getAccumulatedCost();
[Atomic] Map<String, Map<Resourcetype, Rat>> getInstanceDescriptions();