• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
No Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

Commit MetaInfo

Revisionc53b77c930cba0138049a5ea78f6ee961c5f9e65 (tree)
Time2018-02-27 00:10:34
Authorumorigu <umorigu@gmai...>
Commiterumorigu

Log Message

BugTrack/560 tracker_list speeding up with cache

* Implement 2-layer caching

  • Converted HTML cache
  • Contents cache for list pages

* Get only updated/deleted page contents
* Detect page updates by file or page timestamps
* Refer cache/recent.dat file to detect updated pages
* Refer RecentDeleted page to detect deleted pages
* Handle page links properly on tracker_list table
* Save contents cache on cache/(TrackerBase).tracker

How to invalidate cache:

Execute one of following actions.

* Change tracker config file
* Change tracker list file
* Delete cache/*.tracker file manually

Change Summary

Incremental Difference

--- a/plugin/tracker.inc.php
+++ b/plugin/tracker.inc.php
@@ -1,7 +1,7 @@
11 <?php
22 // PukiWiki - Yet another WikiWikiWeb clone
33 // tracker.inc.php
4-// Copyright 2003-2017 PukiWiki Development Team
4+// Copyright 2003-2018 PukiWiki Development Team
55 // License: GPL v2 or (at your option) any later version
66 //
77 // Issue tracker plugin (See Also bugtrack plugin)
@@ -15,6 +15,9 @@ define('TRACKER_LIST_EXCLUDE_PATTERN','#^SubMenu$|/#');
1515 // 項目の取り出しに失敗したページを一覧に表示する
1616 define('TRACKER_LIST_SHOW_ERROR_PAGE',TRUE);
1717
18+// Use cache
19+define('TRACKER_LIST_USE_CACHE', TRUE);
20+
1821 function plugin_tracker_convert()
1922 {
2023 global $vars;
@@ -684,13 +687,13 @@ function plugin_tracker_list_action()
684687 }
685688 function plugin_tracker_getlist($page,$refer,$config_name,$list,$order='',$limit=NULL)
686689 {
687- $config = new Config('plugin/tracker/'.$config_name);
690+ global $whatsdeleted;
688691
692+ $config = new Config('plugin/tracker/'.$config_name);
689693 if (!$config->read())
690694 {
691695 return "<p>config file '".htmlsc($config_name)."' is not exist.</p>";
692696 }
693-
694697 $config->config_name = $config_name;
695698
696699 if (!is_page($config->page.'/'.$list))
@@ -698,9 +701,79 @@ function plugin_tracker_getlist($page,$refer,$config_name,$list,$order='',$limit
698701 return "<p>config file '".make_pagelink($config->page.'/'.$list)."' not found.</p>";
699702 }
700703
701- $list = new Tracker_list($page,$refer,$config,$list);
702- $list->sort($order);
703- return $list->toString($limit);
704+ $cache_enabled = defined('TRACKER_LIST_USE_CACHE') && TRACKER_LIST_USE_CACHE &&
705+ defined('JSON_UNESCAPED_UNICODE') && defined('PKWK_UTF8_ENABLE');
706+ $cache_filepath = CACHE_DIR . encode($page) . '.tracker';
707+ $cachedata = null;
708+ $cache_format_version = 1;
709+ if ($cache_enabled && file_exists($cache_filepath)) {
710+ $config_filetime = get_filetime($config->page);
711+ $config_list_filetime = get_filetime($config->page.'/'. $list);
712+ $json_cached = pkwk_file_get_contents($cache_filepath);
713+ if ($json_cached) {
714+ $wrapdata = json_decode($json_cached, true);
715+ if (is_array($wrapdata)) {
716+ if (isset($wrapdata['version'], $wrapdata['html'], $wrapdata['refreshed_at'])) {
717+ $cache_time_prev = $wrapdata['refreshed_at'];
718+ if ($cache_format_version === $wrapdata['version']) {
719+ if ($config_filetime === $wrapdata['config_updated_at'] &&
720+ $config_list_filetime === $wrapdata['config_list_updated_at']) {
721+ $cachedata = $wrapdata;
722+ } else {
723+ // (Ignore) delete file
724+ unlink($cache_filepath);
725+ }
726+ }
727+ }
728+ }
729+ }
730+ }
731+ // Check recent.dat timestamp
732+ $recent_dat_filemtime = filemtime(CACHE_DIR . PKWK_MAXSHOW_CACHE);
733+ // Check RecentDeleted timestamp
734+ $recent_deleted_filetime = get_filetime($whatsdeleted);
735+ if (is_null($cachedata)) {
736+ $cachedata = array();
737+ } else {
738+ if ($recent_dat_filemtile !== false) {
739+ if ($recent_dat_filemtime === $cachedata['recent_dat_filemtime'] &&
740+ $recent_deleted_filetime === $cachedata['recent_deleted_filetime'] &&
741+ $order === $cachedata['order']) {
742+ // recent.dat is unchanged
743+ // RecentDeleted is unchanged
744+ // order is unchanged
745+ return $cachedata['html'];
746+ }
747+ }
748+ }
749+ $cache_holder = $cachedata;
750+ $tracker_list = new Tracker_list($page,$refer,$config,$list,$cache_holder);
751+ if ($order === $cache_holder['order'] &&
752+ empty($tracker_list->newly_deleted_pages) &&
753+ empty($tracker_list->newly_updated_pages) &&
754+ !$tracker_list->link_update_required) {
755+ $result = $cache_holder['html'];
756+ } else {
757+ $tracker_list->sort($order);
758+ $result = $tracker_list->toString($limit);
759+ }
760+ if ($cache_enabled) {
761+ $refreshed_at = time();
762+ $json = array(
763+ 'refreshed_at' => $refreshed_at,
764+ 'rows' => $tracker_list->rows,
765+ 'html' => $result,
766+ 'order' => $order,
767+ 'config_updated_at' => $config_filetime,
768+ 'config_list_updated_at' => $config_list_filetime,
769+ 'recent_dat_filemtime' => $recent_dat_filemtime,
770+ 'recent_deleted_filetime' => $recent_deleted_filetime,
771+ 'link_pages' => $tracker_list->link_pages,
772+ 'version' => $cache_format_version);
773+ $cache_body = json_encode($json, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
774+ file_put_contents($cache_filepath, $cache_body, LOCK_EX);
775+ }
776+ return $result;
704777 }
705778
706779 // 一覧クラス
@@ -715,13 +788,16 @@ class Tracker_list
715788 var $rows;
716789 var $order;
717790 var $sort_keys;
791+ var $newly_deleted_pages = array();
792+ var $newly_updated_pages = array();
718793
719- function Tracker_list($page,$refer,&$config,$list)
794+ function Tracker_list($page,$refer,&$config,$list,&$cache_holder)
720795 {
721- $this->__construct($page, $refer, $config, $list);
796+ $this->__construct($page, $refer, $config, $list, $cache_holder);
722797 }
723- function __construct($page,$refer,&$config,$list)
798+ function __construct($page,$refer,&$config,$list,&$cache_holder)
724799 {
800+ global $whatsdeleted;
725801 $this->page = $page;
726802 $this->config = &$config;
727803 $this->list = $list;
@@ -746,22 +822,188 @@ class Tracker_list
746822 $this->pattern .= '(.*?)';
747823 }
748824 }
749- // ページの列挙と取り込み
750- $this->rows = array();
751- $pattern = "$page/";
752- $pattern_len = strlen($pattern);
753- foreach (get_existpages() as $_page)
754- {
755- if (strpos($_page,$pattern) === 0)
825+ if (empty($cache_holder)) {
826+ // List pages and get contents (non-cache behavior)
827+ $this->rows = array();
828+ $pattern = "$page/";
829+ $pattern_len = strlen($pattern);
830+ foreach (get_existpages() as $_page)
756831 {
757- $name = substr($_page,$pattern_len);
758- if (preg_match(TRACKER_LIST_EXCLUDE_PATTERN,$name))
832+ if (substr($_page, 0, $pattern_len) === $pattern)
759833 {
760- continue;
834+ $name = substr($_page,$pattern_len);
835+ if (preg_match(TRACKER_LIST_EXCLUDE_PATTERN,$name))
836+ {
837+ continue;
838+ }
839+ $this->add($_page,$name);
840+ }
841+ }
842+ $this->link_pages = $this->get_filetimes($this->get_all_links());
843+ } else {
844+ // Cache-available behavior
845+ // Check RecentDeleted timestamp
846+ $cached_rows = $this->decode_cached_rows($cache_holder['rows']);
847+ $updated_linked_pages = array();
848+ $newly_deleted_pages = array();
849+ $pattern = "$page/";
850+ $pattern_len = strlen($pattern);
851+ $recent_deleted_filetime = get_filetime($whatsdeleted);
852+ $deleted_page_list = array();
853+ if ($recent_deleted_filetime !== $cache_holder['recent_deleted_filetime']) {
854+ //pkwk_log(print_r());
855+ foreach (plugin_tracker_get_source($whatsdeleted) as $line) {
856+ $m = null;
857+ if (preg_match('#\[\[([^\]]+)\]\]#', $line, $m)) {
858+ $_page = $m[1];
859+ if (is_pagename($_page)) {
860+ $deleted_page_list[] = $m[1];
861+ }
862+ }
863+ }
864+ foreach ($deleted_page_list as $_page) {
865+ if (substr($_page, 0, $pattern_len) === $pattern) {
866+ $name = substr($_page, $pattern_len);
867+ if (!is_page($_page) && isset($cached_rows[$name]) &&
868+ !preg_match(TRACKER_LIST_EXCLUDE_PATTERN, $name)) {
869+ // This page was just deleted
870+ array_push($newly_deleted_pages, $_page);
871+ unset($cached_rows[$name]);
872+ }
873+ }
761874 }
762- $this->add($_page,$name);
763875 }
876+ $this->newly_deleted_pages = $newly_deleted_pages;
877+ $updated_pages = array();
878+ $this->rows = $cached_rows;
879+ // Check recent.dat timestamp
880+ $recent_dat_filemtime = filemtime(CACHE_DIR . PKWK_MAXSHOW_CACHE);
881+ $updated_page_list = array();
882+ if ($recent_dat_filemtime !== $cache_holder['recent_dat_filemtime']) {
883+ // recent.dat was updated. Search which page was updated.
884+ $target_pages = array();
885+ // Active page file time (1 hour before timestamp of recent.dat)
886+ $target_filetime = $cache_holder['recent_dat_filemtime'] - LOCALZONE - 60 * 60;
887+ foreach (get_recent_files() as $_page=>$time) {
888+ if ($time <= $target_filetime) {
889+ // Older updated pages
890+ break;
891+ }
892+ $updated_page_list[$_page] = $time;
893+ $name = substr($_page, $pattern_len);
894+ if (substr($_page, 0, $pattern_len) === $pattern) {
895+ $name = substr($_page, $pattern_len);
896+ if (preg_match(TRACKER_LIST_EXCLUDE_PATTERN, $name)) {
897+ continue;
898+ }
899+ // Tracker target page
900+ if (isset($this->rows[$name])) {
901+ // Existing page
902+ $row = $this->rows[$name];
903+ if ($row['_update'] === get_filetime($_page)) {
904+ // Same as cache
905+ continue;
906+ } else {
907+ // Found updated page
908+ $updated_pages[] = $_page;
909+ unset($this->rows[$name]);
910+ $this->add($_page, $name);
911+ }
912+ } else {
913+ // Add new page
914+ $updated_pages[] = $_page;
915+ $this->add($_page, $name);
916+ }
917+ }
918+ }
919+ }
920+ $this->newly_updated_pages = $updated_pages;
921+ $new_link_names = $this->get_all_links();
922+ $old_link_map = array();
923+ foreach ($cache_holder['link_pages'] as $link_page) {
924+ $old_link_map[$link_page['page']] = $link_page['filetime'];
925+ }
926+ $new_link_map = $old_link_map;
927+ $link_update_required = false;
928+ foreach ($deleted_page_list as $_page) {
929+ if (in_array($_page, $new_link_names)) {
930+ if (isset($old_link_map[$_page])) {
931+ // This link keeps existing
932+ if (!is_page($_page)) {
933+ // OK. Confirmed the page doesn't exist
934+ if ($old_link_map[$_page] === 0) {
935+ // Do nothing (From no-page to no-page)
936+ } else {
937+ // This page was just deleted
938+ $new_link_map[$_page] = get_filetime($_page);
939+ $link_update_required = true;
940+ }
941+ }
942+ } else {
943+ // This link was just added
944+ $new_link_map[$_page] = get_filetime($_page);
945+ $link_update_required = true;
946+ }
947+ }
948+ }
949+ foreach ($updated_page_list as $_page=>$time) {
950+ if (in_array($_page, $new_link_names)) {
951+ if (isset($old_link_map[$_page])) {
952+ // This link keeps existing
953+ if (is_page($_page)) {
954+ // OK. Confirmed the page now exists
955+ if ($old_link_map[$_page] === 0) {
956+ // This page was just added
957+ $new_link_map[$_page] = get_filetime($_page);
958+ $link_update_required = true;
959+ } else {
960+ // Do nothing (existing-page to existing-page)
961+ }
962+ }
963+ } else {
964+ // This link was just added
965+ $new_link_map[$_page] = get_filetime($_page);
966+ $link_update_required = true;
967+ }
968+ }
969+ }
970+ $new_link_pages = array();
971+ foreach ($new_link_map as $_page => $time) {
972+ $new_link_pages[] = array(
973+ 'page' => $_page,
974+ 'filetime' => $time,
975+ );
976+ }
977+ $this->link_pages = $new_link_pages;
978+ $this->link_update_required = $link_update_required;
979+ }
980+ }
981+ function decode_cached_rows($decoded_rows)
982+ {
983+ $ar = array();
984+ foreach ($decoded_rows as $row) {
985+ $ar[$row['_real']] = $row;
986+ }
987+ return $ar;
988+ }
989+ function get_all_links() {
990+ $ar = array();
991+ foreach ($this->rows as $row) {
992+ foreach ($row['_links'] as $link) {
993+ $ar[$link] = 0;
994+ }
995+ }
996+ return array_keys($ar);
997+ }
998+ function get_filetimes($pages) {
999+ $filetimes = array();
1000+ foreach ($pages as $page) {
1001+ $filetimes[] = array(
1002+ 'page' => $page,
1003+ 'filetime' => get_filetime($page),
1004+ );
7641005 }
1006+ return $filetimes;
7651007 }
7661008 function add($page,$name)
7671009 {
@@ -786,22 +1028,38 @@ class Tracker_list
7861028 }
7871029 $source = join('',preg_replace('/^(\*{1,3}.*)\[#[A-Za-z][\w-]+\](.*)$/','$1$2',$source));
7881030
789- // デフォルト値
790- $this->rows[$name] = array(
1031+ // Default value
1032+ $page_filetime = get_filetime($page);
1033+ $row = array(
7911034 '_page' => "[[$page]]",
7921035 '_refer' => $this->page,
7931036 '_real' => $name,
794- '_update'=> get_filetime($page),
795- '_past' => get_filetime($page)
1037+ '_update'=> $page_filetime,
1038+ '_past' => $page_filetime,
7961039 );
797- if ($this->rows[$name]['_match'] = preg_match("/{$this->pattern}/s",$source,$matches))
1040+ $links = array();
1041+ if ($row['_match'] = preg_match("/{$this->pattern}/s",$source,$matches))
7981042 {
7991043 array_shift($matches);
8001044 foreach ($this->pattern_fields as $key=>$field)
8011045 {
802- $this->rows[$name][$field] = trim($matches[$key]);
1046+ $row[$field] = trim($matches[$key]);
1047+ if ($field === '_refer') {
1048+ continue;
1049+ }
1050+ $lmatch = null;
1051+ if (preg_match('/\[\[([^\]\]]+)\]/', $row[$field], $lmatch)) {
1052+ $link = $lmatch[1];
1053+ if (is_pagename($link) && $link !== $this->page && $link !== $page) {
1054+ if (!in_array($link, $links)) {
1055+ $links[] = $link;
1056+ }
1057+ }
1058+ }
8031059 }
8041060 }
1061+ $row['_links'] = $links;
1062+ $this->rows[$name] = $row;
8051063 }
8061064 function compare($a, $b)
8071065 {
--- a/plugin/tracker_list.inc.php
+++ b/plugin/tracker_list.inc.php
@@ -1,7 +1,7 @@
11 <?php
22 // PukiWiki - Yet another WikiWikiWeb clone
33 // tracker_list.inc.php
4-// Copyright 2003-2017 PukiWiki Development Team
4+// Copyright 2003-2018 PukiWiki Development Team
55 // License: GPL v2 or (at your option) any later version
66 //
77 // Issue tracker list plugin (a part of tracker plugin)