Use table cache_waypoint_pool to generate a list of unused waypoints and assign waypoints to new caches from this list.

This commit is contained in:
ocoliver
2012-11-04 22:16:39 +01:00
parent b298bfccc0
commit d6ae786829
7 changed files with 332 additions and 43 deletions

View File

@@ -316,7 +316,24 @@
*/
$opt['logic']['ocprefixes'] = 'oc';
/* Database charset
/* pregenerated waypoint list for new caches
* - Waypoint prefix (OC, OP, OZ ... AA=local development)
* - When pool contains less than min_count, generation process starts
* and fills up the pool until max_count is reached.
*/
$opt['logic']['waypoint_pool']['prefix'] = 'AA';
$opt['logic']['waypoint_pool']['min_count'] = 1000;
$opt['logic']['waypoint_pool']['max_count'] = 2000;
// chars used for waypoints. Remember to reinstall triggers and clear cache_waypoint_pool after changing
$opt['logic']['waypoint_pool']['valid_chars'] = '0123456789ABCDEF';
// fill_gaps = true: search for gaps between used waypoints and fill up these gaps
// (fill_gaps is slow and CPU intensive on database server. For
// productive servers you may want to generate some waypoints
// without fill_gaps first)
// fill_gaps = false: continue with the last waypoint
$opt['logic']['waypoint_pool']['fill_gaps'] = false;
/* Database charset
* frontend and php charsets are UTF-8
* here you can set a different charset for the MySQL-Engine
* usefull if your database is not UTF-8.

View File

@@ -75,6 +75,99 @@
END IF;
END;");
// get decimal value of waypoint
sql_dropFunction('WPTODEC');
sql("CREATE FUNCTION `WPTODEC` (wp VARCHAR(7), prefix VARCHAR(2)) RETURNS INT DETERMINISTIC SQL SECURITY INVOKER
BEGIN
-- all used chars in waypoint, in their ascending order
DECLARE WP_ORDER CHAR(36) DEFAULT '&1';
-- list of base 36 chars in their ascending order
DECLARE B36_ORDER CHAR(36) DEFAULT '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
-- will contain the waypoint value, without prefix
DECLARE WP_VALUE CHAR(5) DEFAULT '00000';
-- will contain WP_VALUE where all chars replaced by their equivalents in B36_ORDER
DECLARE B36_VALUE CHAR(5) DEFAULT '';
-- loop counter
DECLARE WP_POS INT DEFAULT 1;
-- index of a char in WP_ORDER/B36_ORDER
DECLARE WP_ORDER_INDEX INT;
-- validate input
IF ISNULL(wp) OR ISNULL(prefix) THEN
RETURN 0;
END IF;
IF LENGTH(prefix) != 2 OR LENGTH(wp)<3 OR LENGTH(wp)>7 THEN
RETURN 0;
END IF;
IF LEFT(wp, 2) != prefix THEN
RETURN 0;
END IF;
-- get waypoint value with exactly 5 digits
SET WP_VALUE = RIGHT(CONCAT('00000', SUBSTRING(wp, 3)), 5);
-- replace each char in WP_VALUE with the equivalent base 36 char
REPEAT
SET WP_ORDER_INDEX = LOCATE(SUBSTRING(WP_VALUE, WP_POS, 1), WP_ORDER);
IF WP_ORDER_INDEX = 0 THEN
RETURN 0;
END IF;
SET B36_VALUE = CONCAT(B36_VALUE, SUBSTRING(B36_ORDER, WP_ORDER_INDEX, 1));
SET WP_POS = WP_POS + 1;
UNTIL WP_POS>5 END REPEAT;
-- now use CONV() to convert from base 36 system to decimal
RETURN CONV(B36_VALUE, LENGTH(WP_ORDER), 10);
END;",
$opt['logic']['waypoint_pool']['valid_chars']);
// inverse function of WPTODEC
sql_dropFunction('DECTOWP');
sql("CREATE FUNCTION `DECTOWP` (wp INT, prefix VARCHAR(2)) RETURNS VARCHAR(7) DETERMINISTIC SQL SECURITY INVOKER
BEGIN
-- all used chars in waypoint, in their ascending order
DECLARE WP_ORDER CHAR(36) DEFAULT '&1';
-- list of base 36 chars in their ascending order
DECLARE B36_ORDER CHAR(36) DEFAULT '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
-- base 36 value of the decimal waypoint value
DECLARE B36_VALUE VARCHAR(5);
-- will contain the waypoint value, without prefix
DECLARE WP_VALUE CHAR(5) DEFAULT '';
-- loop counter
DECLARE B36_POS INT DEFAULT 1;
-- index of a char in WP_ORDER/B36_ORDER
DECLARE B36_ORDER_INDEX INT;
-- validate input
IF ISNULL(wp) OR ISNULL(prefix) THEN
RETURN '';
END IF;
IF LENGTH(prefix) != 2 OR wp=0 THEN
RETURN '';
END IF;
-- convert the decimal waypoint value to base 36
SET B36_VALUE = CONV(wp, 10, LENGTH(WP_ORDER));
-- replace each char in B36_VALUE with the equivalent wp-char
REPEAT
SET B36_ORDER_INDEX = LOCATE(SUBSTRING(B36_VALUE, B36_POS, 1), B36_ORDER);
IF B36_ORDER_INDEX = 0 THEN
RETURN '';
END IF;
SET WP_VALUE = CONCAT(WP_VALUE, SUBSTRING(WP_ORDER, B36_ORDER_INDEX, 1));
SET B36_POS = B36_POS + 1;
UNTIL B36_POS>LENGTH(B36_VALUE) END REPEAT;
IF LENGTH(WP_VALUE)<4 THEN
RETURN CONCAT(prefix, RIGHT(CONCAT('0000', WP_VALUE), 4));
ELSE
RETURN CONCAT(prefix, WP_VALUE);
END IF;
END;",
$opt['logic']['waypoint_pool']['valid_chars']);
/* Stored procedures containing database logic
*/

View File

@@ -0,0 +1,8 @@
SET NAMES 'utf8';
DROP TABLE IF EXISTS `cache_waypoint_pool`;
CREATE TABLE `cache_waypoint_pool` (
`wp_oc` char(7) NOT NULL,
`cache_id` int(10) unsigned default NULL,
PRIMARY KEY (`wp_oc`),
KEY `cache_id` (`cache_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;

View File

@@ -86,43 +86,26 @@
// set a unique waypoint to this cache
function setCacheWaypoint($cacheid)
{
global $oc_waypoint_prefix;
global $opt;
// The following code runs into trouble, if multiple users/threads request a new
// waypoint synchronously (-> RT ticket #4071). We will hack around this problem by
// repeating the operation with random delays. Another solution would be a table-lock.
// cleanup previous assignments failures
sql("DELETE FROM `cache_waypoint_pool` WHERE `cache_id`='&1'", $cacheid);
// 2012-08-23 following
// changes: removed 4 hexdigits limit; added random delay;
// increased maxloop 10 -> 20; simplified loop
// reserve a waypoint
sql("UPDATE `cache_waypoint_pool` SET `cache_id`='&1' ORDER BY WPTODEC(`wp_oc`, '&2') ASC LIMIT 1", $cacheid, $opt['logic']['waypoint_pool']['prefix']);
$nLoop = 20;
// TODO: cronjob for waypoint pool generation may not run on development systems
// add a fix to generate a new waypoints on demand (insert this new waypoint to cache_waypoint_pool with reserved cache_id
// and follow standard assignment code to prevent race conditions with cronjob)
do
{
// add zero to convert CONV's string result to a sortable number
$rs = sql("SELECT MAX(0 + CONV(SUBSTR(wp_oc,3),16,10)) maxwp FROM `caches` WHERE wp_oc!='OCGC77'");
$r = sql_fetch_assoc($rs);
mysql_free_result($rs);
// assign reserved waypoint to the cache
// for the moment, we use IGNORE to catch duplicate keys that occur in any failure case
// the cache keeps without a waypoint in this case. Later we change field caches.wp_oc to NOT NULL and assign waypoint in BEFORE INSERT trigger
sql("UPDATE IGNORE `caches` INNER JOIN `cache_waypoint_pool` ON `caches`.`cache_id`=`cache_waypoint_pool`.`cache_id` SET `caches`.`wp_oc`=`cache_waypoint_pool`.`wp_oc` WHERE `caches`.`cache_id`='&1'", $cacheid);
if ($r['maxwp'] == null)
$nNext = 1; // first cache in database
else
$nNext = $r['maxwp'] + 1;
$nNext = dechex($nNext);
while (mb_strlen($nNext) < 4)
$nNext = '0' . $nNext;
$sWP = $oc_waypoint_prefix . mb_strtoupper($nNext);
if (sql("UPDATE IGNORE `caches` SET `wp_oc`='&1' WHERE `cache_id`='&2' AND ISNULL(`wp_oc`)", $sWP, $cacheid))
$nLoop = 0;
else
usleep(rand(0,50000));
} while (--$nLoop > 0);
}
// cleanup
sql("DELETE FROM `cache_waypoint_pool` WHERE `cache_id`='&1'", $cacheid);
}
function setLastFound($cacheid)
{

View File

@@ -40,10 +40,6 @@
$oc_nodeid = 4;
$opt['logic']['node']['id'] = 4;
// waypoint prefix of the node
// OC = oc.de, OP = oc.pl, ... AA = local development
$oc_waypoint_prefix = 'OC';
//name of the cookie
$opt['cookie']['name'] = 'oc_devel';
$opt['cookie']['path'] = '/';
@@ -120,7 +116,25 @@
$opt['translate']['debug'] = false;
// see config2/settings-dist.inc.php
// copy of config2/settings-dist.inc.php
/* pregenerated waypoint list for new caches
* - Waypoint prefix (OC, OP, OZ etc.)
* - When pool contains less than min_count, generation process starts
* and fills up the pool until max_count is reached.
*/
$opt['logic']['waypoint_pool']['prefix'] = 'AA';
$opt['logic']['waypoint_pool']['min_count'] = 1000;
$opt['logic']['waypoint_pool']['max_count'] = 2000;
// chars used for waypoints. Remember to reinstall triggers and clear cache_waypoint_pool after changing
$opt['logic']['waypoint_pool']['valid_chars'] = '0123456789ABCDEF';
// fill_gaps = true: search for gaps between used waypoints and fill up these gaps
// (fill_gaps is slow and CPU intensive on database server. For
// productive servers you may want to generate some waypoints
// without fill_gaps first)
// fill_gaps = false: continue with the last waypoint
$opt['logic']['waypoint_pool']['fill_gaps'] = false;
// see config2/settings-dist.inc.php
$opt['template']['default']['locale'] = 'DE'; // may be overwritten by $opt['domain'][...]['locale']
$opt['template']['locales']['DE']['show'] = true;

View File

@@ -33,10 +33,6 @@
//id of the node
$oc_nodeid = 4;
// waypoint prefix of the node
// OC = oc.de, OP = oc.pl, ... AA = local development
$oc_waypoint_prefix = 'AA';
//name of the cookie
$opt['cookie']['name'] = 'oc_devel';
$opt['cookie']['path'] = '/';
@@ -122,7 +118,25 @@
$opt['translate']['debug'] = false;
// see config2/settings-dist.inc.php
// copy of config2/settings-dist.inc.php
/* pregenerated waypoint list for new caches
* - Waypoint prefix (OC, OP, OZ etc.)
* - When pool contains less than min_count, generation process starts
* and fills up the pool until max_count is reached.
*/
$opt['logic']['waypoint_pool']['prefix'] = 'AA';
$opt['logic']['waypoint_pool']['min_count'] = 1000;
$opt['logic']['waypoint_pool']['max_count'] = 2000;
// chars used for waypoints. Remember to reinstall triggers and clear cache_waypoint_pool after changing
$opt['logic']['waypoint_pool']['valid_chars'] = '0123456789ABCDEF';
// fill_gaps = true: search for gaps between used waypoints and fill up these gaps
// (fill_gaps is slow and CPU intensive on database server. For
// productive servers you may want to generate some waypoints
// without fill_gaps first)
// fill_gaps = false: continue with the last waypoint
$opt['logic']['waypoint_pool']['fill_gaps'] = false;
// see config2/settings-dist.inc.php
$opt['template']['default']['locale'] = 'DE'; // may be overwritten by $opt['domain'][...]['locale']
$opt['template']['default']['country'] = 'DE'; // may be overwritten by $opt['domain'][...]['country']

View File

@@ -0,0 +1,160 @@
<?php
/***************************************************************************
* For license information see doc/license.txt
*
* Unicode Reminder メモ
*
* This cronjob fills the table cache_waypoint_pool with waypoints that
* can be assigned to new caches. The code is cpu intensive on database
* server.
***************************************************************************/
checkJob(new cache_waypoint_pool());
class cache_waypoint_pool
{
var $name = 'cache_waypoint_pool';
var $interval = 604800; // once a week
function run()
{
global $opt;
$nLastInsertsCount = 1;
// check if the pool needs to be filled up and repeat until the
$nPoolSize = $this->getCurrentPoolSize();
if ($nPoolSize < $opt['logic']['waypoint_pool']['min_count'])
{
while (($nPoolSize < $opt['logic']['waypoint_pool']['max_count']) && ($nLastInsertsCount > 0))
{
$nLastInsertsCount = $this->fill($opt['logic']['waypoint_pool']['max_count'] - $nPoolSize);
$nPoolSize = $this->getCurrentPoolSize();
}
}
}
function getCurrentPoolSize()
{
return sql_value("SELECT COUNT(*) FROM `cache_waypoint_pool`", 0);
}
function fill($max_inserts_count)
{
global $opt;
$rowCount = 0;
$nInsertCount = 0;
if ($opt['logic']['waypoint_pool']['fill_gaps'] == true)
{
// query the first unused waypoint (between other waypoints)
$rsStartWp = sql("SELECT SQL_BUFFER_RESULT DECTOWP(WPTODEC(`c`.`wp_oc`, '&1')+1, '&1') AS `free_wp`
FROM `caches` AS `c`
LEFT JOIN `caches` as `cNext` ON DECTOWP(WPTODEC(`c`.`wp_oc`, '&1')+1, '&1')=`cNext`.`wp_oc`
LEFT JOIN `cache_waypoint_pool` ON DECTOWP(WPTODEC(`c`.`wp_oc` ,'&1')+1, '&1')=`cache_waypoint_pool`.`wp_oc`
WHERE `c`.`wp_oc` REGEXP '&2'
AND ISNULL(`cNext`.`wp_oc`)
AND ISNULL(`cache_waypoint_pool`.`wp_oc`)
ORDER BY `free_wp` ASC
LIMIT 250",
$opt['logic']['waypoint_pool']['prefix'],
'^' . $opt['logic']['waypoint_pool']['prefix'] . '[' . $opt['logic']['waypoint_pool']['valid_chars'] . ']{1,}$');
}
else
{
// query the last used waypoint
$rsStartWp = sql("SELECT SQL_BUFFER_RESULT DECTOWP(MAX(dec_wp)+1, '&2') AS `free_wp`
FROM (
SELECT MAX(WPTODEC(`wp_oc`, '&2')) AS dec_wp
FROM `caches`
WHERE `wp_oc` REGEXP '&1'
UNION
SELECT MAX(WPTODEC(`wp_oc`, '&2')) AS dec_wp
FROM `cache_waypoint_pool`
) AS tbl",
'^' . $opt['logic']['waypoint_pool']['prefix'] . '[' . $opt['logic']['waypoint_pool']['valid_chars'] . ']{1,}$',
$opt['logic']['waypoint_pool']['prefix']);
}
while (($rStartWp = sql_fetch_assoc($rsStartWp)) && ($nInsertCount < $max_inserts_count))
{
$free_wp = $rStartWp['free_wp'];
if ($free_wp == '') $free_wp = $opt['logic']['waypoint_pool']['prefix'] . '0001';
$nInsertCount += $this->fill_turn($free_wp, $max_inserts_count - $nInsertCount);
$rowCount++;
}
sql_free_result($rsStartWp);
if ($rowCount == 0)
{
// new database ...
$nInsertCount += $this->fill_turn($opt['logic']['waypoint_pool']['prefix'] . '0001', $max_inserts_count);
}
return $nInsertCount;
}
// search for the next free range and use that waypoints to fill the waypoint pool
function fill_turn($start_wp, $max_inserts_count)
{
global $opt;
// query the end of this waypoint range
$end_wp = sql_value("SELECT DECTOWP(MIN(dec_wp), '&3')
FROM (
SELECT MIN(WPTODEC(`wp_oc`, '&3')) AS dec_wp
FROM `caches`
WHERE WPTODEC(`wp_oc`, '&3')>WPTODEC('&1', '&3')
AND `wp_oc` REGEXP '&2'
UNION
SELECT MIN(WPTODEC(`wp_oc`, '&3')) AS dec_wp
FROM `cache_waypoint_pool`
WHERE WPTODEC(`wp_oc`, '&3')>WPTODEC('&1', '&3')
) AS tbl",
$opt['logic']['waypoint_pool']['prefix'] . '100000',
$start_wp,
'^' . $opt['logic']['waypoint_pool']['prefix'] . '[' . $opt['logic']['waypoint_pool']['valid_chars'] . ']{1,}$',
$opt['logic']['waypoint_pool']['prefix']);
// now, we have start and end waypoints ...
$nWaypointsGenerated = 0;
while (($nWaypointsGenerated < $max_inserts_count) && ($start_wp != $end_wp))
{
sql("INSERT INTO `cache_waypoint_pool` (`wp_oc`) VALUES ('&1')", $start_wp);
$nWaypointsGenerated++;
$start_wp = $this->increment_waypoint($start_wp, $opt['logic']['waypoint_pool']['prefix']);
}
return $nWaypointsGenerated;
}
// see mysql functions in doc/sql/stored-proc/maintain.php for explanation
function increment_waypoint($wp, $prefix)
{
global $opt;
$wp_chars = $opt['logic']['waypoint_pool']['valid_chars'];
$b36_chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
if (substr($wp, 0, 2) != $prefix)
return '';
$wp_value = substr($wp, 2);
$b36_value = '';
for ($i = 0; $i < strlen($wp_value); $i++)
$b36_value .= substr($b36_chars, strpos($wp_chars, substr($wp_value, $i, 1)), 1);
$dec_value = base_convert($b36_value, strlen($wp_chars), 10)+1;
$b36_value = strtoupper(base_convert($dec_value, 10, strlen($wp_chars)));
$wp_value = '';
for ($i = 0; $i < strlen($b36_value); $i++)
$wp_value .= substr($wp_chars, strpos($b36_chars, substr($b36_value, $i, 1)), 1);
if (strlen($wp_value) < 4)
return $prefix . substr('0000' . $wp_value, -4);
else
return $prefix . $wp_value;
}
}
?>