The preparation to use the library is very simple.
<?php
require_once 'Cascade.php';
(*) You should investigate include_path if you have problems such as no existence of necessary files.
sp1rytus@cadam-01:~/cascade/manual$ php -i | grep include_path
include_path => .:/home/gree-common/src:/usr/share/php:.....
You should ensure that Cascade Library is located in the PATH that can be dynamically resolved.
Required definitions for access to the data
Environment MySQL Database name cascade_test Table name item
Add to /home/gree/etc/mysql.ini.php
1 2 3 4 5 6 7 8 9 10 | return $db_config_list = array(
...
'cascade_test' => array(
'master' => '192.168.1.10',
'slave' => '192.168.1.11',
'standby' => '192.168.1.11',
'db' => 'cascade_test',
),
...
);
|
Procedure of sample data creation
$ mysql -uroot -p -h192.168.1.10
mysql> CREATE DATABSE cascade_test;
mysql> USE cascade_test;
mysql> CREATE TABLE IF NOT EXISTS item (
id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
user_id INT UNSIGNED NOT NULL,
item_id SMALLINT UNSIGNED NOT NULL,
num INT NOT NULL DEFAULT 0,
mtime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
ctime DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
UNIQUE KEY ukey_01(user_id, item_id),
KEY key_01(user_id, num)
) ENGINE=InnoDB;
mysql> TRUNCATE TABLE item;
mysql> INSERT INTO item VALUE(default, 100, 1, 1, now(), now());
mysql> INSERT INTO item VALUE(default, 100, 2, 2, now(), now());
mysql> INSERT INTO item VALUE(default, 100, 3, 3, now(), now());
mysql> INSERT INTO item VALUE(default, 100, 4, 5, now(), now());
mysql> INSERT INTO item VALUE(default, 101, 1, 3, now(), now());
mysql> INSERT INTO item VALUE(default, 102, 2, 1, now(), now());
mysql> INSERT INTO item VALUE(default, 103, 3, 1, now(), now());
mysql> INSERT INTO item VALUE(default, 104, 4, 1, now(), now());
mysql> INSERT INTO item VALUE(default, 105, 1, 3, now(), now());
mysql> INSERT INTO item VALUE(default, 105, 5, 4, now(), now());
Sample data
id user_id item_id num mtime ctime 1 100 1 1 2011-02-15 11:27:30 2011-02-15 11:27:30 2 100 2 2 2011-02-15 11:27:30 2011-02-15 11:27:30 3 100 3 4 2011-02-15 11:27:31 2011-02-15 11:27:30 4 100 4 5 2011-02-15 11:27:30 2011-02-15 11:27:30 5 101 1 3 2011-02-15 11:27:30 2011-02-15 11:27:30 6 102 2 1 2011-02-15 11:27:30 2011-02-15 11:27:30 7 103 3 1 2011-02-15 11:27:30 2011-02-15 11:27:30 8 104 4 1 2011-02-15 11:27:30 2011-02-15 11:27:30 9 105 1 3 2011-02-15 11:27:30 2011-02-15 11:27:30 10 105 5 4 2011-02-15 11:27:30 2011-02-15 11:27:30 12 200 5 3 2011-02-15 11:27:31 2011-02-15 11:27:31
Example of acquiring accessor
$ac = Cascade::getAccessor(${schema name});
${schema name} | ${namespace}${separator}${identifier} |
${separator} | default separator is ‘#’ |
${namespace} | Value in specifies service (ex. sample) |
${identifier} | Value in data structure class name (ex. item) |
Example of the schema definition
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 'xxx : default' => array(
'dataformat.prefix' => 'Service_XXX_Cascade_DataFormat',
'gateway.prefix' => 'Service_XXX_Cascade_Gateway',
),
At this setting
Cascade::getAccessor('xxx#user_item')
=> Service_XXX_Cascade_DataFormat_User_Item
=> Service_XXX_Cascade_Gateway_User_Item
Class name is resolved like this.
('_' is recognized as a directory separator. )
|
/home/gree/etc/cascade.ini.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | return $cascade_config = array(
CASCADE_CONFIG_INDEX_SCHEMA => array(
...
'sample : default' => array(
'dataformat.prefix' => 'Gree_Service_Sample_Cascade_DataFormat',
'dataformat.suffix' => null,
'gateway.prefix' => 'Gree_Service_Sample_Cascade_Gateway',
'gateway.suffix' => null
'load.path' => '/home/gree/service/sample',
'load.ignore_prefix' => 'Gree_Service_Sample',
'load.file_ext' => '.php',
),
...
),
);
The meaning that 'sample : default' is defined as sample that inherited from the defined value of the default schema.
|
/home/gree/service/sample/class/Cascade/DataFormat/Item.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | class Gree_Service_Sample_Cascade_DataFormat_Item extends Cascade_DB_SQL_DataFormat
{
// Table name
protected $table_name = 'item';
// PRIMARY-KEY (multi-column-index are defined by array)
protected $primary_key = 'id';
// Data fetch KEY (primary_key is used at NULL)
protected $fetch_key = NULL;
// AUTO_INCREMENT flag
protected $auto_increment = TRUE;
// Field name (modified time)
protected $updated_at_column = 'mtime';
// Field name (create time)
protected $created_at_column = 'ctime';
// Master DSN
protected $master_dsn = 'gree://master/cascade_test';
// Slave DSN
protected $slave_dsn = 'gree://slave/cascade_test';
// Field name list
protected $field_names = array(
'id', // ID
'user_id', // User ID
'item_id', // Item ID
'num', // having #
'mtime', // modified time
'ctime', // create time
);
// Definition of query
protected $queries = array(
'find_by_user' => array(
'sql' => 'SELECT * FROM __TABLE_NAME__ WHERE user_id = :user_id',
),
);
};
|
Data access is executed as follows
Example of access
1 2 3 4 5 6 7 8 9 10 | $ac = Cascade::getAccessor('sample#item')
// Get data by PRIMARY-ID
$item = $ac->get($id = 1);
// Multi get by PRIMARY-ID list
$item_hash = $ac->mget($idl = array(1, 2, 3));
// Execute query
$item_hash = $ac->find('find_by_user', $param = array('user_id' => 100));
|
example)
Definition of MySQL table
CREATE TABLE `item` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(10) unsigned NOT NULL,
`item_id` int(10) unsigned NOT NULL,
`num` int(10) unsigned NOT NULL DEFAULT '0',
`mtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`ctime` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`)
) ENGINE=InnoDB
Definition of DataFormat
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | class Service_Cascade_DataFormat_Item extends Cascade_DB_SQL_DataFormat
{
// Table name
protected $table_name = 'item';
// PRIMARY-KEY (multi-column-index are defined by array)
protected $primary_key = 'id';
// Data fetch KEY (primary_key is used at NULL)
protected $fetch_key = NULL;
// AUTO_INCREMENT flag
protected $auto_increment = true;
// Field name (modified time)
protected $updated_at_column = 'mtime';
// Field name (create time)
protected $created_at_column = 'ctime';
// Master DSN
protected $master_dsn = 'gree://master/item';
// Slave DSN
protected $slave_dsn = 'gree://slave/item';
// Field name list
protected $field_names = array(
'id', // ID
'user_id', // User ID
'item_id', // Item ID
'num', // Having number
'mtime', // modified time
'ctime', // create time
);
// Query definition
protected $queries = array(
'find_by_user' => array(
'sql' => 'SELECT * FROM __TABLE_NAME__ WHERE user_id = :user_id',
),
'update_num' => array(
'sql' => 'UPDATE __TABLE_NAME__ SET num=:num WHERE user_id = :user_id',
),
);
};
|
Access example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | $cascade = Cascade::getAccessor('service#item');
/***** -- get() : select data by primary_key (used fetch_key if defined) *****/
// Get data from defined table (id='1')
$result = $cascade->get('1');
/***** -- find() : Defined SQL is called. uses for SELECT. *****/
// Find data by SQL defined 'find_by_user', condition is user_id=100
$result = $casacde->find('find_by_user', array('user_id'=>100));
// Find data by SQL defined 'find_by_user', condition is user_id=100, offset is 5, get number is 10
$result = $casacde->find('find_by_user', array('user_id'=>100), 5, 10);
/***** -- execute() : Defined SQL is called. uses for INSERT/UPDATE/DELETE. *****/
// Update date by SQL defined 'update_num', user_id is 100, num is 10
// execute return accected rows.
$result = $casacde->execute('update_num', array('user_id'=>100, 'num' => 10));
|
Sharding is used for database division. Explain the sharding function.
example)
character_id is the key of dividing tables. In the following example, the table is divided into 128 parts and dsn is divided into 8 parts.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | class Application_Cascade_ShardSelector extends Cascade_DB_SQL_ShardSelector
{
/**
* devide key
* @var string
*/
protected $index_criteria_params = 'character_id';
/**
* number of dsn division
* @var int
*/
protected $division_count_dsn = 8;
/**
* number of table division
* @var int
*/
protected $division_count_table = 128;
/**
* format of dsn suffix
* @var string
*/
protected $format_suffix_dsn = '_%d';
/**
* format of table suffix
* @var string
*/
protected $format_suffix_table = '_%d';
};
|
DataFormat is overriding getShardSelector() and returning the object of ShardSelector instance.
class Application_Cascade_DataFormat_Character extends Cascade_DB_SQL_DataFormat {
protected $table_name = ‘character’; protected $primary_key = ‘character_id’; protected $fetch_key = null; protected $auto_increment = true; protected $updated_at_column = ‘mtime’; protected $created_at_column = ‘ctime’; protected $master_dsn = ‘gree://master/cascade_test’; protected $slave_dsn = ‘gree://slave/cascade_test’; protected $field_names = array(
‘character_id’, // Character ID ‘param’, // Paramators ‘item_id’, // Item ID ‘num’, // having # ‘mtime’, // modified time ‘ctime’, // create time);
- {
- return new Application_Cascade_ShardSelector;
}
};
results:
An original implementation is using Custom ShardSelector except hash division.
example)
Date (ex YYYYMMDD) is used in suffix for the table and dsn is selected in 1-4 at random.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | class Application_Cascade_Custom_ShardSelector extends Cascade_DB_SQL_ShardSelector
{
// {{{ getDSNSuffix
/**
* get DSN suffix string
*
* @param Cascade_DB_Criteria criteria
* @return string dsn suffix
*/
public /* string */
function getDSNSuffix(Cascade_DB_Criteria $criteria)
{
$suffix = '_' . mt_rand(1, 4);
return $suffix;
}
// }}}
// {{{ getTableNameSuffix
/**
* get table name suffix string
*
* @param Cascade_DB_Criteria criteria
* @return string table suffix
*/
public /* string */
function getTableNameSuffix(Cascade_DB_Criteria $criteria)
{
$suffix = '_' . date('Ymd');
return $suffix;
}
// }}}
};
|
DataFormat is overriding getShardSelector() and returning the object of ShardSelector instance.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | class Application_Cascade_DataFormat_Character extends Cascade_DB_SQL_DataFormat
{
protected $table_name = 'character';
protected $primary_key = 'character_id';
protected $fetch_key = null;
protected $auto_increment = true;
protected $updated_at_column = 'mtime';
protected $created_at_column = 'ctime';
protected $master_dsn = 'gree://master/cascade_test';
protected $slave_dsn = 'gree://slave/cascade_test';
protected $field_names = array(
'character_id', // Character ID
'param', // Paramators
'item_id', // Item ID
'num', // having #
'mtime', // modified time
'ctime', // create time
);
public /* string */
function getShardSelector(/* void */)
{
return new Application_Cascade_ShardSelector;
}
};
|
results:
Cascade_DB_SQL_DataFormat::getMasterDSN
Cascade_DB_SQL_DataFormat::getSlaveDSN
example)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public function getMasterDSN(Cascade_DB_SQL_Criteria $criteria) { $params = $criteria->params; $hint = $criteria->hint; $id = 0; if (is_array($params) && (array_key_exists('id', $params))) { // Include 'id' on params $id = $criteria->params['id']; } else if (is_array($hint) && (array_key_exists('id', $hint))) { // Include 'id' on hint $id = $hint['id']; } else if (is_numeric($params)) { // Params is id $id = $params; } else { // Id doesn't exist throw new Service_Exception(__METHOD__." invalid criteria"); } $farm = $id % seld::DSN_DIVIDE_NUM; // DSN_DIVIDE_NUM = 4 return ($this->master_dsn . '_' . $farm); }
notice * ‘id’ is must included in params or hint. * Division on various conditions is possible by the change of the farm_id calculation method.
Method of specifying the table to access.
Cascade_DB_SQL_DataFormat::getTableName
example) How to divide into 128 using the value of ‘id’ specified as $params or $hint
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public function getTableName(Cascade_DB_SQL_Criteria $criteria) { $params = $criteria->params; $hint = $criteria->hint; $id = 0; if (is_array($params) && (array_key_exists('id', $params))) { // 'id' included $params $id = $criteria->params['id']; } else if (is_array($hint) && (array_key_exists('id', $hint))) { // 'id' included $hint $id = $hint['id']; } else if (is_numeric($params)) { // $params is used as 'id' $id = $params; } else { // 'id' not found : Updating is impossible. throw new Service_Exception(__METHOD__." invalid criteria"); } $farm = $id % seld::TABLE_DIVIDE_NUM; // TABLE_DIVIDE_NUM = 128 return ($this->table_name . '_' . $farm); }
method of changing the table to access by a day.
Cascade_DB_SQL_DataFormat::getTableName
example) table is divided by each date
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public function getTableName(Cascade_DB_SQL_Criteria $criteria) { $params = $criteria->params; $hint = $criteria->hint; $date = 0; if (is_array($params) && (array_key_exists('date', $params))) { // 'date' included $params $date = $params['date']; } else if (is_array($hint) && (array_key_exists('date', $hint))) { // 'date' included $hint $date = $hint['date']; } else { // 'date' not found : use default date $date = '20110101'; } $tablename = $this->table_name . '_' . $date; return $tablename; }
Present date can also be used when date does not exist in params and hint.
} else { $date = date('Ymd'); }
example) setting slave server for batch program
db-master 192.168.1.100
db-slave1 192.168.1.101
db-slave2 192.168.1.102
db-slave3 192.168.1.103
db-slave4 192.168.1.104
In these composition, db-slave4 is set up as only for batch access.
config
1 2 3 4 5 6 7 8 9 10 11 12 | 'test' => array(
'master' => '192.168.1.100',
'slave' => array(
'192.168.1.101',
'192.168.1.102',
'192.168.1.103',
),
'batch' => array(
'192.168.1.104',
),
'db' => 'test',
),
|
Ex method is used at the time of access.
$cascade->find('find_data', $params, $offset, $limit, $hint);
::::
$cascade->findEx('batch', 'find_data', $params, $offset, $limit, $hint);
In the example of memcached access explains how to access to the KVS.
example) A setup of memcached for caching the data of “character”
Definition of DataFormat
1 2 3 4 5 6 7 8 9 10 11 12 | class Service_Cascade_DataFormat_Character extends Cascade_DB_KVS_DataFormat
{
// ----[ Properties ]---------------------------------------------
// @var string DSN
protected $dsn = 'gree(memcache)://node/character';
// @var int connection driver
protected $driver_type = self::DRIVER_LIBMEMCACHED;
// @var string namespace
protected $namespace = 'service#character';
// @var boolean The data compressed flag
protected $compressed = true;
};
|
Access example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | $cascade = Cascade::getAccessor('service#character');
/***** --- data get by get() *****/
// get data : key = 1
list($data, $token) = $cascade->get('1');
// get data : key = 'test1'
list($data, $token) = $cascade->get('test1');
/***** --- store data by set() *****/
// set value 'character1' for key = 1
$cascade->set('1', 'character1');
|
example) The value of the item ‘data’ stored in KVS is modified and saved
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public function compareAndSwap($key, $data) { $cascade = Cascade::getAccessor('service#data'); $result = false; // The number of CAS_RETRY times repeats. for ($i = 0 ; $i < self::CAS_RETRY ; $i++) { list($value, $token) = $cascade->get($key); $value['data'] = $data; $ret = $cascade->cas($token, $key, $value); if ($ret !== false) { // success $result = true; break; } // If it fails, it will redo from get (new token is acquired). } return $result; }
example) The number of times of login is saved in KVS
Definition of DataFormat
1 2 3 4 5 6 7 8 9 10 11 12 | class Service_Cascade_DataFormat_Logincount extends Cascade_DB_KVS_DataFormat
{
// ----[ Properties ]---------------------------------------------
// @var string DSN
protected $dsn = 'gree(memcache)://node/logincount';
// @var int connection driver
protected $driver_type = self::DRIVER_LIBMEMCACHED;
// @var string namespace
protected $namespace = 'service#logincount';
// @var boolean The data compressed flag
protected $compressed = false;
};
|
Access example
$cascade = Cascade::getAccessor('service#logincount');
// The data of key=1 is added. The value after addition goes into $count.
$count = $cascade->increment('1');
example)
Definition of the config file (sample.ini)
[production]
webhost = www.example.com
database.adapter = pdo_mysql
database.params.host = db.example.com
database.params.username = dbuser
database.params.password = secret
database.params.dbname = dbname
Definition of DataFormat
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class Service_Cascade_DataFormat_Sample extends Cascade_DB_Config_DataFormat
{
// ------------------------ Attributes ---------------------------
// @var string directory of config file
protected $config_path = /path/to/config/directory;
// @var string name of config file
protected $config_file = 'sample.ini';
// @var int driver
protected $driver_type = self::DRIVER_INIFILE;
// @var int fetch mode of result
protected $fetch_mode = self::FETCH_MODE_ASSOC;
// ---------------------------------------------------------------
}
|
Access example
get ‘production’
$cascade = Cascade::getAccessor('service#sample');
$cascade->get('production');
result
1 2 3 4 5 6 7 8 9 10 11 12 | array(
'webhost' => 'www.example.com',
'database' => array(
'adapter' => 'pdo_mysql',
'params' => array(
'host' => 'db.example.com',
'username' => 'dbuser',
'password' => 'secret',
'dbname' => 'dbname',
),
),
);
|
get ‘database’ in ‘production’
$cascade = Cascade::getAccessor('service#sample');
$cascade->get('production', 'database');
result
1 2 3 4 5 6 7 8 9 | array(
'adapter' => 'pdo_mysql',
'params' => array(
'host' => 'db.example.com',
'username' => 'dbuser',
'password' => 'secret',
'dbname' => 'dbname',
),
);
|
get ‘database.params.host’ in ‘production’
$cascade = Cascade::getAccessor('service#sample');
$cascade->get('production', 'database.params.host');
result
'db.example.com'
example) Inheritance of a value
Definition of the config file (sample.ini)
[production]
webhost = www.example.com
database.adapter = pdo_mysql
database.params.host = db.example.com
database.params.username = dbuser
database.params.password = secret
database.params.dbname = dbname
[staging : production]
database.params.host = dev.example.com
database.params.username = devuser
database.params.password = devsecret
Access example
get ‘staging’
$cascade = Cascade::getAccessor('service#sample');
$cascade->get('staging');
result
1 2 3 4 5 6 7 8 9 10 11 12 | array(
'webhost' => 'www.example.com',
'database' => array(
'adapter' => 'pdo_mysql',
'params' => array(
'host' => 'dev.example.com',
'username' => 'devuser',
'password' => 'devsecret',
'dbname' => 'dbname',
),
),
);
|
get ‘database.params.host’ in staging
$cascade = Cascade::getAccessor('service#sample');
$cascade->get('staging', 'database.params.host');
result
'dev.example.com'
example)
Definition of the config file (sample.php)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | return array(
1 => array(
'id' => 10001,
'value' => 3,
'comment' => 'test 1',
),
2 => array(
'id' => 10002,
'value' => 100,
'comment' => 'test 2',
),
'test' => array(
'value' => 'test test',
),
);
|
Definition of DataFormat
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class Service_Cascade_DataFormat_Sample extends Cascade_DB_Config_DataFormat
{
// ------------------------ Attributes ---------------------------
// @var string directory of config file
protected $config_path = /path/to/config/directory;
// @var string name of config file
protected $config_file = 'sample.php';
// @var int driver = self::DRIVER_PHPARRAY
protected $driver_type = self::DRIVER_PHPARRAY;
// @var int fetch mode of result
protected $fetch_mode = self::FETCH_MODE_ASSOC;
// ---------------------------------------------------------------
}
|
Access example
get data key=1
$cascade = Cascade::getAccessor('service#sample');
$cascade->get(1);
result
1 2 3 4 5 | array(
'id' => 10001,
'value' => 3,
'comment' => 'test 1',
);
|
get data key=’test’
$cascade = Cascade::getAccessor('service#sample');
$cascade->get('test');
result
array(
'value' => 'test test',
);
example)
Definition of the config file (sample.csv)
id,kind,min,max,comment
1,1,10,100,test1
2,1,11,200,test2
3,2,10,200,test3
10,3,100,200,test test
Definition of DataFormat
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class Service_Cascade_DataFormat_Sample extends Cascade_DB_Config_DataFormat
{
// ------------------------ Attributes ---------------------------
// @var string directory of config file
protected $config_path = /path/to/config/directory;
// @var string name of config file
protected $config_file = 'sample.csv';
// @var int driver = self::DRIVER_CSVFILE
protected $driver_type = self::DRIVER_CSVFILE;
// @var int fetch mode of result
protected $fetch_mode = self::FETCH_MODE_ASSOC;
// ---------------------------------------------------------------
}
|
Access example
get data key=1
$cascade = Cascade::getAccessor('service#sample');
$cascade->get(1);
result
1 2 3 4 5 6 7 | array(
'id' => 1,
'kind' => 1,
'min' => 10,
'max' => 100,
'comment' => 'test1',
);
|
get data key=10
$cascade = Cascade::getAccessor('service#sample');
$cascade->get(10);
result
1 2 3 4 5 6 7 | array(
'id' => 10,
'kind' => 3,
'min' => 100,
'max' => 200,
'comment' => 'test test',
);
|
example) Text key
Definition of the config file (sample.csv)
id,kind,min,max,comment
level1,1,10,100,test1
level2,1,11,200,test2
level3,2,10,200,test3
level4,3,100,200,test test
Access example
get data key=’level1’
$cascade = Cascade::getAccessor('service#sample');
$cascade->get('level1');
result
1 2 3 4 5 6 7 | array(
'id' => 'level1',
'kind' => '1',
'min' => '10',
'max' => '100',
'comment' => 'test1',
);
|
get data key=’level4’
$cascade = Cascade::getAccessor(‘service#sample’); $cascade->get(‘level4’);
result
1 2 3 4 5 6 7 | array(
'id' => 'level4',
'kind' => '3',
'min' => '100',
'max' => '200',
'comment' => 'test test',
);
|
example) The item master data of MySQL are cached in APC of a local server
Definition of DataFormat
class Service_Cascade_DataFormat_Master_Item extends Cascade_DB_SQL_DataFormat
{
// ------------------------ Attributes ---------------------------
// @var string master DSN
protected $master_dsn = 'gree://master/master';
// @var string slave DSN
protected $slave_dsn = 'gree://slave/master';
// @var array extra DSN
protected $extra_dsn = array();
// @var mixed Primary key
protected $primary_key = 'id';
// @var mixed Data fetch key
protected $fetch_key = NULL;
// @var boolean AUTO_INCREMENT fkag
protected $auto_increment = false;
// @var string modify date
protected $updated_at_column = 'mtime';
// @var string create date
protected $created_at_column = 'ctime';
// @var string table name
protected $table_name = 'item_master';
// @var array field name
protected $field_names = array(
'id', // ItemID
'name', // Item name
'category', // category
'effect_id', // effect ID
'effect_value', // effect value
'state', // status
'mtime', // modified time
'ctime', // created time
);
// @var array query
protected $queries = array(
);
// ---------------------------------------------------------------
}
Definition of Gateway
class Service_Cascade_Gateway_Master_Item extends Cascade_Proxy_ReadLocalCacheGateway
{
}
Access example
$cascade->get(1); // If there is no data in APC, it acquires from mysql and caches in APC.
Notice
example) Read through cache
Definition of DataFormat(MySQL)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | class Service_Cascade_DataFormat_User extends Cascade_DB_SQL_DataFormat
{
// ------------------------ Attributes ---------------------------
// @var string master DSN
protected $master_dsn = 'gree://master/user';
// @var string slave DSN
protected $slave_dsn = 'gree://slave/user';
// @var array extra DSN
protected $extra_dsn = array();
// @var mixed Primary key
protected $primary_key = 'user_id';
// @var mixed Data fetch key
protected $fetch_key = NULL;
// @var boolean auto increment flag
protected $auto_increment = false;
// @var string modified time
protected $updated_at_column = 'mtime';
// @var string created time
protected $created_at_column = 'ctime';
// @var string table name
protected $table_name = 'user';
// @var array field name list
protected $field_names = array(
'user_id', // user ID
'name', // name
'age', // age
'state', // status
'profile', // profile
'mtime', // modified time
'ctime', // created time
);
// @var array definition of queries
protected $queries = array(
'update_age' => array(
'sql' => 'UPDATE __TABLE_NAME__ SET age=:age WHERE user_id=:user_id',
),
'find_by_status' => array(
'sql' => 'SELECT * FROM __TABLE_NAME__ WHERE state=:state',
),
);
}
|
Definition of DataFormat(memcached)
1 2 3 4 5 6 7 8 9 10 11 | class Service_Cascade_DataFormat_Cache_User extends Cascade_DB_KVS_DataFormat
{
// @var string DSN
protected $dsn = 'gree(memcache)://node/user';
// @var int driver
protected $driver_type = self::DRIVER_LIBMEMCACHED;
// @var string namespace
protected $namespace = 'service#user';
// @var boolean The data compressed flag
protected $compressed = true;
};
|
Definition of Gateway
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | class Service_Cascade_Gateway_User extends Cascade_Proxy_CustomGateway // Inheritance CustomGateway base class
{
protected $expiretime = 1800; // memcached expire time : default: 30min
/***
* get method
*/
public function get($key, $hint=null, $use_master=false)
{
// read from memcached
$cache_session = null;
$schema_name = $this->namespace . '#cache_' . $this->identifier; // schema_name : {namespace}#cache_user
try {
// access to memcached
$cache_session = Cascade::getAccessor($schema_name); // get memcached accessor
if ($cache_session !== null) {
list($value, $token) = $cache_session->get($key); // data gett
if ($value !== false) {
// hit cache
return $value;
}
}
} catch (Exception $e) {
// Ignore exception when access memcached
trigger_error(__METHOD__." : catch exception : " . $e->getMessage());
}
// Data read from MySQL
// this->session is accessor to mysqld (becouse {namespace}#user is mysqld DataFoemat)
$value = $this->session->get($key, $hint, $use_master); // Get data
// Data store to memcached
if ($cache_session !== null) {
try {
$cache_session->set($key, $value, $this->expiretime); // Store data
} catch (Exception $e) {
// Ignore exception when access memcached
trigger_error(__METHOD__." : catch exception : " . $e->getMessage());
}
}
return $value;
}
/***
* get method (clear cache data)
*/
public function execute($stmt_name, $params=null, $hint=null)
{
// find key
$df = Cascade::getDataFormat($this->schema_name); // get dataformat class
$keyname = $df->getCardinalKey(); // get key (primary key if defined)
$key = false;
if (is_string($keyname)) {
// key is enabled
$key = $this->_searchKey($keyname, $params, $hint);
}
if ($key) {
// clear cache data
$cache_session = null;
$schema_name = $this->namespace . '#cache_' . $this->identifier; // memcached schema_name : {namespace}#cache_user
try {
$cache_session = Cascade::getAccessor($schema_name); // get accessor for memcached
if ($cache_session !== null) {
$cache_session->delete($key); // delete data
}
} catch (Exception $e) {
// ignore errorx
trigger_error(__METHOD__." : catch exception : " . $e->getMessage());
}
}
// execute request to mysqld (this->session is accessor for mysqld)
return $this->session->execute($stmt_name, $params, $hint);
}
/***
* Find key from params or hint
*/
protected function _searchKey($keyname, $params=null, $hint=null)
{
$key = false;
// Find key from params or hint
if (is_array($params) && (array_key_exists($keyname, $params))) {
// Include key in params
$key = $params[$keyname];
} else if (is_array($hint) && (array_key_exists($keyname, $hint))) {
// Include key in hint
$key = $hint[$keyname];
}
return $key;
}
}
|
1 2 3 4 5 6 7 8 9 // {{{ callSessionBefore /** * @param string Method name * @param array Args array for method * @return mixed result value or null */ public /** mixed */ function callSessionBefore(/* string */ $method, /* array */ $args)
The trigger is performed after a method call is set up.
$result cannot be rewritten.
1 2 3 4 5 6 7 8 9 10 | // {{{ callSessionAfter
/**
* @param string Method name
* @param array Args array for method
* @param mixed Result value
*/
public /* void */
function callSessionAfter(/* string */ $method,
/* array */ $args,
/* mixed */ $result)
|
example) Read through cache
DataFormat(MySQL)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | class Service_Cascade_DataFormat_User extends Cascade_DB_SQL_DataFormat
{
// ------------------------ Attributes ---------------------------
// @var string Master DSN
protected $master_dsn = 'gree://master/user';
// @var string Slave DSN
protected $slave_dsn = 'gree://slave/user';
// @var array Extra DSN list
protected $extra_dsn = array();
// @var mixed Primary key
protected $primary_key = 'user_id';
// @var mixed Data fetch key
protected $fetch_key = NULL;
// @var boolean AUTO_INCREMENT flag
protected $auto_increment = false;
// @var string Last update time colomn name
protected $updated_at_column = 'mtime';
// @var string Create time colomn name
protected $created_at_column = 'ctime';
// @var string Table name
protected $table_name = 'user';
// @var array Field name list
protected $field_names = array(
'user_id', // User ID
'name', // Name
'age', // Age
'state', // Status
'profile', // Profile
'mtime', // last update time
'ctime', // create time
);
// @var array query definitions
protected $queries = array(
'update_age' => array(
'sql' => 'UPDATE __TABLE_NAME__ SET age=:age WHERE user_id=:user_id',
),
'find_by_status' => array(
'sql' => 'SELECT * FROM __TABLE_NAME__ WHERE state=:state',
),
);
// ---------------------------------------------------------------
}
|
DataFormat(memcached)
1 2 3 4 5 6 7 8 9 10 11 | class Service_Cascade_DataFormat_Cache_User extends Cascade_DB_KVS_DataFormat
{
// @var string DSN
protected $dsn = 'gree(memcache)://node/user';
// @var int Driver type
protected $driver_type = self::DRIVER_LIBMEMCACHED;
// @var string namespace
protected $namespace = 'service#user';
// @var boolean The data compressed flag
protected $compressed = true;
};
|
Gateway
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | class Service_Cascade_Gateway_User extends Cascade_Proxy_CustomGateway
{
protected $expiretime = 1800; // memcached expire time : default: 30min
// {{{ callSessionBefore
/**
* Trigger definition before facade call
*
* @param string method name
* @param array method argment values
* @return mixed result value or null
*/
public /** mixed */
function callSessionBefore(/* string */ $method,
/* array */ $args)
{
try {
switch ($method) {
case 'get':
// Get value from cache
$cache_session = $this->_getCacheSession();
if ($cache_session !== null) {
list($value, $token) = $cache_session->get($args[1]/* key */);
if ($value !== false) {
// hit cache
return $value;
}
}
break;
case 'execute':
// Clear cache data
$df = Cascade::getDataFormat($this->schema_name);
$keyname = $df->getCardinalKey(); // find key name
$key = false;
if (is_string($keyname)) {
// Find cache key
$key = $this->_searchKey($keyname, $params, $hint);
}
if ($key) {
$cache_session = $this->getCacheSession();
if ($cache_session !== null) {
$cache_session->delete($key);
}
} else {
trigger_error(__METHOD__." : undefined key");
}
break;
default:
break;
}
} catch (Exception $e) {
// Ignore Exception
trigger_error(__METHOD__." : catch exception : " . $e->getMessage());
}
return null;
}
// }}}
// {{{ callSessionAfter
/**
* Trigger definition after facade call
*
* @param string method name
* @param array method argment values
* @param mixed result value
*/
public /* void */
function callSessionAfter(/* string */ $method,
/* array */ $args,
/* mixed */ $result)
{
try {
switch ($method) {
case 'get':
// Set result to cache
$cache_session = $this->_getCacheSession();
if ( ($cache_session !== null) && ($result) ) {
$cache_session->set($args[1]/* key */, $result, $this->expiretime);
}
break;
default:
break;
}
} catch (Exception $e) {
// Ignore Exception
trigger_error(__METHOD__." : catch exception : " . $e->getMessage());
}
return;
}
// }}}
// Get cache session
private function _getCacheSession()
{
$schema_name = $this->namespace . '#cache_' . $this->identifier;
$cache_session = null;
$cache_session = Cascade::getAccessor($schema_name);
return $cache_session;
}
// Search key from paramator and hint
private function _searchKey($keyname, $params=null, $hint=null)
{
$key = false;
if (is_array($params) && (array_key_exists($keyname, $params))) {
$key = $params[$keyname];
} else if (is_array($hint) && (array_key_exists($keyname, $hint))) {
$key = $hint[$keyname];
}
return $key;
}
}
|
Processed in order of the following.
example) Method which increases a friend : Insert link information and update the number of friends.
DataFormat(user infomation)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | class Service_Cascade_DataFormat_User extends Cascade_DB_SQL_DataFormat
{
// ------------------------ Attributes ---------------------------
// @var string Master DSN
protected $master_dsn = 'gree://master/user';
// @var string Slave DSN
protected $slave_dsn = 'gree://slave/user';
// @var array xtra DSN list
protected $extra_dsn = array();
// @var mixed Primary key
protected $primary_key = 'user_id';
// @var mixed Data fetch key
protected $fetch_key = NULL;
// @var boolean AUTO_INCREMENT flag
protected $auto_increment = false;
// @var string Last update time colomn name
protected $updated_at_column = 'mtime';
// @var string Create time colomn name
protected $created_at_column = 'ctime';
// @var string Table name
protected $table_name = 'user';
// @var array Field name list
protected $field_names = array(
'user_id', // User ID
'name', // Name
'link_num', // Number of friends
'state', // Status
'profile', // Profile
'mtime', // Last update time
'ctime', // Created time
);
// @var array query definitions
protected $queries = array(
'increment_link_num' => array(
'sql' => 'UPDATE __TABLE_NAME__ SET link_num=link_num+1 WHERE user_id=:user_id',
),
);
// ---------------------------------------------------------------
}
|
DataFormat(user link infomation)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | class Service_Cascade_DataFormat_Friend extends Cascade_DB_SQL_DataFormat
{
// ------------------------ Attributes ---------------------------
// @var string Master DSN
protected $master_dsn = 'gree://master/friend';
// @var string Slave DSN
protected $slave_dsn = 'gree://slave/friend';
// @var array Extra DSN list
protected $extra_dsn = array();
// @var mixed Primary key
protected $primary_key = array('user_id', 'to_user_id');
// @var mixed Data fetch key
protected $fetch_key = NULL;
// @var boolean AUTO_INCREMENT flag
protected $auto_increment = false;
// @var string Last update time colomn name
protected $updated_at_column = 'mtime';
// @var string Create time colomn name
protected $created_at_column = 'ctime';
// @var string Table name
protected $table_name = 'friend';
// @var array Field name list
protected $field_names = array(
'user_id', // User ID
'to_user_id', // Friend user ID
'mtime', // Last update time
'ctime', // Created time
);
// @var array query definitions
protected $queries = array(
'create' => array(
'sql' => 'INSERT INTO __TABLE_NAME__ (user_id, to_user_id, ctime) VALUES (:user_id, :to_user_id, NOW()), (:to_user_id, :user_id, NOW())',
),
);
// ---------------------------------------------------------------
}
|
Gateway
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | class Service_Cascade_Gateway_Friend extends Cascade_Proxy_CustomGateway
{
public function addFriend($user_id, $to_user_id)
{
// CAUTION: It omits, although there must originally be a prior application or there must be a number check of the maximum friends.
// Get user session
$schema_name = $this->namespace . '#user';
$user_session = Cascade::getAccessor($schema_name);
if ($user_session == NULL) {
throw new Service_Exception(__METHOD__ . " invalid schema : " . $schema_name);
}
// Count up number of friend
$result = $user_session->execute('increment_link_num', array('user_id' => $user_id));
if ($result == 0) {
throw new Service_Exception(__METHOD__ . " increment_link_num failed : " . $user_id));
}
$result = $user_session->execute('increment_link_num', array('user_id' => $to_user_id));
if ($result == 0) {
// CATION: Error-handling abbreviation
throw new Service_Exception(__METHOD__ . " increment_link_num failed : " . $to_user_id));
}
// Insert friend link data
$params = array(
'user_id' => $user_id,
'to_user_id' => $to_user_id,
);
$result = $this->session->execute('create', $params);
if ($result == 0) {
// CATION: Error-handling abbreviation
throw new Service_Exception(__METHOD__ . " create failed : " . $user_id . ", " . $to_user_id));
}
}
}
|
usage
1 2 3 4 5 6 7 8 9 10 11 12 | try {
$cascade = Cascade::getAccessor('service#friend');
// Add friend (Original method)
$cascade->addFriend($user_id, $to_user_id);
// Get friend data
$params = array('user_id'=>$user_id, 'to_user_id'=>$to_user_id);
$friend = $cascade->get($params);
} catch (Exception $e) {
throw $e;
}
|