Suppose a live system that is to be upgraded. The upgrade alters a
bunch of things (methods, classes).
For example:
Class A is redefined
; undefined state here
Method M(A) is redefined **
Now at the marked spot between those two redefinitions we need to
avoid calling the old method on the new class (slot semantics may have
changed for example).
How can this be avoided? Is there a way to ensure that the
redefinitions occur in an atomic way? Think
(with-upgrade-transaction
(defclass ...
(defmethod ...
Of course uninterning the old definitions beforehand wouldn't be
acceptable either since they might be called in the meantime.
Maybe the MOP can help?
Leslie
On Dec 27, 4:42 am, "Leslie P. Polzer" <·············@gmx.net> wrote:
> How can this be avoided? Is there a way to ensure that the
> redefinitions occur in an atomic way?
What? That would require a big lock around all of the things being
updated.
Can you accept the performance penalties?
Think
>
> (with-upgrade-transaction
> (defclass ...
> (defmethod ...
Anyone using that class or method would have to acquire the same lock
that is used by WITH-UPGRADE-TRANSACTION.
What's worse can't necessarily predict which subsets of classes,
methods or other symbols might be included in a given transaction. In
the worst case, you might want to upgrade everything atomically.
This means having a big, global lock around the use of everything.
There is such a thing as too high a price to pay for atomicity.
"Leslie P. Polzer" <·············@gmx.net> writes:
> How can this be avoided? Is there a way to ensure that the
> redefinitions occur in an atomic way? Think
If your implementation is multithreaded, the multithreading library
will probably support something like a WITHOUT-INTERRUPTS macro. If
it's not multithreaded, you don't have a problem, of course.
...Peder...
--
This must be Thursday. I never could get the hang of Thursdays.
LPP> Now at the marked spot between those two redefinitions we need to
LPP> avoid calling the old method on the new class (slot semantics may have
LPP> changed for example).
we need to avoid calling *anything* during upgrade process if we want really
safe upgrade, because we can have current execution stuff incompatible with
new method or class definitions.
a "big lock" around your main processing code and upgrade procedure will
help.
so you stop all your processing (finishing stuff you're doing and not
starting new stuff), then upgrade, then start processing again.
in case of multithreaded server something like read-write lock is needed --
lock for reading when you do processing, lock for writing when you do
upgrade.
LPP> Maybe the MOP can help?
if few seconds (while upgrade goes) of downtime is not acceptable to you,
the only way would be limiting changes you're making to ensure they are not
breaking anything.
analyzing stuff for race-condition safety during upgrades is pretty complex,
so i think it's better to use two servers if you cannot tolerate delays :).
Leslie P. Polzer wrote:
> Suppose a live system that is to be upgraded. The upgrade alters a
> bunch of things (methods, classes).
>
> For example:
>
> Class A is redefined
> ; undefined state here
> Method M(A) is redefined **
>
> Now at the marked spot between those two redefinitions we need to
> avoid calling the old method on the new class (slot semantics may have
> changed for example).
>
> How can this be avoided? Is there a way to ensure that the
> redefinitions occur in an atomic way? Think
>
> (with-upgrade-transaction
> (defclass ...
> (defmethod ...
>
> Of course uninterning the old definitions beforehand wouldn't be
> acceptable either since they might be called in the meantime.
>
> Maybe the MOP can help?
Probably not; more than just threading issues here; how to cleanly
upgrade all instances of the redefined class, etc.
If you need to do this seamlessly, it will probably look something
like this:
- start up new code running parallel to old code
- if necessary, use a back channel to synchronize the code bases
- have the new code accept all new connections
- instruct the old code to stop accepting new connections
- when the old connections complete or timeout, shut down the old code
If you don't need complete transparency, WITH-UPGRADE-TRANSACTION
would look like
- have the old code stop communicating with the outside world
- load in the new definitions; upgrade all old class instances
- restart communication
This is one thing the unix filesystem got right (and MS botched):
files are unlinked and then garbage collected -- programs shouldn't
have to close the file before it can be deleted or overwritten. Same
thing goes for software upgrades.
Later,
Daniel
Disclaimer: I've never done this with a real system; my opinions are
based on experience with lock-free algorithms.