Skip to content

File monitor_vacuum_ovfp_threshold.cpp

File List > cubrid > src > monitor > monitor_vacuum_ovfp_threshold.cpp

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.
 *
 */

/*
 * monitor_vacuum_ovfp_threshold.cpp - (at Server).
 *
 */

#if !defined (SERVER_MODE)
#error Belongs to server module
#endif

#if defined (SERVER_MODE)

#include <assert.h>
#include "heap_file.h"
#include "monitor_vacuum_ovfp_threshold.hpp"
// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"

/* *INDENT-OFF* */
#define OID_COMPARE(a, b)  (((a)->volid != (b)->volid) ? ((a)->volid - (b)->volid) : \
                              (((a)->pageid != (b)->pageid) ? ((a)->pageid - (b)->pageid) : ((a)->slotid - (b)->slotid)))
#define BTID_COMPARE(a, b) (((a)->vfid.volid != (b)->vfid.volid) ? ((a)->vfid.volid - (b)->vfid.volid) : ((a)->vfid.fileid - (b)->vfid.fileid))
/* *INDENT-ON* */

// ============================================================================
// class ovfp_monitor_lock
// public
ovfp_monitor_lock::ovfp_monitor_lock()
{
  memset (m_lock_arr, 0xFF, sizeof (m_lock_arr));
}

void
ovfp_monitor_lock::lock (int lock_index, int owner_id)
{
  assert (lock_index >= 0 && lock_index < LOCK_ITEMS_SIZE);
  m_ovfp_monitor_mutex.lock();
  while (m_lock_arr[lock_index] != LOCK_FREE_OWNER_ID)
    {
      assert ((m_lock_arr[lock_index] != owner_id) || (LOCK_ALL_OWNER_ID == owner_id));
      m_ovfp_monitor_mutex.unlock();
      usleep (1);
      m_ovfp_monitor_mutex.lock();
    }
  m_lock_arr[lock_index] = owner_id;
  m_ovfp_monitor_mutex.unlock();
}

void
ovfp_monitor_lock::unlock (int lock_index, int owner_id)
{
  assert (lock_index >= 0 && lock_index < LOCK_ITEMS_SIZE);
  m_ovfp_monitor_mutex.lock();
  assert (m_lock_arr[lock_index] == owner_id);
  m_lock_arr[lock_index] = LOCK_FREE_OWNER_ID;
  m_ovfp_monitor_mutex.unlock();
}

// ============================================================================
// class ovfp_threshold
// private
void
ovfp_threshold::clear (INDEX_OVFP_INFO *root)
{
  INDEX_OVFP_INFO *pt;
  while (root)
    {
      pt = root;
      root = pt->next;
      free (pt);
    }
}

void
ovfp_threshold::free_info_mem (INDEX_OVFP_INFO *pt)
{
  assert (pt != NULL);
  pt->next = m_free_head ? m_free_head : NULL;
  m_free_head = pt;
}

INDEX_OVFP_INFO *
ovfp_threshold::add (BTID *btid, OID *class_oid, time_t time_now, int npages)
{
  INDEX_OVFP_INFO  *pt;

  pt = find (btid, class_oid);
  if (pt)
    {
      pt->event_time[RECENT_POS] = time_now;
      pt->read_pages[RECENT_POS] = npages;

      if (pt->read_pages[MAX_POS] <= npages)
    {
      pt->event_time[MAX_POS] = time_now;
      pt->read_pages[MAX_POS] = npages;
    }
      pt->hit_cnt++;

      return pt;
    }


  pt = alloc_info_mem();
  assert (pt != NULL);
  if (pt)
    {
      // new item
      BTID_COPY ( & (pt->btid), btid);
      COPY_OID (& (pt->class_oid), class_oid);

      pt->event_time[RECENT_POS] = time_now;
      pt->read_pages[RECENT_POS] = npages;
      pt->event_time[MAX_POS] = time_now;
      pt->read_pages[MAX_POS] = npages;
      pt->hit_cnt = 1;

      if (m_head == NULL || m_prev == NULL)
    {
      pt->next = m_head;
      m_head = pt;
    }
      else
    {
      pt->next = m_prev->next;
      m_prev->next = pt;
    }
    }

  return pt;
}

//protected
INDEX_OVFP_INFO  *
ovfp_threshold::alloc_info_mem()
{
  INDEX_OVFP_INFO *pt;

  if (m_free_head)
    {
      pt = m_free_head;
      m_free_head = m_free_head->next;
    }
  else
    {
      pt = (INDEX_OVFP_INFO *) malloc (sizeof (INDEX_OVFP_INFO));
      assert (pt != NULL);
    }
  return pt;
}

INDEX_OVFP_INFO *
ovfp_threshold::find (BTID *btid, OID *class_oid)
{
  int cmp;

  if (m_head == NULL)
    {
      m_prev = NULL;
      return NULL;
    }

  for (INDEX_OVFP_INFO *cur = m_head; cur; cur = cur->next)
    {
      cmp = OID_COMPARE (& (cur->class_oid), class_oid);
      if ( cmp == 0 )
    {
      cmp = BTID_COMPARE (& (cur->btid), btid);
    }

      if ( cmp > 0 )
    {
      return NULL;
    }
      else if ( cmp == 0 )
    {
      return cur;
    }

      m_prev = cur;
    }

  return NULL;
}

//public
ovfp_threshold::ovfp_threshold()
{
  m_free_head = m_head = m_prev = NULL;

  m_lock_idx = -1;
  m_lock_ptr = NULL;
}
ovfp_threshold::~ovfp_threshold()
{
  if (m_lock_ptr)
    {
      m_lock_ptr->lock (m_lock_idx, m_lock_idx);
      clear (m_head);
      clear (m_free_head);
      m_lock_ptr->unlock (m_lock_idx, m_lock_idx);
    }
  else
    {
      clear (m_head);
      clear (m_free_head);
    }
  m_head = m_free_head = NULL;
}

void
ovfp_threshold::set_worker_idx (int idx, ovfp_monitor_lock *lock_mgr_p)
{
  assert (idx >= 0 && idx <= VACUUM_MAX_WORKER_COUNT);
  m_lock_idx = idx;
  if (m_lock_idx >= 0 && m_lock_idx < VACUUM_MAX_WORKER_COUNT)
    {
      m_lock_ptr = lock_mgr_p;
    }
}

INDEX_OVFP_INFO *
ovfp_threshold::get_head ()
{
  return m_head;
}

void
ovfp_threshold::add_info (BTID *btid, OID *class_oid, int npages)
{
  time_t     time_now;

  time_now = time (NULL);
  if (m_lock_ptr)
    {
      m_lock_ptr->lock (m_lock_idx, m_lock_idx);
      add (btid, class_oid, time_now, npages);
      m_lock_ptr->unlock (m_lock_idx, m_lock_idx);
    }
  else
    {
      add (btid, class_oid, time_now, npages);
    }
}

void
ovfp_threshold::check_over_duration_times (time_t *over_tm)
{
  // Delete information that has not been updated for a long time.
  INDEX_OVFP_INFO    *cur, *new_head, *tail;

  new_head = tail = NULL;
  while (m_head)
    {
      cur = m_head;
      m_head = m_head->next;
      if (*over_tm - cur->event_time[RECENT_POS] > 0)
    {
      free_info_mem (cur);
    }
      else
    {
      if (tail)
        {
          tail->next = cur;
        }
      else
        {
          new_head = cur;
        }
      tail = cur;
    }
    }

  if (tail)
    {
      tail->next = NULL;
    }

  m_head = new_head;
}

// ============================================================================
// class ovfp_printer: public ovfp_threshold
// public
void
ovfp_printer::sort()
{
  INDEX_OVFP_INFO  *tmp, *prev, *cur, *new_head;

  assert (m_lock_idx == LOCK_ALL_OWNER_ID);
  new_head = tmp = NULL;
  while (m_head)
    {
      tmp = m_head;
      m_head = m_head->next;

      if (new_head == NULL)
    {
      new_head = tmp;
      tmp->next = NULL;
    }
      else
    {
      prev = NULL;
      cur = new_head;
      while (cur)
        {
          if (cur->event_time[RECENT_POS] <= tmp->event_time[RECENT_POS])
        {
          break;
        }

          prev = cur;
          cur = cur->next;
        }

      if (prev == NULL)
        {
          tmp->next = new_head;
          new_head = tmp;
        }
      else
        {
          tmp->next = prev->next;
          prev->next = tmp;
        }
    }
    }

  m_head = new_head;
}

void
ovfp_printer::add_info (INDEX_OVFP_INFO *new_info)
{
  INDEX_OVFP_INFO *pt;
  // This function must obtain a lock and be guaranteed to be called.

  pt = find (&new_info->btid,&new_info->class_oid);
  if (pt == NULL)
    {
      pt = alloc_info_mem();
      assert (pt != NULL);
      if (pt)
    {
      // new item
      memcpy (pt, new_info, sizeof (INDEX_OVFP_INFO));
      if (m_head == NULL)
        {
          pt->next = NULL;
          m_head = pt;
        }
      else if (m_prev == NULL)
        {
          pt->next = m_head;
          m_head = pt;
        }
      else
        {
          pt->next = m_prev->next;
          m_prev->next = pt;
        }
    }
    }
  else
    {
      pt->hit_cnt += new_info->hit_cnt;
      if (pt->read_pages[MAX_POS] <=  new_info->read_pages[MAX_POS])
    {
      if (pt->read_pages[MAX_POS] < new_info->read_pages[MAX_POS])
        {
          pt->read_pages[MAX_POS] = new_info->read_pages[MAX_POS];
          pt->event_time[MAX_POS] = new_info->event_time[MAX_POS];
        }
      else if (pt->event_time[MAX_POS] < new_info->event_time[MAX_POS])
        {
          pt->event_time[MAX_POS] = new_info->event_time[MAX_POS];
        }
    }

      if (pt->event_time[RECENT_POS] < new_info->event_time[RECENT_POS])
    {
      pt->read_pages[RECENT_POS] = new_info->read_pages[RECENT_POS];
      pt->event_time[RECENT_POS] = new_info->event_time[RECENT_POS];
    }
      else if (pt->event_time[RECENT_POS] == new_info->event_time[RECENT_POS])
    {
      if (pt->read_pages[RECENT_POS] < new_info->read_pages[RECENT_POS])
        {
          pt->read_pages[RECENT_POS] = new_info->read_pages[RECENT_POS];
        }
    }
    }
}


// ============================================================================
// class ovfp_threshold_mgr
// private function
bool
ovfp_threshold_mgr::get_classoid (THREAD_ENTRY *thread_p, BTID *btid, OID *class_oid)
{
  FILE_DESCRIPTORS fdes;
  assert (btid != NULL);

  if (file_descriptor_get (thread_p, &btid->vfid, &fdes) != NO_ERROR)
    {
      //OID_SET_NULL(&class_oid);
      return false;
    }

  COPY_OID (class_oid, & (fdes.btree.class_oid));
  return true;
}

void
ovfp_threshold_mgr::get_class_name_index_name (THREAD_ENTRY *thread_p, BTID *btid, OID *class_oid, char **class_name,
    char **index_name)
{
  int ret = NO_ERROR;
  char tmp[128];

  assert (btid != NULL);
  assert (class_name != NULL && index_name != NULL);

  if (*class_name != NULL)
    {
      free_and_init (*class_name);
    }
  if (*index_name != NULL)
    {
      free_and_init (*index_name);
    }

  if (heap_get_class_name (thread_p, class_oid, class_name) == NO_ERROR)
    {
      /* get index name */
      if (heap_get_indexinfo_of_btid (thread_p, class_oid, btid, NULL, NULL, NULL, NULL, index_name, NULL) != NO_ERROR)
    {
      sprintf (tmp, "(%d, %d|%d)", btid->root_pageid, btid->vfid.volid, btid->vfid.fileid /* BTID_AS_ARGS(btid) */);
      *index_name = strdup (tmp);
    }
    }
  else
    {
      sprintf (tmp, "(%d|%d|%d)", class_oid->volid, class_oid->pageid, class_oid->slotid/* OID_AS_ARGS(class_oid) */);
      *class_name = strdup (tmp);
      sprintf (tmp, "(%d, %d|%d)", btid->root_pageid,  btid->vfid.volid, btid->vfid.fileid/* BTID_AS_ARGS(btid) */);
      *index_name = strdup (tmp);
    }
}

char *
ovfp_threshold_mgr::time_to_string (time_t er_time, char *buf, int size)
{
  struct tm er_tm;
  struct tm *er_tm_p = NULL;

  if (er_time != 0)
    {
      er_tm_p = localtime_r (&er_time, &er_tm);
    }

  if (er_tm_p == NULL)
    {
      strcpy (buf, "00/00/00 00:00:00");
    }
  else
    {
      strftime (buf, size, "%m/%d/%y %H:%M:%S", er_tm_p);
    }
  return buf;
}

void
ovfp_threshold_mgr::print (THREAD_ENTRY *thread_p, FILE *outfp, const INDEX_OVFP_INFO *head_ptr)
{
  INDEX_OVFP_INFO *pt;
  char *class_name, *index_name;
  char time_buf[32], line_buf[256];
#define NUM_ATTRS_CNT (5)
  const char *attr_names[NUM_ATTRS_CNT] = {"Class name", "Index name", "Count", "Num of OVFP recent read", "Max num of OVFP read"};
  int attr_lengths[NUM_ATTRS_CNT] = { 20, 20, 12, 25, 25 };
  int len;

  fprintf (outfp, "\n*** Exceeding read threshold (%d pages) for OID overflow pages (OVFP), Since %s ***\n",
       m_threshold_pages, m_since_time);
  len = 0;
  for (int i = 0; i < NUM_ATTRS_CNT; i++)
    {
      len += fprintf (outfp, "  %-*s", attr_lengths[i], attr_names[i]);
    }

  memset (line_buf, '=', len);
  line_buf[len] = '\0';
  fprintf (outfp, "\n%s\n", line_buf);

  class_name = index_name = NULL;
  pt = (INDEX_OVFP_INFO *) head_ptr;
  while (pt)
    {
      get_class_name_index_name (thread_p, & (pt->btid), & (pt->class_oid), &class_name, &index_name);
      assert (class_name != NULL && index_name != NULL);
      if (class_name)
    {
      fprintf (outfp, "  %-*s", MAX (attr_lengths[0], (int)strlen (class_name)), class_name);
      free_and_init (class_name);
    }
      if (index_name)
    {
      fprintf (outfp, "  %-*s", MAX (attr_lengths[1], (int)strlen (index_name)), index_name);
      free_and_init (index_name);
    }

      fprintf (outfp, "  %*lld", attr_lengths[2], (long long) pt->hit_cnt);

      sprintf (line_buf, "  %d (%s)", pt->read_pages[RECENT_POS], time_to_string (pt->event_time[RECENT_POS], time_buf,
           sizeof (time_buf)));
      fprintf (outfp, "  %-*s", MAX (attr_lengths[3], (int)strlen (line_buf)), line_buf);

      sprintf (line_buf, "  %d (%s)", pt->read_pages[MAX_POS], time_to_string (pt->event_time[MAX_POS], time_buf,
           sizeof (time_buf)));
      fprintf (outfp, "  %-*s\n", MAX (attr_lengths[4], (int)strlen (line_buf)), line_buf);

      pt = pt->next;
    }
  fprintf (outfp, "\n");

  if (class_name)
    {
      free_and_init (class_name);
    }
  if (index_name)
    {
      free_and_init (index_name);
    }
}

// public function
ovfp_threshold_mgr::ovfp_threshold_mgr()
{
  for ( int worker_idx = 0; worker_idx < VACUUM_MAX_WORKER_COUNT; worker_idx++)
    {
      m_ovfp_threshold[worker_idx].set_worker_idx (worker_idx, &m_ovfp_lock);
    }

  m_over_secs = 45000 /* min */ * 60; // sec
  m_threshold_pages = 1000;
  m_since_time[0] = '\0';
}

void
ovfp_threshold_mgr::init()
{
  time_to_string (time (NULL), m_since_time, sizeof (m_since_time));

  m_over_secs = prm_get_integer_value (PRM_ID_VACUUM_OVFP_CHECK_DURATION);
  m_over_secs *= 60; // to second
  m_threshold_pages = prm_get_integer_value (PRM_ID_VACUUM_OVFP_CHECK_THRESHOLD);
}

void
ovfp_threshold_mgr::add_read_pages_count (THREAD_ENTRY *thread_p, int worker_idx, BTID *btid, int npages)
{
  OID class_oid;

  assert (worker_idx >= 0 && worker_idx < VACUUM_MAX_WORKER_COUNT);
  assert (npages >= m_threshold_pages);

  // BTIDs can be reused. However, CLASS_OID is not reused.
  // That's why we get the class oid immediately here.
  if (get_classoid (thread_p, btid, &class_oid))
    {
      m_ovfp_threshold[worker_idx].add_info (btid, &class_oid, npages);
    }
}

void
ovfp_threshold_mgr::dump (THREAD_ENTRY *thread_p, FILE *outfp)
{
  INDEX_OVFP_INFO *pt;
  ovfp_printer  printer;
  time_t over_tm = time (NULL) - m_over_secs;

  assert (outfp != NULL);

  printer.set_worker_idx (LOCK_ALL_OWNER_ID, NULL);

  for (int i = 0; i < VACUUM_MAX_WORKER_COUNT; i++)
    {
      m_ovfp_lock.lock (i, LOCK_ALL_OWNER_ID);

      m_ovfp_threshold[i].check_over_duration_times (&over_tm);

      pt = m_ovfp_threshold[i].get_head();
      while (pt)
    {
      printer.add_info (pt);
      pt = pt->next;
    }

      m_ovfp_lock.unlock (i, LOCK_ALL_OWNER_ID);
    }

  // sort by recent time
  printer.sort();
  print (thread_p, outfp, printer.get_head());
}

#endif // #if defined (SERVER_MODE)