Skip to content

File log_recovery_redo.hpp

File List > cubrid > src > transaction > log_recovery_redo.hpp

Go to the documentation of this file

/*
 * Copyright 2008 Search Solution Corporation
 * Copyright 2016 CUBRID Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

#ifndef _LOC_RECOVERY_REDO_HPP_
#define _LOC_RECOVERY_REDO_HPP_

#include "log_compress.h"
#include "log_lsa.hpp"
#include "log_reader.hpp"
#include "log_record.hpp"
#include "log_recovery.h"
#include "page_buffer.h"
#include "perf_monitor_trackers.hpp"
#include "scope_exit.hpp"
#include "system_parameter.h"
#include "type_helper.hpp"

struct log_rv_redo_context
{
  log_reader m_reader { LOG_CS_SAFE_READER };
  LOG_ZIP m_redo_zip;
  LOG_ZIP m_undo_zip;
  const LOG_LSA m_end_redo_lsa = NULL_LSA;
  const log_reader::fetch_mode m_reader_fetch_page_mode = log_reader::fetch_mode::FORCE;

  log_rv_redo_context () = delete;
  log_rv_redo_context (const log_lsa &end_redo_lsa, log_reader::fetch_mode fetch_mode);
  ~log_rv_redo_context ();

  log_rv_redo_context (const log_rv_redo_context &);
  log_rv_redo_context (log_rv_redo_context &&) = delete;

  log_rv_redo_context &operator= (const log_rv_redo_context &) = delete;
  log_rv_redo_context &operator= (log_rv_redo_context &&) = delete;
};

template <typename T>
struct log_rv_redo_rec_info
{
  const LOG_LSA m_start_lsa = NULL_LSA;
  const LOG_RECTYPE m_type = LOG_SMALLER_LOGREC_TYPE;
  const T m_logrec;

  log_rv_redo_rec_info () = delete;
  log_rv_redo_rec_info (const log_lsa lsa, LOG_RECTYPE type, const T &t);

  log_rv_redo_rec_info (const log_rv_redo_rec_info &) = delete;
  log_rv_redo_rec_info (log_rv_redo_rec_info &&) = delete;

  log_rv_redo_rec_info &operator= (const log_rv_redo_rec_info &) = delete;
  log_rv_redo_rec_info &operator= (log_rv_redo_rec_info &&) = delete;
};

/*
 * helper functions to assist with log recovery redo
 */

/*
 * template declarations
 */

/* recovery data out of a log record
 */
template <typename T>
const LOG_DATA &log_rv_get_log_rec_data (const T &log_rec);

/*
 */
template <typename T>
MVCCID log_rv_get_log_rec_mvccid (const T &log_rec);

/*
 */
template <typename T>
VPID log_rv_get_log_rec_vpid (const T &log_rec);

/*
 */
template <typename T>
int log_rv_get_log_rec_redo_length (const T &log_rec);

/*
 */
template <typename T>
int log_rv_get_log_rec_offset (const T &log_rec);

/*
 */
template <typename T>
rvfun::fun_t log_rv_get_fun (const T &, LOG_RCVINDEX rcvindex);

/*
 * template implementations
 */

template <typename T>
const LOG_DATA &log_rv_get_log_rec_data (const T &log_rec)
{
  static_assert (sizeof (T) == 0, "should not be called");
  static constexpr log_data LOG_DATA_INITIALIZER =
  {
    /*.rcvindex =*/ RV_NOT_DEFINED,
    /*.pageid =*/ NULL_PAGEID,
    /*.offset =*/ -1,
    /*.volid =*/ NULL_VOLID
  };
  static const LOG_DATA dummy = LOG_DATA_INITIALIZER;
  return dummy;
}

template <>
inline const LOG_DATA &log_rv_get_log_rec_data<LOG_REC_MVCC_UNDOREDO> (const LOG_REC_MVCC_UNDOREDO &log_rec)
{
  return log_rec.undoredo.data;
}

template <>
inline const LOG_DATA &log_rv_get_log_rec_data<LOG_REC_UNDOREDO> (const LOG_REC_UNDOREDO &log_rec)
{
  return log_rec.data;
}

template <>
inline const LOG_DATA &log_rv_get_log_rec_data<LOG_REC_MVCC_REDO> (const LOG_REC_MVCC_REDO &log_rec)
{
  return log_rec.redo.data;
}

template <>
inline const LOG_DATA &log_rv_get_log_rec_data<LOG_REC_REDO> (const LOG_REC_REDO &log_rec)
{
  return log_rec.data;
}

template <>
inline const LOG_DATA &log_rv_get_log_rec_data<LOG_REC_RUN_POSTPONE> (const LOG_REC_RUN_POSTPONE &log_rec)
{
  return log_rec.data;
}

template <>
inline const LOG_DATA &log_rv_get_log_rec_data<LOG_REC_COMPENSATE> (const LOG_REC_COMPENSATE &log_rec)
{
  return log_rec.data;
}

template <typename T>
MVCCID log_rv_get_log_rec_mvccid (const T &)
{
  static_assert (sizeof (T) == 0, "should not be called");
  return MVCCID_NULL;
}

template <>
inline MVCCID log_rv_get_log_rec_mvccid<LOG_REC_MVCC_UNDOREDO> (const LOG_REC_MVCC_UNDOREDO &log_rec)
{
  return log_rec.mvccid;
}

template <>
inline MVCCID log_rv_get_log_rec_mvccid<LOG_REC_UNDOREDO> (const LOG_REC_UNDOREDO &log_rec)
{
  return MVCCID_NULL;
}

template <>
inline MVCCID log_rv_get_log_rec_mvccid<LOG_REC_MVCC_REDO> (const LOG_REC_MVCC_REDO &log_rec)
{
  return log_rec.mvccid;
}

template <>
inline MVCCID log_rv_get_log_rec_mvccid<LOG_REC_REDO> (const LOG_REC_REDO &log_rec)
{
  return MVCCID_NULL;
}

template <>
inline MVCCID log_rv_get_log_rec_mvccid<LOG_REC_RUN_POSTPONE> (const LOG_REC_RUN_POSTPONE &log_rec)
{
  return MVCCID_NULL;
}

template <>
inline MVCCID log_rv_get_log_rec_mvccid<LOG_REC_COMPENSATE> (const LOG_REC_COMPENSATE &log_rec)
{
  return MVCCID_NULL;
}

template <typename T>
VPID log_rv_get_log_rec_vpid (const T &log_rec)
{
  static_assert (sizeof (T) == 0, "should not be called");
  return VPID_INITIALIZER;
}

template <>
inline VPID log_rv_get_log_rec_vpid<LOG_REC_MVCC_UNDOREDO> (const LOG_REC_MVCC_UNDOREDO &log_rec)
{
  return
  {
    log_rec.undoredo.data.pageid,
    log_rec.undoredo.data.volid
  };
}

template <>
inline VPID log_rv_get_log_rec_vpid<LOG_REC_UNDOREDO> (const LOG_REC_UNDOREDO &log_rec)
{
  return
  {
    log_rec.data.pageid,
    log_rec.data.volid
  };
}

template <>
inline VPID log_rv_get_log_rec_vpid<LOG_REC_MVCC_REDO> (const LOG_REC_MVCC_REDO &log_rec)
{
  return
  {
    log_rec.redo.data.pageid,
    log_rec.redo.data.volid
  };
}

template <>
inline VPID log_rv_get_log_rec_vpid<LOG_REC_REDO> (const LOG_REC_REDO &log_rec)
{
  return
  {
    log_rec.data.pageid,
    log_rec.data.volid
  };
}

template <>
inline VPID log_rv_get_log_rec_vpid<LOG_REC_RUN_POSTPONE> (const LOG_REC_RUN_POSTPONE &log_rec)
{
  return
  {
    log_rec.data.pageid,
    log_rec.data.volid
  };
}

template <>
inline VPID log_rv_get_log_rec_vpid<LOG_REC_COMPENSATE> (const LOG_REC_COMPENSATE &log_rec)
{
  return
  {
    log_rec.data.pageid,
    log_rec.data.volid
  };
}

template <typename T>
int log_rv_get_log_rec_redo_length (const T &log_rec)
{
  static_assert (sizeof (T) == 0, "should not be called");
  return -1;
}

template <>
inline int log_rv_get_log_rec_redo_length<LOG_REC_MVCC_UNDOREDO> (const LOG_REC_MVCC_UNDOREDO &log_rec)
{
  return log_rec.undoredo.rlength;
}

template <>
inline int log_rv_get_log_rec_redo_length<LOG_REC_UNDOREDO> (const LOG_REC_UNDOREDO &log_rec)
{
  return log_rec.rlength;
}

template <>
inline int log_rv_get_log_rec_redo_length<LOG_REC_MVCC_REDO> (const LOG_REC_MVCC_REDO &log_rec)
{
  return log_rec.redo.length;
}

template <>
inline int log_rv_get_log_rec_redo_length<LOG_REC_REDO> (const LOG_REC_REDO &log_rec)
{
  return log_rec.length;
}

template <>
inline int log_rv_get_log_rec_redo_length<LOG_REC_RUN_POSTPONE> (const LOG_REC_RUN_POSTPONE &log_rec)
{
  return log_rec.length;
}

template <>
inline int log_rv_get_log_rec_redo_length<LOG_REC_COMPENSATE> (const LOG_REC_COMPENSATE &log_rec)
{
  return log_rec.length;
}

template <typename T>
int log_rv_get_log_rec_offset (const T &log_rec)
{
  static_assert (sizeof (T) == 0, "should not be called");
  return -1;
}

template <>
inline int log_rv_get_log_rec_offset<LOG_REC_MVCC_UNDOREDO> (const LOG_REC_MVCC_UNDOREDO &log_rec)
{
  return log_rec.undoredo.data.offset;
}

template <>
inline int log_rv_get_log_rec_offset<LOG_REC_UNDOREDO> (const LOG_REC_UNDOREDO &log_rec)
{
  return log_rec.data.offset;
}

template <>
inline int log_rv_get_log_rec_offset<LOG_REC_MVCC_REDO> (const LOG_REC_MVCC_REDO &log_rec)
{
  return log_rec.redo.data.offset;
}

template <>
inline int log_rv_get_log_rec_offset<LOG_REC_REDO> (const LOG_REC_REDO &log_rec)
{
  return log_rec.data.offset;
}

template <>
inline int log_rv_get_log_rec_offset<LOG_REC_RUN_POSTPONE> (const LOG_REC_RUN_POSTPONE &log_rec)
{
  return log_rec.data.offset;
}

template <>
inline int log_rv_get_log_rec_offset<LOG_REC_COMPENSATE> (const LOG_REC_COMPENSATE &log_rec)
{
  return log_rec.data.offset;
}

template <typename T>
rvfun::fun_t log_rv_get_fun (const T &, LOG_RCVINDEX rcvindex)
{
  static_assert (sizeof (T) == 0, "should not be called");
  return nullptr;
}

template <>
inline rvfun::fun_t log_rv_get_fun<LOG_REC_MVCC_UNDOREDO> (const LOG_REC_MVCC_UNDOREDO &, LOG_RCVINDEX rcvindex)
{
  return RV_fun[rcvindex].redofun;
}

template <>
inline rvfun::fun_t log_rv_get_fun<LOG_REC_UNDOREDO> (const LOG_REC_UNDOREDO &, LOG_RCVINDEX rcvindex)
{
  return RV_fun[rcvindex].redofun;
}

template <>
inline rvfun::fun_t log_rv_get_fun<LOG_REC_MVCC_REDO> (const LOG_REC_MVCC_REDO &, LOG_RCVINDEX rcvindex)
{
  return RV_fun[rcvindex].redofun;
}

template <>
inline rvfun::fun_t log_rv_get_fun<LOG_REC_REDO> (const LOG_REC_REDO &, LOG_RCVINDEX rcvindex)
{
  return RV_fun[rcvindex].redofun;
}

template <>
inline rvfun::fun_t log_rv_get_fun<LOG_REC_RUN_POSTPONE> (const LOG_REC_RUN_POSTPONE &, LOG_RCVINDEX rcvindex)
{
  return RV_fun[rcvindex].redofun;
}

template <>
inline rvfun::fun_t log_rv_get_fun<LOG_REC_COMPENSATE> (const LOG_REC_COMPENSATE &, LOG_RCVINDEX rcvindex)
{
  // yes, undo
  return RV_fun[rcvindex].undofun;
}

#if !defined(NDEBUG)
DBG_REGISTER_PARSE_TYPE_NAME (LOG_REC_MVCC_UNDOREDO)
DBG_REGISTER_PARSE_TYPE_NAME (LOG_REC_UNDOREDO)
DBG_REGISTER_PARSE_TYPE_NAME (LOG_REC_MVCC_REDO)
DBG_REGISTER_PARSE_TYPE_NAME (LOG_REC_REDO)
DBG_REGISTER_PARSE_TYPE_NAME (LOG_REC_RUN_POSTPONE)
DBG_REGISTER_PARSE_TYPE_NAME (LOG_REC_COMPENSATE)
#endif

/* log_rv_redo_record_debug_logging - utility function which prints debug information
 *                        templated in order to be able to specifically print out user readable name
 *                        of the log record structure
 */
template <typename T>
void log_rv_redo_record_debug_logging (const log_lsa &rcv_lsa, LOG_RCVINDEX rcvindex, const vpid &rcv_vpid,
                       const log_rcv &rcv)
{
#if !defined(NDEBUG)
  if (prm_get_bool_value (PRM_ID_LOG_TRACE_DEBUG))
    {
      constexpr const char *const log_rec_name = dbg_parse_type_name<T> ();
      fprintf (stdout,
           "TRACE REDOING[%s]: LSA = %lld|%d, Rv_index = %s,\n"
           "      volid = %d, pageid = %d, offset = %d,\n", log_rec_name, LSA_AS_ARGS (&rcv_lsa),
           rv_rcvindex_string (rcvindex), rcv_vpid.volid, rcv_vpid.pageid, rcv.offset);
      if (rcv.pgptr != nullptr)
    {
      const log_lsa *const rcv_page_lsaptr = pgbuf_get_lsa (rcv.pgptr);
      assert (rcv_page_lsaptr != nullptr);
      fprintf (stdout, "      page_lsa = %lld|%d\n", LSA_AS_ARGS (rcv_page_lsaptr));
    }
      else
    {
      fprintf (stdout, "      page_lsa = %lld|%d\n", -1LL, -1);
    }
      fflush (stdout);
    }
#endif /* !NDEBUG */
}

/*
 * log_rv_get_log_rec_redo_data - GET UNZIPPED [and DIFFED, if needed] REDO LOG DATA FROM LOG
 *
 * return: error code
 *
 *   thread_p(in):
 *   log_pgptr_reader(in/out): log reader
 *   log_rec(in): log record structure with info about the location and size of the data in the log page
 *   rcv(in/out): Recovery structure for recovery function
 *   log_rtype(in): log record type needed to check if diff information is needed or should be skipped
 *   undo_unzip_support(out): extracted undo data support structure (set as a side effect)
 *   redo_unzip_support(out): extracted redo data support structure (set as a side effect); required to
 *                    be passed by address because it also functions as an internal working buffer
 */
template <typename T>
int log_rv_get_log_rec_redo_data (THREAD_ENTRY *thread_p, log_rv_redo_context &redo_context,
                  const log_rv_redo_rec_info<T> &record_info, log_rcv &rcv)
{
  static_assert (sizeof (T) == 0, "should not be called");
  return -1;
}

template <>
inline int
log_rv_get_log_rec_redo_data<LOG_REC_UNDOREDO> (THREAD_ENTRY *thread_p, log_rv_redo_context &redo_context,
    const log_rv_redo_rec_info<LOG_REC_UNDOREDO> &record_info, log_rcv &rcv)
{
  /* current log reader position is aligned at the undo data, redo data follows (aligned) the undo data
   */
  const bool need_diff_with_undo =
      (record_info.m_type == LOG_DIFF_UNDOREDO_DATA || record_info.m_type == LOG_MVCC_DIFF_UNDOREDO_DATA);
  if (need_diff_with_undo)
    {
      /* for the diff log records, undo and redo data must be read, the diff be applied between the undo and the redo
       * to reconstruct the actual redo data
       */
      bool dummy_is_zip = false;
      const int err_undo_unzip =
          log_rv_get_unzip_log_data (thread_p, record_info.m_logrec.ulength, redo_context.m_reader,
                     &redo_context.m_undo_zip, dummy_is_zip);
      if (err_undo_unzip != NO_ERROR)
    {
      log_Gl.unique_stats_table.curr_rcv_rec_lsa.set_null ();
      logpb_fatal_error (thread_p, true, ARG_FILE_LINE, "log_rv_get_log_rec_redo_data");
      return err_undo_unzip;
    }
      redo_context.m_reader.align ();
      return log_rv_get_unzip_and_diff_redo_log_data (thread_p, redo_context.m_reader, &rcv,
         redo_context.m_undo_zip.data_length, redo_context.m_undo_zip.log_data,
         redo_context.m_redo_zip);
    }
  else
    {
      /* for the non-diff log records, it is enough to skip undo data and read the redo data
       */
      const int temp_length = GET_ZIP_LEN (record_info.m_logrec.ulength);
      const int err_skip = redo_context.m_reader.skip (temp_length);
      if (err_skip != NO_ERROR)
    {
      log_Gl.unique_stats_table.curr_rcv_rec_lsa.set_null ();
      logpb_fatal_error (thread_p, true, ARG_FILE_LINE, "log_rv_get_log_rec_redo_data");
      return ER_FAILED;
    }
      redo_context.m_reader.align ();
      return log_rv_get_unzip_and_diff_redo_log_data (thread_p, redo_context.m_reader, &rcv, 0, nullptr,
         redo_context.m_redo_zip);
    }
}

template <>
inline int
log_rv_get_log_rec_redo_data<LOG_REC_MVCC_UNDOREDO> (THREAD_ENTRY *thread_p, log_rv_redo_context &redo_context,
    const log_rv_redo_rec_info<LOG_REC_MVCC_UNDOREDO> &record_info, log_rcv &rcv)
{
  log_rv_redo_rec_info<LOG_REC_UNDOREDO> convert_record_info (record_info.m_start_lsa, record_info.m_type,
      record_info.m_logrec.undoredo);
  return log_rv_get_log_rec_redo_data<LOG_REC_UNDOREDO> (thread_p,  redo_context, convert_record_info, rcv);
}

template <>
inline int
log_rv_get_log_rec_redo_data<LOG_REC_MVCC_REDO> (THREAD_ENTRY *thread_p, log_rv_redo_context &redo_context,
    const log_rv_redo_rec_info<LOG_REC_MVCC_REDO> &log_rec, log_rcv &rcv)
{
  return log_rv_get_unzip_and_diff_redo_log_data (thread_p, redo_context.m_reader, &rcv, 0, nullptr,
     redo_context.m_redo_zip);
}

template <>
inline int
log_rv_get_log_rec_redo_data<LOG_REC_REDO> (THREAD_ENTRY *thread_p, log_rv_redo_context &redo_context,
    const log_rv_redo_rec_info<LOG_REC_REDO> &log_rec, log_rcv &rcv)
{
  return log_rv_get_unzip_and_diff_redo_log_data (thread_p, redo_context.m_reader, &rcv, 0, nullptr,
     redo_context.m_redo_zip);
}

template <>
inline int
log_rv_get_log_rec_redo_data<LOG_REC_RUN_POSTPONE> (THREAD_ENTRY *thread_p, log_rv_redo_context &redo_context,
    const log_rv_redo_rec_info<LOG_REC_RUN_POSTPONE> &log_rec, log_rcv &rcv)
{
  return log_rv_get_unzip_and_diff_redo_log_data (thread_p, redo_context.m_reader, &rcv, 0, nullptr,
     redo_context.m_redo_zip);
}

template <>
inline int
log_rv_get_log_rec_redo_data<LOG_REC_COMPENSATE> (THREAD_ENTRY *thread_p, log_rv_redo_context &redo_context,
    const log_rv_redo_rec_info<LOG_REC_COMPENSATE> &log_rec, log_rcv &rcv)
{
  return log_rv_get_unzip_and_diff_redo_log_data (thread_p, redo_context.m_reader, &rcv, 0, nullptr,
     redo_context.m_redo_zip);
}

#if !defined(NDEBUG)
class vpid_lsa_consistency_check
{
  public:
    vpid_lsa_consistency_check () = default;
    ~vpid_lsa_consistency_check () = default;

    vpid_lsa_consistency_check (const vpid_lsa_consistency_check &) = delete;
    vpid_lsa_consistency_check (vpid_lsa_consistency_check &&) = delete;

    vpid_lsa_consistency_check &operator= (const vpid_lsa_consistency_check &) = delete;
    vpid_lsa_consistency_check &operator= (vpid_lsa_consistency_check &&) = delete;

    void check (const struct vpid &a_vpid, const struct log_lsa &a_log_lsa);
    void cleanup ();

  private:
    using vpid_key_t = std::pair<short, int32_t>;
    using vpid_log_lsa_map_t = std::map<vpid_key_t, struct log_lsa>;

    std::mutex mtx;
    vpid_log_lsa_map_t consistency_check_map;
};
#endif

#if !defined(NDEBUG)
extern vpid_lsa_consistency_check log_Gl_recovery_redo_consistency_check;
#endif

template <typename T>
void log_rv_redo_record_sync (THREAD_ENTRY *thread_p, log_rv_redo_context &redo_context,
                  const log_rv_redo_rec_info<T> &record_info, const VPID &rcv_vpid)
{
#if !defined(NDEBUG)
  if (log_Gl.rcv_phase != LOG_RESTARTED)
    {
      // bit of debug code to ensure that, should this code be executed asynchronously, within the same page,
      // the lsa is ever-increasing, thus, not altering the order in which it has been added to the log in the first place
      log_Gl_recovery_redo_consistency_check.check (rcv_vpid, record_info.m_start_lsa);
    }
#endif

  const LOG_DATA &log_data = log_rv_get_log_rec_data<T> (record_info.m_logrec);

  LOG_RCV rcv;
  if (!log_rv_fix_page_and_check_redo_is_needed (thread_p, rcv_vpid, rcv, log_data.rcvindex, record_info.m_start_lsa,
      redo_context.m_end_redo_lsa))
    {
      /* nothing else needs to be done, see explanation in function */
      assert (rcv.pgptr == nullptr);
      return;
    }
  // at this point, pgptr can be null or not

  /* will take care of unfixing the page, will be correctly de-allocated as it is the same
   * storage class as 'rcv' and allocated on the stack after 'rcv' */
  scope_exit unfix_rcv_pgptr
  {
    [&thread_p, &rcv] ()
    {
      pgbuf_unfix_and_init_after_check (thread_p, rcv.pgptr);
    }
  };

  rcv.length = log_rv_get_log_rec_redo_length<T> (record_info.m_logrec);
  rcv.mvcc_id = log_rv_get_log_rec_mvccid<T> (record_info.m_logrec);
  rcv.offset = log_rv_get_log_rec_offset<T> (record_info.m_logrec);

  log_rv_redo_record_debug_logging<T> (record_info.m_start_lsa, log_data.rcvindex, rcv_vpid, rcv);

  const auto err_redo_data = log_rv_get_log_rec_redo_data<T> (thread_p, redo_context, record_info, rcv);
  if (err_redo_data != NO_ERROR)
    {
      logpb_fatal_error (thread_p, true, ARG_FILE_LINE, "log_rv_get_log_rec_redo_data");
      // rcv pgptr will be automatically unfixed
      return;
    }

  rvfun::fun_t redofunc = log_rv_get_fun<T> (record_info.m_logrec, log_data.rcvindex);
  if (redofunc != nullptr)
    {
      /* perf data for actually calling the log redo function; it is relevant in two contexts:
       *  - log recovery redo after a crash (either synchronously or using the parallel
       *    infrastructure)
       *  - log replication on the page server; both when applying the replication redo synchronously
       *    or in parallel will log to this entry
       */
      perfmon_counter_timer_raii_tracker perfmon { PSTAT_LOG_REDO_FUNC_EXEC };

      const int err_func = redofunc (thread_p, &rcv);
      if (err_func != NO_ERROR)
    {
      logpb_fatal_error (thread_p, true, ARG_FILE_LINE,
                 "log_rv_redo_record_sync: Error applying redo record at log_lsa=(%lld, %d), "
                 "rcv = {mvccid=%llu, vpid=(%d, %d), offset = %d, data_length = %d}",
                 LSA_AS_ARGS (&record_info.m_start_lsa), (long long int) rcv.mvcc_id,
                 VPID_AS_ARGS (&rcv_vpid), (int) rcv.offset, (int) rcv.length);
    }
    }
  else
    {
      er_log_debug (ARG_FILE_LINE,
            "log_rv_redo_record_sync: WARNING.. There is not a"
            " REDO (or, possibly, UNDO) function to execute. May produce recovery problems.");
    }

  if (rcv.pgptr != nullptr)
    {
      pgbuf_set_lsa (thread_p, rcv.pgptr, &record_info.m_start_lsa);
      // rcv pgptr will be automatically unfixed at the end of the parent scope
    }
}

template <typename T>
log_rv_redo_rec_info<T>::log_rv_redo_rec_info (const log_lsa lsa, LOG_RECTYPE type, const T &t)
  : m_start_lsa { lsa }
  , m_type { type }
  , m_logrec { t }
{

}

#endif // _LOC_RECOVERY_REDO_HPP_