• No results found

Dynamic software updating

In document Interactive functional programming (Page 70-72)

As we saw in §3.7above, retroactive update relies on a closed-world assumption: that every past interaction falls within the scope of the update. When this assumption cannot be justified, we must pursue update solutions which are not so committed to reinventing the entire past. Dynamic software updating (DSU) seeks a compromise between the unrestricted chaos of live coding, and the impractical rigidity of global retroactive update. The goal is to balance flexibility – permitting as broad a class of updates as possible – with safety, which in early DSU work simply meant that subsequent execution would not fail with a runtime type error as a consequence of the update. We refer the reader to Hicks and Nettles for a survey of techniques [HN05]. Here we describe the Proteus system of Stoyle et al. [SHB+07] and its successor Proteus-tx by Neamtiu et al.

[NHFP08], which we take to be roughly representative of the state of the art. One challenge facing DSU is that, while consistency with a from-scratch run is evidently too strong a property, merely guaranteeing that an update will not introduce a type error is too weak.

First, we review the version of DSU which only guarantees type safety. Then we discuss its shortcomings, with an example, and consider an improved approach and its connection to retroactive update. In the Proteus approach to DSU, the user first specifies a collection of modifications called an update which they intend to apply to a running system. An update is a set of new bindings for selected global symbols, with no fine- grained information about how they changed. (This is in contrast with interactive programming, where changes are syntactic deltas.) DSU also permits data type definitions to change, which we do not support. The update must then include a “migration” function which will be used, when the update is applied, to transform values created under the old data type definition to the new one.

An update point is a static program location specified by the user. At runtime, if an update is pending and an update point is reached for which the update is deemed type-safe, the update is performed and then execution resumed. Otherwise execution continues as normal. One thing which distinguishes DSU from retroactive update is that invocations of functions which are active when the update is applied will finish executing with the old version of the code, and only subsequent invocations will use the new code; and of course none of the effects of prior invocations will be retroactively amended to reflect the changes. For this mixture of old and new behaviour to be type-safe, DSU relies on a property called “con-freeness”. Intuitively, the idea behind con-freeness (for an update to a data type definition, for example) is that old code that will resume after the update will not concretely manipulate values of that type. Accessing a field or pattern- matching are examples of concrete usage; simply mentioning a value of that type, without relying on its representation, does not compromise con-freeness. Notions of con-freeness are also defined for function and variable update, and then the authors give an algorithm which statically approximates con-freeness for a given update site.

This form of DSU ensures that updating is sound from a typing point of view. However it does not rule out other runtime errors which would not arise under LambdaCalc-style retroactive update, but only under a hybrid execution of old and new code. Figure3.6shows an example from Neamtiu et al. [NHFP08]. Suppose the program on the left is edited into the program on the right by moving the call ofginhinto the body off.

For the sake of simplicity also suppose that this is the only call offin the program. Now, if we dynamically

update the program during an invocation ofh, just beforehmakes the call tof(at the point indicated in the

figure), then that call tofwill invoke the new version off, which will callg. Then, the old definition ofh

will finish executing, resulting ingbeing called twice, even though this is not possible under either version

of the program alone. Ifghas side-effects, this could be catastrophic, and moreover such hybrid executions

are very hard to reason about. DSU in this form is nothing more than a type-safe version of live coding.

proc f () = proc f () =

... ...

g();

proc h () = proc h () =

... ...

// update point // update point

f(); f();

g();

Figure 3.6 Type-safe but potentially incorrect update via DSU

This observation prompted Neamtiu et al. to develop the transactional version consistency approach to DSU [ibid.]. The idea of transactional version consistency is to allow the user to designate blocks of code as transactions whose execution will always be attributable to a single version of the program. They use a contextual effect system which, for any expression, statically computes approximations of the effect of the computation that has already taken place (the prior effect), and of the effect that has yet to take place (the future effect). An update is permitted during the execution of a transaction if it will be “as though” that transaction had run under the new code from the outset, or under the old code from the outset, but not under a hybrid of the two, using the prior and future effects of the update point to decide this conservatively. If neither situation obtains, then either the update or the transaction must be rejected.

This transactional approach is quite effective for so-called “long-running” applications, such as online retail websites, which commit many transactions to a database. In practice, these systems are actually structured around relatively short-lived, concurrently executing user sessions. The long-living parts of the application are the external effects of code – changes to data, and so on. Each session either aborts or executes to completion, committing changes to a shared persistent store. For these systems, an update semantically equivalent to re-running all past transactions with the new code against an empty database might not be feasible to execute for performance reasons or more likely because the business model does not permit “changing the past”. For such applications, transactional version consistency is at least the basis of a more

structured approach to DSU, although the possibility remains of things going wrong for business logic that extends over multiple transactions.

Stoyle et al. attribute the difficulties with DSU which transactional version consistency addresses to the flexibility of being able to change the program in the future [SHB+07]. But from the vantage point of a

system like LambdaCalc, they can equally be attributed to the inflexibility of being unable to change the program in the past. Indeed, it seems that transactional version consistency is a tacit acknowledgment of the need, even in long-running systems, for some of the semantic consistency of retroactive update. In Future Work, §7.2.2, we discuss an approach to DSU based on transaction-level retroactive update, rather than transactional version consistency. The idea is to use a more “local” version of retroactive update, for which the semantic correctness criterion is consistency with a from-scratch run of an individual transaction or session, not of an all-encompassing closed program.

In document Interactive functional programming (Page 70-72)