in src/Trinity.Core/Storage/LocalMemoryStorage/LocalMemoryStorage.WriteAheadLog.cs [352:521]
private void _LoadWriteAheadLogFile()
{
lock (m_lock)
{
if (TrinityConfig.ReadOnly)
return;
string path = _WriteAheadLogFilePath();
Log.WriteLine(LogLevel.Debug, "Loading write-ahead log file {0}", path);
LOG_FILE_HEADER header = LOG_FILE_HEADER.New();
TRINITY_IMAGE_SIGNATURE current_sig = new TRINITY_IMAGE_SIGNATURE();
LOG_RECORD_HEADER record_header = new LOG_RECORD_HEADER();
long record_cnt = 0;
byte[] cell_buff = new byte[128];
void* new_fp = null;
bool ver_compatible = true;
bool img_compatible = true;
GetTrinityImageSignature(¤t_sig);
_DropWriteAheadLogFile();
if (!File.Exists(path))
{
Log.WriteLine(LogLevel.Debug, "Write ahead log doesn't exist, quit loading.");
return;
}
if (0 != Stdio._wfopen_s(out new_fp, path, "rb"))
{
Log.WriteLine(LogLevel.Fatal, "Cannot open write ahead log for read. Exiting.");
goto load_fail;
}
/* Read log header */
if (1 != CStdio.C_fread(&header, (ulong)sizeof(LOG_FILE_HEADER), 1, new_fp))
{
Log.WriteLine(LogLevel.Fatal, "Cannot read write-ahead-log header. Exiting.");
goto load_fail;
}
ver_compatible = header.CompatibilityCheck();
img_compatible = Memory.Compare((byte*)&header.LOG_ASSOCIATED_IMAGE_SIGNATURE, (byte*)¤t_sig, sizeof(TRINITY_IMAGE_SIGNATURE));
if (!ver_compatible || !img_compatible)
{
/* The log is not ours. Ignore if it's empty. */
if (0 == CStdio.C_feof(new_fp))
{
Log.WriteLine(LogLevel.Warning, "Found incompatible empty write-ahead-log file, ignoring.");
CStdio.C_fclose(new_fp);
return;
}
else if (this.CellCount != 0)
{
goto load_incompatible;
}
/* Otherwise, (CellCount is 0), it indicates that we're recovering from a fresh start. */
}
Log.WriteLine(LogLevel.Info, "Reading log file.");
while (1 == CStdio.C_fread(&record_header, (ulong)sizeof(LOG_RECORD_HEADER), 1, new_fp))
{
if (record_header.CONTENT_LEN >= 0)
{
/* Ensure space for the cell buffer */
if (record_header.CONTENT_LEN > cell_buff.Length)
{
if (record_header.CONTENT_LEN < 1<<20)
{
cell_buff = new byte[record_header.CONTENT_LEN * 2];
}
else
{
cell_buff = new byte[record_header.CONTENT_LEN];
}
}
fixed (byte* p_buff = cell_buff)
{
if (1 != CStdio.C_fread(p_buff, (ulong)record_header.CONTENT_LEN, 1, new_fp) && record_header.CONTENT_LEN != 0)
{
Log.WriteLine(LogLevel.Error, "Incomplete write-ahead-log record at the end of file");
break;
}
if (false == LOG_RECORD_HEADER.CWriteAheadLogValidateChecksum(&record_header, p_buff))
{
Log.WriteLine(LogLevel.Fatal, "Checksum mismatch for log record #{0}", record_cnt);
goto load_fail;
}
this.SaveCell(record_header.CELL_ID, p_buff, record_header.CONTENT_LEN, record_header.CELL_TYPE);
}
}
else /* if (record_header.CONTENT_LEN < 0) */
{
if (false == LOG_RECORD_HEADER.CWriteAheadLogValidateChecksum(&record_header, null))
{
Log.WriteLine(LogLevel.Fatal, "Checksum mismatch for log record #{0}", record_cnt);
goto load_fail;
}
this.RemoveCell(record_header.CELL_ID);
}
++record_cnt;
}
goto load_success;
////////////////////////////////////////
load_incompatible:
if (ver_compatible)
{
Log.WriteLine(LogLevel.Fatal, "The log file is incompatible with the current version. Cannot recover.");
}
if (img_compatible)
{
Log.WriteLine(LogLevel.Fatal, "The log file has a different signature than the current image. Cannot recover.");
}
goto load_fail;
////////////////////////////////////////
load_success:
Log.WriteLine(LogLevel.Info, "Write-ahead-log successfully loaded. Recovered {0} records.", record_cnt);
if (0 != CStdio.C_fclose(new_fp))
{
Log.WriteLine(LogLevel.Error, "Cannot close the write-ahead-log file. Logging disabled.");
return;
}
/* Only save storage when the log is not empty. */
if (record_cnt == 0 || TrinityErrorCode.E_SUCCESS == SaveStorage())
{
/* Save storage succeeded. Dropping old logs now. */
try
{
File.Delete(path);
}
catch (Exception ex)
{
Log.WriteLine(LogLevel.Error, "Failed to delete the old logs: {0}", ex);
}
}
else
{
/* Save storage failed. */
Log.WriteLine(LogLevel.Fatal, "Failed to save the recovered storage. The old log is retained");
goto load_fail;
}
return;
////////////////////////////////////////
load_fail:
if (new_fp != null)
CStdio.C_fclose(new_fp);
Environment.Exit(-1);
}
}