VersionX переполнил базу данных

Друзья привет.
столкнулся с болью обнаружил что на обном из сайтов БД выросла но неимоверных размеров!
После недолгого копания выяснилось что VersionX хранит в БД все что произошло за год в размере 3,4 гигов.



И это печаль.

Итак вопрос как говорится к старшим товарищам что с этим можно безопасно сделать?
Мои варианты:
  1. Очистить эту таблицу в БД — тут я не великий специалист боюсь угробить сайт...
  2. Понять как работает дополнение и встроить настройку чтоб сохранялось только 3 последних версий — я тут совсем не специалист.
  3. Написать автору дополнения и просить помощи
Приоритетно конечно попытаться решить какой то малой кровью если кто подскажет, поможет буду благодарен.
Владимир Ульяновский
12 сентября 2017, 15:02
modx.pro
6
4 879
+1

Комментарии: 8

Роман Ильин
12 сентября 2017, 23:09
13
+2
Сниппет для удаления старых версий:

<?php
/**
 * versionCleanX
 *
 * @author Scott Pronych, September 27, 2013
 *
 * DESCRIPTION
 *
 * For use with VersionX to delete old content
 *
 * PROPERTIES:
 *
 * &contentType - can be resource, chunk, plugin, snippet, template, or templatevar
 * &maxVersions - integer value for max number of versions you would like to keep
 *
 * USAGE:
 *
 * [[!versionCleanX? &contentType=`resource` &maxVersions=`10`]]
 *
 */
 
$cx_type = $modx->getOption('contentType', $scriptProperties, 'resource');
$cx_max = (int) $modx->getOption('maxVersions', $scriptProperties, 5);
 
$GLOBALS['count'] = 1;
$GLOBALS['total'] = 0;
$GLOBALS['deleted'] = 0;
$GLOBALS['page_total'] = 0;
$GLOBALS['page_deleted'] = 0;
$GLOBALS['page_name'] = '';
$GLOBALS['prev_content_id'] = 0;
$GLOBALS['prev_version_id'] = 0;
 
switch ($cx_type) {
    case 'chunk':
        $name = 'name';
        break;
    case 'plugin':
        $name = 'name';
        break;
    case 'snippet':
        $name = 'name';
        break;
    case 'template':
        $name = 'templatename';
        break;
    case 'templatevar':
        $name = 'name';
        break;
    default:
        $name = 'title';
        $cx_type = 'resource';
}
 
$GLOBALS['db_name'] = 'modx_versionx_' . $cx_type;
 
function delete_row ($id) {
    global $modx;
    $query = "DELETE FROM `" . $GLOBALS['db_name'] . "` WHERE version_id = '" . $id . "'";
    $result = $modx->query($query);
    if (!is_object($result)) return false;
    else {
        $GLOBALS['deleted']++;
        $GLOBALS['page_deleted']++;
        $GLOBALS['page_total']++;
        $GLOBALS['total']++;
        $GLOBALS['count']++;
        return true;
    }
}
 
function log_row () {
    $GLOBALS['data'] .= '<tr><td>' . $GLOBALS['page_name'] . '</td><td>' . $GLOBALS['page_total'] . '</td><td>' . $GLOBALS['page_deleted'] . "</td></tr>\n";
    $GLOBALS['page_deleted'] = 0;
    $GLOBALS['page_total'] = 1;
    $GLOBALS['count'] = 1;
}
 
$query = "SELECT version_id, content_id, " . $name . " AS page_title FROM `" . $GLOBALS['db_name'] . "` ORDER BY content_id ASC, version_id DESC";
 
$GLOBALS['data'] = '';
$output = 'An error occurred: ';
$versionx = $modx->query($query);
if (!is_object($versionx)) {
   return $output . 'query error ' . print_r($modx->errorInfo(), true);
}
else {
 
while($row = $versionx->fetch(PDO::FETCH_ASSOC)) {
    // New content_id so reset
    if ($prev_content_id == 0) {
        $prev_content_id = $row['content_id'];
        $prev_version_id = $row['version_id'];
        $GLOBALS['total']++;
        $GLOBALS['page_total']++;
        $GLOBALS['count']++;
        $GLOBALS['page_name'] = $row['page_title'];
    }
    elseif ($prev_content_id != $row['content_id']) {
        if ($GLOBALS['count'] > $cx_max) {
            if (!delete_row($prev_version_id)) return $output .  'deleting row for ' . $GLOBALS['page_name'] . ' Row: ' . $prev_content_id . ' ' . print_r($modx->errorInfo(), true);
            $GLOBALS['page_total']--;
 
        }
        else {
            $GLOBALS['total']++;
            $GLOBALS['count']++;
        }
        log_row();
        $prev_content_id = $row['content_id'];
        $prev_version_id = $row['version_id'];
        $GLOBALS['page_name'] = $row['page_title'];
    }
    // Content count is over the max so delete previous row
    elseif ($GLOBALS['count'] > $cx_max) {
            delete_row($prev_version_id);
            $prev_content_id = $row['content_id'];
            $prev_version_id = $row['version_id'];
    }
    else {
        $GLOBALS['count']++;
        $GLOBALS['page_total']++;
        $GLOBALS['total']++;
        $prev_content_id = $row['content_id'];
        $prev_version_id = $row['version_id'];
    }
}
log_row();
 
if ($GLOBALS['data'] != '') {
$output = '<h3>VersionX Cleanup for ' . $GLOBALS['db_name'] . '</h3>
<p>Total records: <strong>' . $GLOBALS['total'] . '</strong>
Total deleted: <strong>' . $GLOBALS['deleted'] . '</strong></p>
<table class="table table-striped">
<thead>
<tr>
<th>Page name</th>
<th>Total found</th>
<th>Deleted</th>
</tr>
</thead>
<tbody>
' . $GLOBALS['data'] .  '</tbody></table>
';
}
else $output = 'Error: no data found.';
}
 
$query = "OPTIMIZE TABLE `" . $GLOBALS['db_name'] . "`";
$versionx = $modx->query($query);
if (!is_object($versionx)) {
   $output = 'Optimize error ' . print_r($modx->errorInfo(), true) . $output;
}
 
return $output;


Вызывать так:
[[!versionCleanX? &contentType=`resource` &maxVersions=`10`]]
Владимир Ульяновский
13 сентября 2017, 15:28
0
An error occurred: query error Array ( [0] => 42S02 [1] => 1146 [2] => Table 's86.modx_versionx_resource' doesn't exist )
Сие нам говорит что таблицы нет, но она есть! Так как префикс modx_ у БД на модхосте меняется, то надо либо его заменить ручками, либо прописать динамическое определение префикса.

Если кто подскажет как сделать динамику будет огонь!

Я нашел на 55 строке
$GLOBALS['db_name'] = 'сюда дописал префикс_versionx_' . $cx_type;
    Илья Уткин
    14 сентября 2017, 11:14
    +2
    $GLOBALS['db_name'] = $modx->getOption('table_prefix') . 'versionx_' . $cx_type;
    Сергей
    04 августа 2022, 12:19
    0
    Если записей слишком много и сервер не может обработать запрос то добавьте это. У меня уже под 200к версий и выпадало в ошибку

    <?php ini_set('memory_limit', '256M');
      Виктор
      04 августа 2022, 13:20
      -1
      Можно оптимизировать БД + почистить логи

      XMLSEO
        TheRealMaN
        23 февраля 2025, 19:42
        0
        А все-таки можно просто очистить таблицу)?

        Если нет, то скажите, пожалуйста, куда добавлять?
        [[!versionCleanX? &contentType=`resource` &maxVersions=`10`]]
          Олег Захаров
          19 мая 2026, 04:04
          1
          0
          Сделал новую версию с табами и возможностью запуска сразу для всех вариантов.
          Сначала содержимое для технического ресурса откуда будет запускаться выполнение сниппета.
          [[!versionCleanXTabs? &maxVersions=`2` &types=`chunk,resource,template,snippet,plugin,templatevar` &dryRun=`0` &optimize=`1`]]
          Далее содержимое versionCleanXTabs:
          — в описании Tabbed wrapper for versionCleanX cleanup results.
          — в код:
          <?php
          /**
           * versionCleanXTabs
           *
           * Runs versionCleanX for several VersionX entity tables and shows results in tabs.
           *
           * PROPERTIES:
           * &types - comma-separated list: chunk,resource,template,snippet,plugin,templatevar
           * &maxVersions - integer max versions to keep per element
           * &dryRun - 1 to show what would be deleted without deleting rows
           * &optimize - 1 to run OPTIMIZE TABLE in versionCleanX
           */
          
          $escape = function ($value) {
              return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
          };
          
          $toBool = function ($value) {
              return filter_var($value, FILTER_VALIDATE_BOOLEAN);
          };
          
          $typesRaw = (string)$modx->getOption('types', $scriptProperties, 'chunk,resource,template,snippet,plugin,templatevar');
          $maxVersions = (int)$modx->getOption('maxVersions', $scriptProperties, 2);
          $dryRun = $toBool($modx->getOption('dryRun', $scriptProperties, false));
          $optimize = $toBool($modx->getOption('optimize', $scriptProperties, true));
          
          $labels = [
              'chunk' => ['Чанки', 'versionx_chunk'],
              'resource' => ['Ресурсы', 'versionx_resource'],
              'template' => ['Шаблоны', 'versionx_template'],
              'snippet' => ['Сниппеты', 'versionx_snippet'],
              'plugin' => ['Плагины', 'versionx_plugin'],
              'templatevar' => ['TV-поля', 'versionx_templatevar'],
          ];
          
          $types = [];
          foreach (preg_split('/\s*,\s*/', $typesRaw, -1, PREG_SPLIT_NO_EMPTY) as $type) {
              $type = strtolower(trim($type));
              if (isset($labels[$type]) && !in_array($type, $types, true)) {
                  $types[] = $type;
              }
          }
          
          if ($maxVersions < 1) {
              $maxVersions = 2;
          }
          
          if (empty($types)) {
              return '<p>versionCleanXTabs: не передан ни один поддерживаемый тип VersionX.</p>';
          }
          
          $uid = 'vcx-tabs-' . substr(md5(uniqid('', true)), 0, 10);
          
          $css = <<<'VCX_STYLE'
          <style>
          .vcx-tool {
              max-width: 1180px;
              margin: 32px auto;
              padding: 0 18px 40px;
              color: #172033;
              font-family: Arial, sans-serif;
          }
          .vcx-tool h1 {
              margin: 0 0 12px;
              font-size: 34px;
              line-height: 1.15;
              font-weight: 600;
          }
          .vcx-tool__lead {
              max-width: 860px;
              margin: 0 0 22px;
              color: #536071;
              font-size: 16px;
              line-height: 1.55;
          }
          .vcx-tabs__nav {
              display: flex;
              flex-wrap: wrap;
              gap: 8px;
              margin: 24px 0 0;
              border-bottom: 1px solid #d8dee8;
          }
          .vcx-tabs__button {
              appearance: none;
              border: 1px solid #d8dee8;
              border-bottom: 0;
              border-radius: 8px 8px 0 0;
              background: #f4f7fb;
              color: #1b2a41;
              padding: 11px 15px 10px;
              min-width: 132px;
              text-align: left;
              cursor: pointer;
          }
          .vcx-tabs__button small {
              display: block;
              margin-top: 2px;
              color: #6b7585;
              font-size: 11px;
          }
          .vcx-tabs__button.is-active {
              background: #fff;
              color: #00458f;
              border-color: #c5d1df;
          }
          .vcx-tabs__panel {
              display: none;
              padding: 24px 0 0;
          }
          .vcx-tabs__panel.is-active {
              display: block;
          }
          .vcx-result {
              overflow-x: auto;
          }
          .vcx-result h3 {
              margin-top: 0;
          }
          .vcx-result table {
              width: 100%;
              border-collapse: collapse;
              margin-top: 14px;
          }
          .vcx-result th,
          .vcx-result td {
              padding: 9px 10px;
              border-bottom: 1px solid #e5e9f0;
              text-align: left;
              vertical-align: top;
          }
          .vcx-result th {
              background: #f5f7fa;
              font-weight: 600;
          }
          .vcx-tool__note {
              margin-top: 18px;
              padding: 12px 14px;
              border-left: 4px solid #00458f;
              background: #f3f7fc;
              color: #3d4a5c;
          }
          @media (max-width: 680px) {
              .vcx-tool h1 {
                  font-size: 27px;
              }
              .vcx-tabs__button {
                  flex: 1 1 100%;
              }
          }
          </style>
          VCX_STYLE;
          
          $out = $css;
          $out .= '<section class="vcx-tool" id="' . $escape($uid) . '">';
          $out .= '<h1>VersionX cleanup</h1>';
          $out .= '<p class="vcx-tool__lead">Служебная страница очистки промежуточных версий VersionX. При открытии страницы запускается очистка всех вкладок: остаются только ' . (int)$maxVersions . ' последние версии для выбранных сущностей.</p>';
          if ($dryRun) {
              $out .= '<p class="vcx-tool__note"><strong>Dry run:</strong> записи не удаляются, показан только расчет.</p>';
          }
          
          $out .= '<div class="vcx-tabs">';
          $out .= '<div class="vcx-tabs__nav" role="tablist" aria-label="VersionX cleanup tabs">';
          foreach ($types as $index => $type) {
              $panelId = $uid . '-' . $type;
              $active = $index === 0 ? ' is-active' : '';
              $out .= '<button class="vcx-tabs__button' . $active . '" type="button" role="tab" aria-selected="' . ($index === 0 ? 'true' : 'false') . '" data-vcx-tab="' . $escape($panelId) . '">';
              $out .= '<span>' . $escape($labels[$type][0]) . '</span><small>_' . $escape($labels[$type][1]) . '</small>';
              $out .= '</button>';
          }
          $out .= '</div>';
          
          foreach ($types as $index => $type) {
              $panelId = $uid . '-' . $type;
              $active = $index === 0 ? ' is-active' : '';
              $result = (string)$modx->runSnippet('versionCleanX', [
                  'contentType' => $type,
                  'maxVersions' => $maxVersions,
                  'dryRun' => $dryRun ? 1 : 0,
                  'optimize' => $optimize ? 1 : 0,
              ]);
          
              if ($result === '') {
                  $result = '<p>Сниппет versionCleanX не вернул результат для типа ' . $escape($type) . '.</p>';
              }
          
              $out .= '<div class="vcx-tabs__panel' . $active . '" id="' . $escape($panelId) . '" role="tabpanel">';
              $out .= '<div class="vcx-result">' . $result . '</div>';
              $out .= '</div>';
          }
          $out .= '</div>';
          
          $out .= <<<'VCX_SCRIPT'
          <script>
          (function () {
              var root = document.currentScript ? document.currentScript.closest('.vcx-tool') : null;
              if (!root) {
                  return;
              }
              var buttons = root.querySelectorAll('[data-vcx-tab]');
              var panels = root.querySelectorAll('.vcx-tabs__panel');
              buttons.forEach(function (button) {
                  button.addEventListener('click', function () {
                      var targetId = button.getAttribute('data-vcx-tab');
                      buttons.forEach(function (item) {
                          var active = item === button;
                          item.classList.toggle('is-active', active);
                          item.setAttribute('aria-selected', active ? 'true' : 'false');
                      });
                      panels.forEach(function (panel) {
                          panel.classList.toggle('is-active', panel.id === targetId);
                      });
                  });
              });
          })();
          </script>
          VCX_SCRIPT;
          
          $out .= '</section>';
          
          return $out;
          Далее содержимое сниппета versionCleanX (отличается от варианта выше):
          <?php
          ini_set('memory_limit', '256M');
          
          /**
           * versionCleanX
           *
           * Cleans old VersionX rows while keeping the newest N versions per element.
           *
           * PROPERTIES:
           * &contentType - resource, chunk, plugin, snippet, template, or templatevar
           * &maxVersions - integer max versions to keep per element
           * &dryRun - 1 to show what would be deleted without deleting rows
           * &optimize - 1 to run OPTIMIZE TABLE after cleanup
           *
           * USAGE:
           * [[!versionCleanX? &contentType=`resource` &maxVersions=`10`]]
           */
          
          $type = strtolower(trim((string)$modx->getOption('contentType', $scriptProperties, 'resource')));
          $maxVersions = (int)$modx->getOption('maxVersions', $scriptProperties, 5);
          $dryRun = (bool)$modx->getOption('dryRun', $scriptProperties, false);
          $optimize = (bool)$modx->getOption('optimize', $scriptProperties, true);
          
          $titleColumns = [
              'chunk' => 'name',
              'plugin' => 'name',
              'snippet' => 'name',
              'template' => 'templatename',
              'templatevar' => 'name',
              'resource' => 'title',
          ];
          
          if (!array_key_exists($type, $titleColumns)) {
              $type = 'resource';
          }
          
          if ($maxVersions < 1) {
              return 'VersionX cleanup error: maxVersions must be greater than 0.';
          }
          
          $table = $modx->getOption('table_prefix') . 'versionx_' . $type;
          $titleColumn = $titleColumns[$type];
          $query = "SELECT version_id, content_id, {$titleColumn} AS page_title FROM `{$table}` ORDER BY content_id ASC, version_id DESC";
          
          $stmt = $modx->query($query);
          if (!is_object($stmt)) {
              return 'VersionX cleanup query error: ' . print_r($modx->errorInfo(), true);
          }
          
          $rowsHtml = '';
          $total = 0;
          $deleted = 0;
          $currentContentId = null;
          $currentTitle = '';
          $currentTotal = 0;
          $currentDeleted = 0;
          $keptForCurrent = 0;
          
          $flushRow = function () use (&$rowsHtml, &$currentContentId, &$currentTitle, &$currentTotal, &$currentDeleted) {
              if ($currentContentId === null) {
                  return;
              }
          
              $rowsHtml .= '<tr><td>' . htmlspecialchars((string)$currentTitle, ENT_QUOTES, 'UTF-8') . '</td><td>' .
                  (int)$currentTotal . '</td><td>' . (int)$currentDeleted . "</td></tr>\n";
          };
          
          while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
              $contentId = (int)$row['content_id'];
          
              if ($currentContentId !== $contentId) {
                  $flushRow();
                  $currentContentId = $contentId;
                  $currentTitle = (string)$row['page_title'];
                  $currentTotal = 0;
                  $currentDeleted = 0;
                  $keptForCurrent = 0;
              }
          
              $total++;
              $currentTotal++;
          
              if ($keptForCurrent < $maxVersions) {
                  $keptForCurrent++;
                  continue;
              }
          
              $versionId = (int)$row['version_id'];
              if (!$dryRun) {
                  $delete = $modx->query("DELETE FROM `{$table}` WHERE version_id = {$versionId}");
                  if (!is_object($delete)) {
                      return 'VersionX cleanup delete error for ' . htmlspecialchars($currentTitle, ENT_QUOTES, 'UTF-8') .
                          ': ' . print_r($modx->errorInfo(), true);
                  }
              }
          
              $deleted++;
              $currentDeleted++;
          }
          
          $flushRow();
          
          $optimizeMessage = '';
          if (!$dryRun && $optimize) {
              $optimized = $modx->query("OPTIMIZE TABLE `{$table}`");
              if (!is_object($optimized)) {
                  $optimizeMessage = '<p>Optimize error: ' . htmlspecialchars(print_r($modx->errorInfo(), true), ENT_QUOTES, 'UTF-8') . '</p>';
              }
          }
          
          return '<h3>VersionX Cleanup for ' . htmlspecialchars($table, ENT_QUOTES, 'UTF-8') . '</h3>' .
              ($dryRun ? '<p><strong>Dry run:</strong> rows were not deleted.</p>' : '') .
              '<p>Total records: <strong>' . (int)$total . '</strong>
          Total deleted: <strong>' . (int)$deleted . '</strong></p>' .
              $optimizeMessage .
              '<table class="table table-striped"><thead><tr><th>Page name</th><th>Total found</th><th>Deleted</th></tr></thead><tbody>' .
              $rowsHtml .
              '</tbody></table>';
          Итого имеем удобный вывод с вкладками.
          <cut/>
          Из статьи на моем сайте gowindo.ru/articles/modx/versioncleanxtabs-chistka-ot-ustarevshix-versij
            Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
            8