Copyright (C) 1994, Digital Equipment Corp.
Created by Carsten Weich and Greg Nelson
A LogManager provides an object which manages readers and writers
for the log and checkpoint files used by stable objects.
The default log manager stores the stable state in the file system. Since the default is satisfactory for most applications, most clients of stable objects don't need to use the LogManager interface directly.
A situation in which you might want to use this interface is
if you want to test whether a checkpoint is available before initializing
a stable object. In this case the recoverable method of the
log manager will be useful to you. As another example, if you
don't want to use the file system for storing the stable state,
you will need to study the specifications in this interface and
implement your own log manager that stores the stable state in
your preferred form of stable storage.
INTERFACEALogManager ; IMPORT Pathname, Wr, Rd, OSError; TYPE T <: Public; Public = OBJECT METHODS beginCheckpoint(nm: Pathname.T): Wr.T (* new checkpoint *) RAISES {OSError.E}; endCheckpoint(nm: Pathname.T):Wr.T (* new log *) RAISES {OSError.E}; reOpenLog(nm: Pathname.T): Wr.T RAISES {OSError.E}; recover(nm: Pathname.T; VAR log, checkp: Rd.T) RAISES {OSError.E}; recoverable(nm: Pathname.T): BOOLEAN RAISES {OSError.E}; emptyLog(nm: Pathname.T): BOOLEAN RAISES {OSError.E}; dispose(nm: Pathname.T) RAISES {OSError.E}; END; DefaultPublic = T OBJECT METHODS init(): Default END; Default <: DefaultPublic; VAR default: Default; END LogManager.
LogManager.T manages a repository of named stable snapshots.
A snapshot consists of two sequences of bytes, a {\it checkpoint}
and a {\it redo log}. The repository must be stable; that is,
it must survive program crashes. Here are specifications for
the methods of a LogManager.T named lm:
The beginCheckpoint and endCheckpoint methods are
used to write a new stable snapshot. The call
lm.beginCheckpoint(nm)
returns a writer whose target is the checkpoint named nm
in lm's repository. The call
lm.endCheckpoint(nm)
should be made only after a previous call to lm.beginCheckpoint(nm).
The endCheckpoint method commits the bytes that have been written
to the writer that was returned by beginCheckpoint, making them
become the new checkpoint. The endCheckpoint method also empties
the redo log and returns a writer that can be used to extend
the now-empty redo log.
Therefore, to make a new checkpoint, you should execute the following steps:
wr1 := lm.beginCheckpoint(nm);
write a new checkpoint to wr1;
wr2 := lm.endCheckpoint(nm);
new checkpoint is made; log updates to wr2
If the application exits or crashes before the call to endCheckpoint,
any bytes written to wr1 will be discarded, and the previous
checkpoint will not be changed. (Of course these steps are performed
by the Checkpoint procedure in the generic Stable interface, so
there is no reason for the typical client of stable objects to
recode this procedure. But if you are implementing your own
log managers, it is important to know how the methods will
be used by your clients.)
An application may close the writer returned by endCheckpoint
(for example, to free up file descriptors or other resources associated
with inactive stable objects). If this writer has been closed,
the call
lm.reopenLog(nm)
will return a writer which will append to the current redo
log.
The call
lm.recoverable(nm)
returns TRUE if and only if the repository managed by lm contains
a snapshot named nm.
The call
lm.emptyLog(nm)
returns TRUE if and only if the repository managed by lm contains
a snapshot nm which has an empty log. The log will be empty if the
program that created a stable object did a checkpoint right
before terminating. A non empty log might indicate a crashed program.
emptyLog() will raise the exception if there is no snapshot nm.
The call
lm.recover(nm, cprd, logrd)
sets cprd and logrd to readers whose sources are the
checkpoint and redo log named nm, respectively, assuming
a snapshot exists under that name in the repository managed
by lm.
The call
lm.dispose(nm)
discards any snapshot named nm from lm's repository and
reclaims any associated stable storage.
So much for the methods of a general LogManager.T. Almost all
clients will use a LogManager.Default, which uses the
ordinary file system as its source of stable storage.
A LogManager.Default must be initialized by the client;
to obtain one, call
VAR lm := NEW(LogManager.Default).init(); ...
The methods of a LogManager.Default interpret nm as a directory
in the file system, in which they expects to find files containing a
checkpoint and log. The beginCheckpoint method will create the
directory if necessary. File renaming is used to make checkpoint
atomic with respect to crashes.
The variable LogManager.default is initialized by the
LogManager module to a valid LogManager.Default.