Continuation Servers

You can probably begin to see why continuations might be interesting for web servers. If you want to look at a web application as one continuous application with suspend/resume breaks in between to communicate with the user, it makes more sense. While waiting for user input in the form of an HTTP request, the web server could simply store a state, stash the continuation object away in the HTTP session, and instantly return to that frozen point in time when it's time to process another request. Notice in Figure 8-1 that I've conveniently inverted the control. Instead of thinking of a web app as a series of request/response pairs initiated by the user, I can think of a web app as a series of response/request pairs controlled by the server. My server code gets much simpler.

Figure 8-1. Continuation servers invert control from client to server, simplifying the world view, and the code, of the server

Your web application server is no longer composed of many different independent requests. The server can conveniently look at the world as a bunch of simple end-to-end applications. It processes individual requests by loading the state of each user when it's time to process another request, and suspending the user's application when it's time to communicate with the user again. Voil! Your application can maintain state, and use it to seamlessly control application flow.

At a lower level, the continuation server becomes a collection of web applications with states frozen at a point in time, in the form of continuations. Each user has a session. The continuation server assigns an ID to each session, and organizes the continuations per session. After each request, the continuation server takes a snapshot of the execution state with a continuation object, and associates that continuation with the session. So, a server has multiple sessions, and each session has one or more continuations representing frozen points in time, as shown in Figure 8-2. You can no longer see individual HTTP requests, because they're buried in the application flow. As they should be!

Glenn Vanderburg: Continuation Servers

Author of Maximum Java 1.1

Glenn Vanderburg, a consultant from Dallas, has been writing Java programs since before it was called Java, and was the author of one of the first advanced Java books. Glenn has 19 years of software development experience, encompassing a wide variety of languages, platforms, industries, and domains.

What's wrong with current web development models, like the Servlet model?

GV: There are two big problems. I'll start with the most obvious. When I did mainframe programming, I would build a screen of information mixed with form fields, and push it out to a 3270 terminal. The program wouldn't hear from the terminal again until the user hit Enter. Sound familiar?

In the mainframe days, the program got to pause and wait on the user's submission. Web programming is actually worse, because in the interest of scaling to thousands of users (as opposed to hundreds), the program is asked to forget as much as possible between each interaction so that each submission can stand alone. The stateless nature of the web programming model forces programmers to manually manipulate, store, and retrieve the program state at every stage. Web frameworks help some, but programmers still have to consider carefully how to deal with each piece of state. One mistake and we get web applications that are (at best) very confusing to use.

The other big deficiency of the web development model is that our programs are held together with strings. The navigational structure is defined by URLs we stick in links, and those URLs have to also go in configuration files to tie them to pieces of code that get invoked. User input comes to us in form fields that are named with strings. State that we store in the session is usually referenced by a key that is a string. We have all of these strongly typed programming languages and IDEs to go with them to make sure we don't make silly errors like misspelling variable names, but that all goes out the window with web apps, because the tools don't help us to validate all of our uses of URL fragments, form fields, etc. Also, those strings provide ways for crackers to attack our applications. Here again, some frameworks help us manage the tangled ball of strings, but most of them just reduce the problem, they don't solve it.

But those fundamental problems come straight from HTTP and HTML, not Java, right?

GV: True, but we shouldn't discount how much they hurt our productivity. Those two things together make web applications significantly more complex than more traditional counterparts. And complexity costs usin time and in quality. Managing the complexity of our systems is the fundamental problem of software development.

What is a continuation server?

GV: First, I really don't like the term continuation server, for two reasons. First, it obscures what these servers and frameworks are all about. They serve web applications. Frameworks like Seaside and Iowa employ continuations as a way of hiding the stateless, back-and-forth nature of web applications from the programmer. Continuations are used deep inside the framework; developers don't deal with them directly. The second reason I don't really like the term is that continuations are just one of the techniques that frameworks like Seaside use to provide a better web development experience.

What these servers do is to use continuations (as well as closures stored as callbacks, plus automatic tracking of session state and caching of backtracking information) to build high-level abstractions for web development, transparently handling many of the messy details that web developers are constantly wrestling with. Continuations, closures, and the common features of dynamic languages provide much more powerful tools for abstraction than Java does.

What do they bring to the table?

GV: They simplify web development. And it's a radical simplification: many of the most difficult issues of web development, things that nearly all applications punt on because they're too difficult, are handled automatically and transparently so that they're built into your applications by default. Seaside, for example, makes it easy to develop web applications that work the way users expect: proper handling of the Back button, proper session forking if the user opens multiple windows or tabs, and no "accidental double purchase" when backing up to a form result page.

In Seaside, web application code looks like the code you'd write for a desktop application. Need to ask the user a question? Call a dialog, wait for it to return, and act on the result. Of course, within the scope of that dialog call, a lot of things happen: a continuation is saved, a dialog page is sent to the browser, the user considers the question (possibly for a long time) and answers, and when Seaside receives the response it looks up the saved continuation, calls itand the dialog call returns, just as if the thread had been waiting on the response the whole time. And, in a very lightweight sense, it actually was.

Figure 8-2. A continuation server stores snapshots that have the state of web applications in progress

Advantages and Disadvantages

You've seen the primary benefit: you can look at a web application as one big piece, instead of coordinating lots of little requests. That's incredibly powerful. Continuation servers have some other capabilities as well. The Back button problem becomes much easier to solve, because if the Back button is not disabled, you can just revert the application state to the last continuation, or any previous continuation. To disable the Back button, you simply tell the browser and delete past continuations. Threading also becomes trivial, because each thread can work on a private continuation, each with an application's own resources. You don't have to worry about serializing access to a shared session.

Continuation servers work best for applications that have complex state management issues and sophisticated control flows between pages. The continuation server simplifies navigation dramatically by letting you maintain application state between pages.

Continuation servers do have a few problems:

  • The servers typically attach identifiers to URLs, and some don't like ugly URLs (though web sites like use them).

  • You must guarantee session affinity , meaning that after an initial request in a user's session, the same machine must serve the user for every subsequent request. You could overcome this problem with a distributed continuation cache, but just as distributed HTTP sessions are a problem, distributing a continuation cache may not be completely practical.

  • Continuations are more expensive than other session management techniques. I've seen little practical evidence that this has been a problem in production deployments. Still, some believe this approach will not scale as well as traditional web apps. Research on partial continuations will probably solve this problem eventually.

To me, the powerful advantages dwarf the potential disadvantages. It's possible, even likely, that a continuation server in some language will garner enough popularity to serve as a catalyst. Respected programmers Dave Thomas, Glenn Vanderburg, and David Heinemeier Hansson have all pointed to the continuation server as an important technology to watch. Hackers and Painters author, Paul Graham, used continuations in web applications with devastating effect at Viaweb, on an application that eventually became Yahoo! Store. He's also a proponent of continuation servers. Let's see an example of the most popular web framework supporting continuations.