public static LOG_FILE_HEADER New() { LOG_FILE_HEADER ret = new LOG_FILE_HEADER(); ret.Initialize(); return(ret); }
private unsafe void CreateWriteAheadLogFile() { if (TrinityConfig.ReadOnly) { return; } string path = WriteAheadLogFilePath; Log.WriteLine(LogLevel.Info, "Creating write-ahead log file {0}", path); DropWriteAheadLogFile(); if (File.Exists(path)) { BackupWriteAheadLogFile(path); } void *new_fp = null; if (0 != Stdio._wfopen_s(out new_fp, path, "wb")) { Log.WriteLine(LogLevel.Error, "Cannot open the log file"); return; } LOG_FILE_HEADER header = LOG_FILE_HEADER.New(); GetTrinityImageSignature(&header.LOG_ASSOCIATED_IMAGE_SIGNATURE); CStdio.fwrite(&header, (ulong)sizeof(LOG_FILE_HEADER), 1, new_fp); CStdio.fflush(new_fp); _update_write_ahead_log_file(path, new_fp); }
internal bool CompatibilityCheck() { LOG_FILE_HEADER default_header = LOG_FILE_HEADER.New(); fixed(LOG_FILE_HEADER *p_lhs = &this) { /* Currently we make a strict compare of magic header and version. */ return(Memory.Compare((byte *)p_lhs, (byte *)&default_header, 8)); } }
/// <summary> /// Opens the log file in read mode and replay the actions inside, /// and when the logs are synced, save the storage to an image, then /// drop the old log file. /// This method always target [PRIMARY] storage slot. /// </summary> 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); } }