Objective-C in the Cloud

Cloud Applications

This document is about the basic concept of cloud applications.

Introduction

Before you continue

Before you continue with this document you should have an account and create a cloud application as described here.

Structure of Cloud Application and Services

A cloud application is more or less a sandboxed OS X application. Basically you can do everything inside a cloud application you could do inside a sandboxed OS X application.

The cloud application holds some global properties common to all services. These are important for administration and at runtime:

  • Every cloud application has an unique name. It becomes a part of the domain to address it (//appName.obcl.io). The name is unique to all cloud applications of all accounts and will be reserved for a while even you remove the application or your account.
  • The cloud application contains the services and is built together with them.
  • The cloud application determines the runtime lifecycle of all of its services.
  • The cloud application provides the database access for its services.

Creating a Cloud Appliation

When you create a cloud application as described here, some things happens behind the scene:

  • The life of a cloud application usually starts at Objective-Cloud Connect. For each cloud application which is created via Objective-Cloud Connect a corresponding git repository is created for you automatically.
  • Shortly after the git repository has been created we initialize it with an Xcode project that is preconfigured to work with your credentials out of the box.
  • The initial Xcode project also includes a helloworld example. The initial push of the Xcode project causes the app to be deployed automatically. This is why you can use your cloud application immediately after you have created it.

Services

A cloud application contains its services.

There are two types of services: invocation-based and request-based.

Request-based Services

Request-based services are a kind of rawer: They response to a HTTP request. You define a Objective-C block as handler (C) and a predicate with a HTTP method (A) and a path (B) as matching rule:

[self handleRequestsWithMethod:@"GET"          // A
                  matchingPath:@"/sayhelloto"  // B
                     withBlock:                // C
                     ^(OCFRequest *request) 
                     {
                        [request respondWith:[NSString stringWithFormat:@"Hello %@", request.parameters[@"name"]]);
                     }];
}

To such a service you can send a GET request as greeter.obcl.io/sayhello?name=Objective-Cloud and will receive the respond Hello Objective-Cloud.

We added methods to make receiving parameters and sending responses as easy as it could be.

To get more informations about request-based services refers to their documentation.

Invocation-based Services

We built invocation-based services on top of request-based services. As usual you put your code in methods belonging to classes. The class name builds the path of the URL. The process of addressing the code, transferring arguments and returning values is transparent to the service.

+ (NSString *)sayHelloTo:(NSString*)name
    return [NSString stringWithFormat:@"Hello %@", name]);
}

To get more informations about invocation-based services refers to their documentation.

Selecting a Service Type

Having the option to choose one of two service types leads to the question, which one to use. First of all: You can do everything with both services: You could write your own dispatcher in a request-based service, you have complete access to the HTTP request and response in an invocation-based service. So the correct question is: "What is more convenient to you?" And the answer depends of your service, especially on the API.

Let's have an example: You plan to write a service, that publishes an API to store and retrieve data. Using a request-based service you would probably use the HTTP methods to select an operation. (This is the reason for HTTP methods being a part of the handler's predicate.) Using an invocation-based service you would use method names for selecting the operation, especially naming rules, key-value coding. (This is the reason for selectors being a part of the dispatch process.) You can do both.

But you should not develop an invocation-based service using the HTTP method to select, whether some data has to be set or read. The Objective-C's pattern for this is the name of the accessor, not the method of the HTTP layer. This would be mixing up two worlds into one.

And you should not develop a request-based service and write your own dispatcher. We already did it.

There is a good rule of thumb: If your service relies heavily on HTTP, it is request-based, otherwise it is invocation-based. If you have a general handler for invocations (i. e. for authorization) this is possible and recommended for invocation-based services. But you shouldn't have access to the HTTP request inside many methods of your invocation-based service.

There are typical scenarios:

  • If your client is an application, especially using the client framework, an invocation-based service is probably the better choice.
  • If your client is a web browser, a request-based service is probably the better choice.
  • If you want APIs for both, write an invocation-based service and then a request-based service that uses the invocation-based service.

Developing and Debugging

After you have created and cloned a cloud application as described here, you want to develop your service, test it, fix bugs and so on. You do not do this on our servers, but in a local test environment called Terrasphere.

Terrasphere

To be more precise: Terrasphere is only a (lightweight) UI for terrasphered (the actual application server). It serves the tasks for the development a cloud application.

After launching terrasphere it will add a menu extra to your menu bar.

Unified Access

When a cloud application is launched it creates an HTTP server on a random port. This is very inconvenient if you want to use your cloud application (for example: you usually test your cloud application locally and thus you need to communicate with your cloud application). You would have to determine the port of your cloud application before you can actually use it. In contrast, Terrasphere is always running on port 10000. If you send a request to Terrasphere it will forward it to the correct cloud application. Terrasphere can do that because it knows about all cloud applications. This brings us to another thing Terrasphere is doing for you: Forwarding.

Forwarding

Terrasphere knows which cloud applications are currently running and it also knows their ports. If a request comes in, Terrasphere looks at the subdomain of the request's host and finds out which cloud application handles requests for this specific path component. If Terrasphere found a cloud application that can handle the request it forwards the HTTP request to that cloud application.

Updating and Bootstrapping

The whole point of Terrasphere is that you can test your cloud applications locally before pushing them to Objective-Cloud. Of course you want to test your cloud application in the same environment that it will be used when running it on Objective-Cloud. Basically, you have to mirror Objective-Cloud on your Mac. Since Objective-Cloud is a complex software we have extracted the most important components and packed them in an installer. When you install Terrasphere you also install a scaled down and simplified version of Objective-Cloud on your Mac. In addition Terrasphere tries to keep your local copy of Objective-Cloud up to date: When we deploy a new version of Objective-Cloud to our servers we also update Terrasphere. In turn Terrasphere will automatically ask you to download and execute the installer again if appropriate.

Gotchas

Terrasphere works great at long you are only working on one cloud application at a time. Each cloud application has a name. When a cloud application is deployed and a request comes in, you have to specify the name of the cloud application as part of the subdomain. For example https://helloworld.obcl.io identifies the cloud application called helloworld. As long you are working locally you make your request to localhost and localhost cannot have subdomains by default. This is why there can only ever be one cloud application locally. There are ways around this though: You can modify your /etc/hosts file for example.

Xcode

Once you launched Terrasphere you can launch your cloud application as usual in Xcode with Run. You can use the debugging tools.

You do not have to relaunch Terrasphere, when you relaunch your cloud application. Terrasphere detects the restart automatically.

Deployment

Deploying your cloud applications to Objective-Cloud is done by using git. You simply push your source code to the git repository we created, when your cloud application has been created. This triggers to actions:

  • The cloud application is built.
  • The built cloud application is run.

Building your Cloud Application

Building your cloud application is part of the deployment process.

Environment

We build your cloud application in a virtual machine with the following environment:

  • OS X 10.9
  • Xcode 5 with command line tools installed
  • The current Objective-Cloud frameworks (OCFoundation, OCFJSONCoding, OCFWeb and OCFWebServer) are available and your application will be linked against them.

When building your cloud application we automatically set the build setting called OBJECTIVE_CLOUD_APPLICATION_NAME.

You should not change the value of this build setting. It will break things. We may decide to not deploy your application of this build setting is not correct.

How we build your app

Your app is build by using xcodebuild like so:

xcodebuild -sdk macosx10.9 install \
           -configuration Release \
           -allTargets \
           OBJECTIVE_CLOUD_APPLICATION_NAME=$yourApp  \
           CONFIGURATION_BUILD_DIR=~/Desktop \
           FRAMEWORK_SEARCH_PATHS=$path_to_objective-cloud-frameworks \
           OBJROOT=~/obj \
           SYMROOT=~/sym \
           DSTROOT=~/dst \
           | tee xcodebuild.log

This means several things:

  • Your app has to be compatible with the new Mac OS X 10.9 SDK.
  • We are building all of your targets. The default template we provide does only contain a single target (your cloud app). If you add additional targets they will be built as well. If they do not build they break the build and your app won't be deployed.
  • We automatically link dynamically against the latest version of the Objective-Cloud frameworks. You should have them installed already locally.
  • If you want to use third party frameworks you are free to do so. You simply have to modify your target so that it includes all third party frameworks you need.

Runtime Lifecycle

It may surprise you but the lifecycle of a cloud application is very similar to the lifecycle of an iOS application or an XPC service on a Mac: They are launched by demand . If there are no special circumstances apps usually run forever. However the operating system can decide to terminate running apps at almost any time. One reason that would cause apps to be terminated by the operating system is memory pressure: When the system determines that it may run out of memory soon it may decide to terminate apps at will. The same principles apply to cloud applications as well: Once your cloud application is deployed on Objective-Cloud it can be launched by incoming requests (usually made by users of your cloud application). Your cloud application is then running. Just like iOS, Objective-Cloud may terminate you cloud application at any time. We do that in certain situations:

  • Heavy load: Under heavy load we terminate cloud application which have not handled request in a long time to free up resources needed by other cloud applications.
  • Too many timeouts: If we detect that lots of requests made to your cloud application have timed out we assume that your cloud application has some kind of deadlock. In this situation we will gather as many information about the current state of the cloud application, send you the information via e-mail and then terminate the cloud application. Terminating the cloud application in those cases works well in practice but you should definitely try to figure out why your cloud application deadlocked.
  • Long time, no see: If there was no request for your cloud application in a long time we may decide to terminate it as well. This is because we assume that your cloud application is no longer in use.

If a cloud application has been terminated, it has to be relaunched. In most cases you would not recognize that. But there are some side effects:

  • Launching an application takes time. The first request may be a little bit slower.
  • If your cloud application uses caches (i. e. in global vars), they will be empty.
  • If your cloud application holds some global data (i. e. for administration), you should persist it to the database at application termination and read it at application launch.

If your cloud application behaves correctly (i. e. do not crash), the usual launch and termination process of OS X is used.

Parallelization

You have to keep in mind, that your application runs on different servers simultaneously. Every request reaching Objective-Cloud first goes through a load balancer and is distributed to on one of these application servers. Each is running Terrasphere. Terrasphere knows the available applications, launches your application if needed and then forwards the request to your application.

This leads to a behavior similar to the behavior terminating applications:

  • Every request can end up on a different machine.
  • If your cloud application uses caches (i. e. in global vars), they can be different on every machine.
  • If your cloud application holds some global data (i. e. for administration), it can be different on very machine.

If you want to synchronize data between machines you should persist it into the database as usual in stateless services. The database is unique to the cloud application independently of the server it is running on.