/// <summary>
        /// Only for unit test purpose.
        /// </summary>
        internal void _reset_write_ahead_log_status()
        {
            lock (m_lock)
            {
                if (m_logfile != null)
                {
                    CStdio.C_fclose(m_logfile);
                }

                _update_write_ahead_log_file(null, null);
            }
        }
 /// <summary>
 /// Tries to close the WAL file.
 /// Only called when the local storage is being disposed.
 /// </summary>
 private unsafe void _CloseWriteAheadLogFile()
 {
     lock (m_lock)
     {
         if (m_logfile == null)
         {
             return;
         }
         if (0 != CStdio.C_fclose(m_logfile))
         {
             Log.WriteLine(LogLevel.Error, "Failed to close the log file");
         }
     }
 }
        /// <summary>
        /// Drops the current log file and clean up the member variables.
        /// </summary>
        private unsafe void _DropWriteAheadLogFile()
        {
            lock (m_lock)
            {
                if (TrinityConfig.ReadOnly)
                {
                    return;
                }

                if (m_logfile == null)
                {
                    return;
                }

                Debug.Assert(m_logfile != null);
                Debug.Assert(m_logfile_path != null);

                Log.WriteLine(LogLevel.Info, "Dropping write-ahead-log file {0}", m_logfile_path);

                if (0 != CStdio.C_fclose(m_logfile))
                {
                    Log.WriteLine(LogLevel.Error, "Failed to close the log file");
                }

                try
                {
                    File.Delete(m_logfile_path);
                }
                catch (Exception ex)
                {
                    Log.WriteLine(LogLevel.Error, "Failed to delete the log file: {0}", ex);
                }

                _update_write_ahead_log_file(null, null);
            }
        }
        /// <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(&current_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 *)&current_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);
            }
        }