performance.install 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. <?php
  2. /**
  3. * @file
  4. * Install and update for Performance Logging
  5. *
  6. * Copyright Khalid Baheyeldin 2008 of http://2bits.com
  7. */
  8. // Minimum APC shm memory size to require
  9. define('PERFORMANCE_MIN_MEMORY', 48);
  10. /**
  11. * Implements hook_schema().
  12. */
  13. function performance_schema() {
  14. $schema = array();
  15. $schema['cache_performance'] = array(
  16. 'description' => 'Table that holds summary performance data.',
  17. 'fields' => array(
  18. 'cid' => array(
  19. 'description' => 'Primary Key: Unique cache ID.',
  20. 'type' => 'varchar',
  21. 'length' => 255,
  22. 'not null' => TRUE,
  23. 'default' => '',
  24. ),
  25. 'data' => array(
  26. 'description' => 'A collection of data to cache.',
  27. 'type' => 'blob',
  28. 'not null' => FALSE,
  29. 'size' => 'big',
  30. ),
  31. 'expire' => array(
  32. 'description' => 'A Unix timestamp indicating when the cache entry should expire, or 0 for never.',
  33. 'type' => 'int',
  34. 'not null' => TRUE,
  35. 'default' => 0,
  36. ),
  37. 'created' => array(
  38. 'description' => 'A Unix timestamp indicating when the cache entry was created.',
  39. 'type' => 'int',
  40. 'not null' => TRUE,
  41. 'default' => 0,
  42. ),
  43. 'serialized' => array(
  44. 'description' => 'A flag to indicate whether content is serialized (1) or not (0).',
  45. 'type' => 'int',
  46. 'size' => 'small',
  47. 'not null' => TRUE,
  48. 'default' => 0,
  49. ),
  50. ),
  51. 'indexes' => array(
  52. 'expire' => array('expire'),
  53. ),
  54. 'primary key' => array('cid'),
  55. );
  56. $schema['performance_detail'] = array(
  57. 'fields' => array(
  58. 'pid' => array(
  59. 'description' => 'Primary Key: Unique path ID.',
  60. 'type' => 'serial',
  61. 'not null' => TRUE,
  62. ),
  63. 'timestamp' => array(
  64. 'description' => 'A Unix timestamp indicating when the entry was created.',
  65. 'type' => 'int',
  66. 'not null' => TRUE,
  67. 'default' => 0,
  68. ),
  69. 'bytes' => array(
  70. 'description' => 'Memory consumed in bytes.',
  71. 'type' => 'int',
  72. 'not null' => TRUE,
  73. 'default' => 0,
  74. ),
  75. 'ms' => array(
  76. 'description' => 'Time consumed in milliseconds.',
  77. 'type' => 'int',
  78. 'not null' => TRUE,
  79. 'default' => 0,
  80. ),
  81. 'query_count' => array(
  82. 'description' => 'Number of queries executed.',
  83. 'type' => 'int',
  84. 'not null' => TRUE,
  85. 'default' => 0,
  86. ),
  87. 'query_timer' => array(
  88. 'description' => 'Time taken to execute queries.',
  89. 'type' => 'int',
  90. 'not null' => TRUE,
  91. 'default' => 0,
  92. ),
  93. 'anon' => array(
  94. 'description' => 'Whether the page was accessed by an anonymous user.',
  95. 'type' => 'int',
  96. 'not null' => FALSE,
  97. 'default' => 1,
  98. ),
  99. 'path' => array(
  100. 'description' => 'The path the data belongs to.',
  101. 'type' => 'varchar',
  102. 'length' => '255',
  103. 'not null' => FALSE,
  104. ),
  105. 'language' => array(
  106. 'description' => 'The {languages}.language used when calling this path.',
  107. 'type' => 'varchar',
  108. 'length' => 12,
  109. 'not null' => TRUE,
  110. 'default' => '',
  111. ),
  112. 'data' => array(
  113. 'description' => 'Additional (debug) info for future expansion.',
  114. 'type' => 'blob',
  115. 'not null' => FALSE,
  116. 'size' => 'big',
  117. ),
  118. ),
  119. 'primary key' => array('pid'),
  120. 'indexes' => array(
  121. 'timestamp' => array('timestamp'),
  122. ),
  123. );
  124. return $schema;
  125. }
  126. /**
  127. * Implements hook_install().
  128. */
  129. function performance_install() {
  130. // Set the weight so this module runs last
  131. // SQL: UPDATE {system} SET weight = 3000 WHERE name = 'performance'
  132. db_update('system')
  133. ->fields(array('weight' => 3000))
  134. ->condition('name', 'performance')
  135. ->execute();
  136. }
  137. /**
  138. * Implements hook_uninstall().
  139. */
  140. function performance_uninstall() {
  141. // SQL: DELETE FROM {variable} WHERE name LIKE 'performance\_%'
  142. db_delete('variable')
  143. ->condition('name', 'performance\_%', 'LIKE')
  144. ->execute();
  145. }
  146. /**
  147. * Implements hook_requirements().
  148. */
  149. function performance_requirements($phase) {
  150. $requirements = array();
  151. if ($phase != 'runtime') {
  152. return $requirements;
  153. }
  154. if (variable_get('performance_detail', 0)) {
  155. $requirements['performance_detail'] = array(
  156. 'title' => t('Performance logging details'),
  157. 'value' => 'Enabled',
  158. 'severity' => REQUIREMENT_WARNING,
  159. 'description' => t('Performance detailed logging is !link. This can cause severe issues on live sites.', array('!link' => l(t('enabled'), PERFORMANCE_SETTINGS))),
  160. );
  161. }
  162. if (variable_get(PERFORMANCE_QUERY_VAR, 0)) {
  163. if (variable_get('performance_detail', 0) || variable_get('performance_summary', 0)) {
  164. $requirements['performance_query'] = array(
  165. 'title' => t('Performance logging query'),
  166. 'value' => 'Enabled',
  167. 'severity' => REQUIREMENT_WARNING,
  168. 'description' => t('Query timing and count logging is !link. This can cause memory size per page to be larger than normal.', array('!link' => l(t('enabled'), PERFORMANCE_SETTINGS))),
  169. );
  170. }
  171. }
  172. $default = 'DrupalDatabaseCache';
  173. $cache = variable_get(PERFORMANCE_CACHE, $default);
  174. if ($cache == $default) {
  175. $requirements['performance_cache'] = array(
  176. 'title' => t('Performance logging summary'),
  177. 'value' => 'Disabled',
  178. 'severity' => REQUIREMENT_WARNING,
  179. 'description' => t('Performance logging on live web sites works best if an alternative caching mechanism, like APC or Memcache, is enabled.'),
  180. );
  181. }
  182. $shm_size = ini_get('apc.shm_size');
  183. if (function_exists('apc_fetch') && $shm_size < PERFORMANCE_MIN_MEMORY) {
  184. $requirements['performance_apc_mem'] = array(
  185. 'title' => t('Performance logging APC memory size'),
  186. 'value' => $shm_size,
  187. 'severity' => REQUIREMENT_WARNING,
  188. 'description' => t('APC has been configured for !size, which is less than the recommended !min_memory MB of memory. If you encounter errors when viewing the summary report, then try to increase that limit for APC.', array('!size' => 1 * $shm_size, '!min_memory' => PERFORMANCE_MIN_MEMORY)),
  189. );
  190. }
  191. return $requirements;
  192. }
  193. /**
  194. * Harmonize notations for milliseconds to "ms".
  195. */
  196. function performance_update_7001() {
  197. $int_field = array(
  198. 'type' => 'int',
  199. 'not null' => TRUE,
  200. 'default' => 0,
  201. 'disp-width' => '11',
  202. );
  203. db_change_field('performance_summary', 'millisecs_max', 'ms_max', $int_field);
  204. db_change_field('performance_summary', 'millisecs_avg', 'ms_avg', $int_field);
  205. db_change_field('performance_detail', 'millisecs', 'ms', $int_field);
  206. // We don't have a cache update method, so it's better to clear it
  207. if (function_exists('apc_fetch')) {
  208. apc_clear_cache('user');
  209. }
  210. }
  211. /**
  212. * Move summary data from old performance_summary table to new cache bin.
  213. * Note that we now cache per language and separate anonymous users from logged
  214. * in users! During this migration, we do BASIC separation between anonymous and
  215. * logged in users based on admin paths. Back up your data if you want to keep
  216. * the original logs!
  217. * DRUSH NOTE: make sure to set a proper $base_url using the --uri parameter or
  218. * by means of a drushrc.php config file! Alternatively you can setup a
  219. * $conf['performance_key'] = 'my_unique_key'; in settings.php when using
  220. * multiple domains for one site.
  221. */
  222. function performance_update_7200() {
  223. global $language;
  224. // Abort when improperly invoked when using drush. See DRUSH NOTE above.
  225. if(drupal_is_cli() && variable_get('performance_key', FALSE) === FALSE && $GLOBALS['base_url'] == 'http://default') {
  226. // Throw an exception to make this update fail. Found no other way. Better
  227. // suggestion anyone? Tried various drush functions, such as
  228. // drush_set_error(), to no avail. Update was always marked as 'completed'
  229. // and was not re-executed when re-running drush updb.
  230. throw new Exception(dt("Please run drush with the --uri parameter or setup a drushrc.php file to successfully migrate the summary data! The site's domain name will be used in the cache key! Use \$conf['performance_key'] = 'my_unique_key'; in settings.php when using multiple domains on one site."));
  231. return;
  232. }
  233. // Create new cache bin, see http://drupal.org/node/150220.
  234. $table = array(
  235. 'description' => 'Table that holds summary performance data.',
  236. 'fields' => array(
  237. 'cid' => array(
  238. 'description' => 'Primary Key: Unique cache ID.',
  239. 'type' => 'varchar',
  240. 'length' => 255,
  241. 'not null' => TRUE,
  242. 'default' => '',
  243. ),
  244. 'data' => array(
  245. 'description' => 'A collection of data to cache.',
  246. 'type' => 'blob',
  247. 'not null' => FALSE,
  248. 'size' => 'big',
  249. ),
  250. 'expire' => array(
  251. 'description' => 'A Unix timestamp indicating when the cache entry should expire, or 0 for never.',
  252. 'type' => 'int',
  253. 'not null' => TRUE,
  254. 'default' => 0,
  255. ),
  256. 'created' => array(
  257. 'description' => 'A Unix timestamp indicating when the cache entry was created.',
  258. 'type' => 'int',
  259. 'not null' => TRUE,
  260. 'default' => 0,
  261. ),
  262. 'serialized' => array(
  263. 'description' => 'A flag to indicate whether content is serialized (1) or not (0).',
  264. 'type' => 'int',
  265. 'size' => 'small',
  266. 'not null' => TRUE,
  267. 'default' => 0,
  268. ),
  269. ),
  270. 'indexes' => array(
  271. 'expire' => array('expire'),
  272. ),
  273. 'primary key' => array('cid'),
  274. );
  275. $keys_cache = array();
  276. // Keep records for 1 day.
  277. $expire = REQUEST_TIME + (24 * 60 * 60);
  278. // Extra check to prevent errors when running this update multiple times.
  279. if (db_table_exists('performance_summary') && !db_table_exists('cache_performance')) {
  280. db_create_table('cache_performance', $table);
  281. $result = db_select('performance_summary', 'ps')
  282. ->fields('ps')
  283. ->execute();
  284. foreach ($result as $row) {
  285. // Make some BASIC separation between anonymous users and logged in users.
  286. $anonymous = 1;
  287. if (preg_match('/^(admin|admin\/.*|node\/add.*)$/i', $row->path) || preg_match('/^(node|user)\/\d+\/(edit|translate|delete|revisions.*)$/i', $row->path)) {
  288. $anonymous = 0;
  289. }
  290. $data = array(
  291. 'path' => $row->path,
  292. 'bytes_max' => $row->bytes_max,
  293. 'bytes_sum' => $row->bytes_avg * $row->num_accesses,
  294. 'ms_max' => $row->ms_max,
  295. 'ms_sum' => $row->ms_avg * $row->num_accesses,
  296. 'query_timer_max' => $row->query_timer_max,
  297. 'query_timer_sum' => $row->query_timer_avg * $row->num_accesses,
  298. 'query_count_max' => $row->query_count_max,
  299. 'query_count_sum' => $row->query_count_avg * $row->num_accesses,
  300. 'num_accesses' => $row->num_accesses,
  301. 'last_access' => $row->last_access,
  302. 'anon' => $anonymous,
  303. 'language' => $language->language,
  304. );
  305. // We now cache per language and distinguish between anonymous users and
  306. // logged in users. As usual, we keep logs for one day!
  307. $key = PERFORMANCE_KEY . $row->path . ':' . $language->language . ':' . $anonymous;
  308. cache_set($key, $data, 'cache_performance', $expire);
  309. // Keep the key for the key cache store. We do it this way so that keys
  310. // will replace eachother which would not happen when using
  311. // $keys_cache[] = $key;
  312. $keys_cache[$key] = 1;
  313. }
  314. // This will allow easy retrieval of data as we cannot use SQL queries since
  315. // the cache store can by anything!
  316. cache_set(PERFORMANCE_KEY, $keys_cache, 'cache_performance');
  317. // No longer needed!
  318. db_drop_table('performance_summary');
  319. // Preserve summary logging setting if enabled.
  320. // There can, in theory, be unlimited variables named performance_summary_%
  321. // where we do not know what % is, hence this approach.
  322. $result = db_select('variable', 'v')
  323. ->fields('v', array('value'))
  324. ->condition('name', 'performance_summary\_%', 'LIKE')
  325. ->execute();
  326. foreach ($result as $row) {
  327. if (unserialize($row->value)) {
  328. variable_set('performance_summary', 1);
  329. break;
  330. }
  331. }
  332. // Delete old performance summary variables!
  333. db_delete('variable')
  334. ->condition('name', 'performance_summary\_%', 'LIKE')
  335. ->execute();
  336. return t('Migrated all data from old performance_summary table to new cache bin.');
  337. }
  338. }
  339. /**
  340. * Add new language field to performance_detail table.
  341. */
  342. function performance_update_7201() {
  343. $field = 'language';
  344. $table = 'performance_detail';
  345. // See http://drupal.org/node/150220.
  346. $language = array(
  347. 'description' => 'The {languages}.language used when calling this path.',
  348. 'type' => 'varchar',
  349. 'length' => 12,
  350. 'not null' => TRUE,
  351. 'default' => '',
  352. );
  353. if (!db_field_exists($table, $field)) {
  354. db_add_field($table, $field, $language);
  355. return t('Created language field in performance_detail table.');
  356. }
  357. }