NuthatchUpdaterImpl.mesa
Contents: Process to read from a nuthatch log and call the appropriate Nuthatch database routines to effect the updates.
The log reads and database writes will occur under a Cypress transaction.
Last Edited by: Lia, September 30, 1983 11:52 am
Last Edited by: Swinehart, July 11, 1985 12:14:26 pm PDT
DIRECTORY
DB USING [ Aborted, Error, Failure ], -- Cheery interface, in't it?
DBEnvironment USING [ ErrorCode ],
GVBasics,
IO,
Nuthatch,
NuthatchLog,
NuthatchDB,
NuthatchUpdater,
Rope,
BasicTime,
Thrush;
NuthatchUpdaterImpl: CEDAR MONITOR
IMPORTS
DB, IO, NuthatchLog, NuthatchDB, Rope
EXPORTS NuthatchUpdater = {
OPEN NuthatchDB, NuthatchUpdater;
The updater process should:
1. Opens a readonly transaction on the Nuthatch segment and the user's Nuthatch log. Under the same transaction, examine the log to get its length, and compare it against the user's log-read-point information in the database. If they're the same, no updating is necessary. Otherwise,
2. Prepare to update the database according to the log. Upgrade the transaction to read-write the database. (Still readonly for the log.) For each line in the log between the read point and the end,
. parse it into a valid database update command,
. call the Nuthatch DB routine to perform the update. Update the read-point.
. mark the transaction
3. Close the transaction.
ROPE: TYPE = Rope.ROPE;
DoLogEntries: PUBLIC ENTRY PROC [nuthatchUserHandle: Nuthatch.NuthatchUserHandle, close: BOOL] = TRUSTED {
ENABLE UNWIND => NULL;
The Cypress memorial collection of signal arguments
cTrans: REF ANYNIL;
cWhat: ATOMNIL;
cInfo: ROPENIL;
cCode: DBEnvironment.ErrorCode;
cError: BOOLFALSE;
TRUSTED {
ENABLE {
NuthatchLog.Error => SELECT code FROM
aborted => RETRY;
Transaction aborted; restart loop, but completed actions are OK.
failed => NULL; -- Communication failure or other environmental problem. give up.
In either case, the reservation that SetLogIndex makes has been cancelled.
ENDCASE;
DB.Aborted => { cTrans ← trans; RETRY; };
DB.Error => { cCode ← code; cError ← TRUE; GOTO Failed; };
DB.Failure => { cTrans ← trans; cWhat ← what; cInfo←info; GOTO Failed; };
};
logEntryStream: IO.STREAM;
token: Rope.ROPE;
userName: GVBasics.RName;
tuneNumber: INT;
recordedTime: BasicTime.GMT;
referenceCount: INT;
samples: INT;
startingSample: INT;
encryptionKeyRope: Rope.ROPE;
type: Rope.ROPE;
voiceFileID: Nuthatch.VoiceFileID;
refIDType: ROPE;
refID: ROPE;
DoProc: SAFE PROC[
voiceFileID:Nuthatch.VoiceFileID,
refIDType: Nuthatch.IDType,
refID: Nuthatch.ID,
user: GVBasics.RName,
close: BOOL
];
doType: NAT𡤀
IF cTrans#NIL THEN NuthatchDB.AbortTransaction[];
cTrans ← NIL;
NuthatchLog.SetLogIndex[nuthatchUserHandle];
DO
IF ([logEntryStream,] ← NextEntryStream[nuthatchUserHandle]).endOfLog THEN EXIT;
token ← IO.GetCedarTokenRope[logEntryStream].token;
SELECT TRUE FROM
token.Equal["Catalog"] => doType𡤃
token.Equal["Create"] => { doType𡤁 DoProc←NuthatchDB.MakeInterestEntry; };
token.Equal["AddRef"] => { doType𡤁 DoProc←NuthatchDB.AddInterest; };
token.Equal["AddUserRef"] => { doType𡤂 DoProc←NuthatchDB.AddInterest; };
token.Equal["LoseRef"] => { doType𡤁 DoProc←NuthatchDB.LoseInterest; };
token.Equal["LoseUserRef"] => { doType𡤂 DoProc←NuthatchDB.LoseInterest; };
token.Equal["RemoveRef"] => { doType𡤁 DoProc←NuthatchDB.RemoveInterestEntry; };
token.Equal["RemoveUserRef"] => { doType𡤂 DoProc←NuthatchDB.RemoveInterestEntry; };
ENDCASE;
SELECT doType FROM
1 => {
voiceFileID ← GetVoiceFileID[logEntryStream];
logEntryStream ← NextEntryStream[nuthatchUserHandle].s;
userName ←GetUserName[logEntryStream];
[refIDType, refID] ← GetRefIDS[nuthatchUserHandle];
DoProc[voiceFileID, refIDType, refID, userName, FALSE]
};
2 => {
userName ← GetUserName[logEntryStream];
[refIDType, refID] ← GetRefIDS[nuthatchUserHandle];
DoProc[NIL, refIDType, refID, userName, FALSE];
};
3 => {
voiceFileID ← GetVoiceFileID[logEntryStream];
logEntryStream ← NextEntryStream[nuthatchUserHandle].s;
userName ←GetUserName[logEntryStream];
tuneNumber ←IO.GetInt[logEntryStream];
recordedTime←logEntryStream.GetTime[];
logEntryStream ← NextEntryStream[nuthatchUserHandle].s;
referenceCount←IO.GetInt[logEntryStream];
samples←IO.GetInt[logEntryStream];
startingSample←IO.GetInt[logEntryStream];
encryptionKeyRope ← IO.PutFR["%bB %bB", IO.card[IO.GetCard[logEntryStream]], IO.card[IO.GetCard[logEntryStream]]];
type←IO.GetCedarTokenRope[logEntryStream].token;
[] ← NuthatchDB.CatalogVoiceFile[
msg: voiceFileID,
tuneNumber: tuneNumber,
recordTime: recordedTime,
creator: userName,
samples: samples,
startSample: startingSample,
encryptionKeyRope: encryptionKeyRope,
type: type,
referenceCount: referenceCount,
close: FALSE
];
};
ENDCASE => ERROR;
ENDLOOP;
NuthatchDB.MarkTransaction[]; -- be sure that updated read point is truth
NuthatchLog.UpdateLogIndex[nuthatchUserHandle]; -- If aborts, it's before getting new val.
NuthatchDB.SetReadPoint[nuthatchUserHandle, close]; -- Aborted retry does only this
EXITS
Failed => {
NuthatchDB.AbortTransaction[];
IF cError THEN DB.Error[cCode] ELSE DB.Failure[cTrans, cWhat, cInfo];
};
};
}; 
GetVoiceFileID: PROC[s: IO.STREAM] RETURNS [voiceFileID: ROPE] = {
[]←s.SkipWhitespace[]; -- flush blanks --
voiceFileID ←s.GetLineRope[];
};
NextEntryStream: PROC[nuthatchUserHandle: Nuthatch.NuthatchUserHandle]
RETURNS [s: IO.STREAMNIL, endOfLog: BOOL] = {
logEntryRope: ROPE;
[logEntryRope, endOfLog] ← NuthatchLog.ReadLogEntry[nuthatchUserHandle];
RETURN[IO.RIS[logEntryRope], endOfLog];
};
GetRefIDS: PROC[nuthatchUserHandle: Nuthatch.NuthatchUserHandle]
RETURNS [refIDType, refID: ROPENIL] = {
s: IO.STREAM = NextEntryStream[nuthatchUserHandle].s;
refIDType ← s.GetCedarTokenRope[].token;
[]←s.SkipWhitespace[]; -- flush blanks
refID ← s.GetLineRope[]; -- rest of line is ID
};
GetUserName: PROC[stream: IO.STREAM] RETURNS [userName: Rope.ROPE] = {
RETURN[stream.GetTokenRope[IO.IDProc].token];
};
}.