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 wr2If 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
.