curl http://download.tarantool.org/tarantool/1.7/gpgkey | sudo apt-key add - release=`lsb_release -c -s` # install https download transport for APT sudo apt-get -y install apt-transport-https # append two lines to a list of source repositories sudo rm -f /etc/apt/sources.list.d/*tarantool*.list sudo tee /etc/apt/sources.list.d/tarantool_1_7.list <<- EOF deb http://download.tarantool.org/tarantool/1.7/debian/ $release main deb-src http://download.tarantool.org/tarantool/1.7/debian/ $release main EOF # install sudo apt-get update sudo apt-get -y install tarantool
ps xauf | grep taran root 2735 0.0 0.2 13972 2132 pts/0 S+ 22:05 0:00 \_ grep taran taranto+ 568 0.0 0.8 812304 8632 ? Ssl 17:03 0:03 tarantool example.lua <running>
tarantoolctl connect '3301' connected to localhost:3301 localhost:3301> print ('The good!') --- ... localhost:3301>
box.cfg { -- listen = 3311; -- slab_alloc_arena = 0.2; -- } local function bootstrap() local space = box.schema.create_space('example') space:create_index('primary') -- -- box.schema.user.grant('guest', 'read,write,execute', 'universe') -- box.schema.user.create('good', { password = 'secret' }) box.schema.user.grant('good', 'read,write,execute', 'universe') end -- box.once('example-2.0', bootstrap)
tarantoolctl connect "good:secret@0:3311" connected to 0:3311 0:3311>
local function bootstrap() box.schema.user.create('good', { password = 'secret' }) box.schema.user.grant('good', 'read,write,execute', 'universe') ----------------------------------- -- local stickers = box.schema.create_space('stickers') -- stickers:create_index('primary', { type = 'TREE', parts = {1, 'unsigned'} }) -- stickers:create_index('secondary', { type = 'TREE', unique = false, parts = {2, 'integer'} }) -- stickers:create_index('ternary', { type ='HASH', parts = {3, 'string', 4, 'string'} }) ----------------------------------- -- - local packs = box.schema.create_space('packs') -- - packs:create_index('primary', { type = 'HASH', parts = {1, 'string'} }) -- packs:create_index('secondary', { type = 'TREE', unique = false, parts = {2, 'integer'} }) ----------------------------------- -- local secret = box.schema.create_space('secret') -- secret:create_index('primary', { type = 'HASH', parts = {1, 'string'} }) -- secret:create_index('secondary', { type = 'TREE', unique = false, parts = {2, 'integer'} }) ----------------------------------- -- local sessions = box.schema.create_space('sessions') -- sessions:create_index('primary', { type = 'HASH', parts = {1, 'string'} }) -- sessions:create_index('secondary', { type = 'TREE', unique = false, parts = {2, 'integer'} }) ----------------------------------- -- local server = box.schema.create_space('server') -- server:create_index('primary', { type = 'TREE', parts = {1, 'unsigned'} }) -- server:insert{1, 0, 0, 0} end
box.space.stickers:drop()
box.space.stickers.index.ternary:drop()
box.space.stickers:truncate()
box.space.stickers:drop()
location / { try_files $uri $uri/ /index.php?q=$uri&$args; }
$_REQUEST['q']
array in the PHP script, and implement the routing of HTTP requests.
location ~* \.php$ { try_files $uri =404; fastcgi_pass unix:/var/run/php5-fpm.sock; fastcgi_index index.php; include fastcgi_params; expires -1; }
expires -1;
we disable the caching of requests, it is not needed for the voting pages and the output of the Top Charts. The rest of the locale cache data for 24 hours or 30 days from the upstream HTTP settings. Probably, everyone has their own collection of Nginx options.
sudo apt-get install php5-cli php5-dev php-pear pecl channel-discover tarantool.imtqy.com/tarantool-php/pecl pecl install Tarantool-PHP/Tarantool-beta
Build process completed successfully Installing '/usr/lib/php5/20131226/tarantool.so' install ok: channel://tarantool.imtqy.com/tarantool-php/pecl/Tarantool-0.0.13 configuration option "php_ini" is not set to php.ini location You should add "extension=tarantool.so" to php.ini
php -v PHP Warning: PHP Startup: Unable to load dynamic library '/usr/lib/php5/20131226/tarantool.so' - /usr/lib/php5/20131226/tarantool.so: undefined symbol: tarantool_schema_destroy in Unknown on line 0 PHP 5.6.29-0+deb8u1 (cli) (built: Dec 13 2016 16:02:08)
pecl uninstall Tarantool-PHP/Tarantool-beta cd ~ git clone https://github.com/tarantool/tarantool-php.git cd tarantool-php phpize ./configure make make install
<?php $tarantool = new Tarantool('localhost', 3311, 'good', 'secret'); try { $tarantool->ping(); } catch (Exception $e) { echo "Exception: ", $e->getMessage(), "\n"; } ?>
php config.php
from the command line and check how it worked. If configured incorrectly, we get an error message. Check out!
box.space.packs:upsert({'key1',0}, {{'=',2,0}})
$tarantool->upsert('packs', array ($pack,0), array ( array( "field" => 1, "op" => "=", "arg" => 0 ) ));
"field" => 1
from the PHP array corresponds to the {'=',2,0}
entry in Lua. Wherever the arrays start from zero, the current connectors work the same way. This behavior has been changed since version 1.6. Reading examples on the Internet, pay attention to the version of the Tarantula! This article is written according to version 1.7, and about version 1.5, developers are asked not to remember at all.
box.space.stickers:auto_increment({0,'pack2','sticker2'})
$tarantool->call('box.space.stickers:auto_increment', array( array(0,$pack, $i . '.png', $url, 0, 0) ));
# Get routes from request $route = isset($_REQUEST['q']) ? $_REQUEST['q'] : '/'; $vote_plus = isset($_REQUEST['vote_plus']) ? $_REQUEST['vote_plus'] : ''; $vote_minus = isset($_REQUEST['vote_minus']) ? $_REQUEST['vote_minus'] : ''; switch ($route) { case '/good': action_good(); break; case '/bad': action_bad(); break; case '/ugly': action_ugly(); break; case '/about': action_about(); break; default: if (!empty($vote_plus) && !empty($vote_minus)) { sleep (1); do_post($vote_plus, $vote_minus); } action_main(); }
$r = $tarantool->evaluate( "digest = require('digest') return (digest.urandom(4))" ); $seed = unpack('L', $r[0])[1]; srand($seed);
function create_random_vote() { # Get random sticker id global $tarantool; $tuple = $tarantool->call("box.space.stickers.index.primary:random", array(rand())); $id = $tuple[0][0]; $url = $tuple[0][4]; # Create random sticker token $token = $tarantool->evaluate( "digest = require('digest') return ( digest.md5_hex(digest.urandom(32)))" )[0]; $time = time(); # Set secure token to protect post action ################################################################## # # box.space.secret:insert({key', 0, 456, 'bla-bla'}) # ################################################################## $tarantool->insert('secret', array ($token, $time, $id, $url)); return array ( $url, $token ); }
function update_votes($id, $plus, $minus) { global $tarantool; ########################################################### # # box.space.stickers:update(1, {{'+', 6, 1}, {'+', 7, -1}}) # ########################################################### $tarantool->update("stickers", $id, array ( array( "field" => 5, "op" => "+", "arg" => $plus ), array( "field" => 6, "op" => "+", "arg" => $minus ) ) ); }
op" => "="
parameter, which means that the field is replaced in an existing tuple. There is also a parameter +, - and some other operations. They perform a very important task. Usually, to replace a value in the database, we first read some field, then change it. To maintain data consistency, you have to block access to the table and use transactions. In Tarantula, due to its architecture, the update and upsert commands work atomically inside the server process without locking the database. It allows you to build damn fast systems!
<?php # Init database $tarantool = new Tarantool('localhost', 3301, 'good', 'bad'); try { $tarantool->ping(); } catch (Exception $e) { echo "Exception: ", $e->getMessage(), "\n"; } const MIN_VOTES = 20; // Number of votes to show the ugly const UPDATE_PLUS = 1; // Increment for positive update const UPDATE_MINUS = -1; // Increment for negative update const NO_UPDATE = 0; const COOKIE = 'uuid'; // Cookie name const HIDDEN = '/img/Question.svg';// Picture for hidden element # Get routes from request $route = isset($_REQUEST['q']) ? $_REQUEST['q'] : '/'; $vote_plus = isset($_REQUEST['vote_plus']) ? $_REQUEST['vote_plus'] : ''; $vote_minus = isset($_REQUEST['vote_minus']) ? $_REQUEST['vote_minus'] : ''; # Get cookie from request or create new value $cookie = isset($_COOKIE[COOKIE]) ? $_COOKIE[COOKIE] : update_user(''); switch ($route) { case '/good': action_good(); break; case '/bad': action_bad(); break; case '/ugly': action_ugly($cookie); break; case '/about': action_about(); break; default: # This is post request: if (!empty($vote_plus) && !empty($vote_minus)) { sleep (1); $cookie = update_user($cookie); do_post($vote_plus, $vote_minus); } setcookie(COOKIE, $cookie, time() + (86400 * 30), "/"); action_main(); } exit(); function action_main() { global $tarantool; # Get crypto safe random seed from Tarantool LUA module # https://tarantool.org/doc/reference/reference_lua/digest.html $r = $tarantool->evaluate( "digest = require('digest') return (digest.urandom(4))" ); $seed = unpack('L', $r[0])[1]; srand($seed); list ($left_url, $left_token_plus) = create_random_vote(); list ($right_url, $right_token_plus) = create_random_vote(); $left_token_minus = $right_token_plus; $right_token_minus = $left_token_plus; update_stats(UPDATE_PLUS, NO_UPDATE); $title = ' Telegram'; include_once('main.html'); } function action_good() { $title = ' Telegram'; $top = get_top(10,Tarantool::ITERATOR_LE); $active_good ='class="active"'; $active_bad =''; include_once('top.html'); } function action_bad () { $title = ' Telegram'; $active_bad ='class="active"'; $active_good =''; $top = get_top(10,Tarantool::ITERATOR_GE); # Hide the ugly $top[0][4] = HIDDEN; include_once('top.html'); } function action_ugly($user) { $title = ' Telegram'; $top = get_top(1,Tarantool::ITERATOR_GE); $votes = get_session($user); # Hide the ugly until getting enough votes if ($votes < MIN_VOTES) { $ugly_message = " " . MIN_VOTES . " <br>"; $ugly_message .= " " . (MIN_VOTES - $votes) . " "; $ugly_img = HIDDEN; } else { $ugly_img = $top[0][4]; } include_once('ugly.html'); } function action_about() { $title = ' ?'; list($stickers, $shows, $votes, $visitors) = get_server_stats(); include_once('about.html'); } function do_post($vote_plus, $vote_minus) { global $tarantool; $tuple_plus = $tarantool->select("secret", $vote_plus); $tuple_minus = $tarantool->select("secret", $vote_minus); $id_plus = $tuple_plus[0][2]; $id_minus = $tuple_minus[0][2]; # Clean up used tokens if (!empty($vote_plus) && !empty($vote_minus)) { $tarantool->delete("secret", $vote_plus); $tarantool->delete("secret", $vote_minus); } # Get actual tuple data if (!empty($id_plus) && !empty($id_minus)) { $raiting = +1; update_rating($id_plus, $raiting); $raiting = -1; update_rating($id_minus, $raiting); update_votes($id_plus, UPDATE_PLUS, NO_UPDATE); update_votes($id_minus, NO_UPDATE, UPDATE_MINUS); update_stats(NO_UPDATE, UPDATE_PLUS); } } function create_random_vote() { # Get random sticker id global $tarantool; $tuple = $tarantool->call("box.space.stickers.index.primary:random", array(rand())); $id = $tuple[0][0]; $url = $tuple[0][4]; # Create random sticker token $token = $tarantool->evaluate( "digest = require('digest') return ( digest.md5_hex(digest.urandom(32)))" )[0]; $time = time(); # Set secure token to protect post action ################################################################## # # box.space.secret:insert({key', 0, 456, 'bla-bla'}) # ################################################################## $tarantool->insert('secret', array ($token, $time, $id, $url)); return array ( $url, $token ); } function update_rating($id, $update) { global $tarantool; ################################################# # # box.space.stickers:update(7856, {{'+', 2, 10}}) # ################################################# $tarantool->update("stickers", $id, array ( array( "field" => 1, "op" => "+", "arg" => $update ) )); } function update_votes($id, $plus, $minus) { global $tarantool; ########################################################### # # box.space.stickers:update(1, {{'+', 6, 1}, {'+', 7, -1}}) # ########################################################### $tarantool->update("stickers", $id, array ( array( "field" => 5, "op" => "+", "arg" => $plus ), array( "field" => 6, "op" => "+", "arg" => $minus ) ) ); } function update_user($cookie) { global $tarantool; # Create uuid if first time user if (empty($cookie)) { ################################## # # uuid = require('uuid') # uuid() # ################################## $uuid = $tarantool->evaluate( "uuid = require('uuid') return (uuid.str())" )[0]; } else { $uuid = $cookie; } $time = time(); $ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : ''; $agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : ''; # Create session or update user stat inside ########################################################### # # box.space.sessions:upsert({'111222333', 123456, 0, 'ip', 'agent'}, # {{'=', 2, 1}, {'+', 3, 1}, {'=', 4, 'ip'}, {'=', 5, 'agent'}}) # ########################################################### # Please check https://github.com/tarantool/tarantool-php/issues/111 $tarantool->upsert("sessions", array($uuid, $time, 0, $ip, $agent), array ( array( "field" => 1 "op" => "=", "arg" => $time ), array( "field" => 2 "op" => "+", "arg" => 1 ), array( "field" => 3 "op" => "=", "arg" => $ip ), array( "field" => 4, "op" => "=", "arg" => $agent ) ) ); return($uuid); } function update_stats($vote, $click) { global $tarantool; ######################################################## # # box.space.server:update(1, {{'+', 3, 1}, {'+', 4, 1}}) # ######################################################## $tarantool->update("server",1, array ( array( "field" => 2, "op" => "+", "arg" => $vote ), array( "field" => 3, "op" => "+", "arg" => $click ) ) ); } function get_session($sid) { global $tarantool; ########################################## # # box.space.sessions:select('id') # ######################################### if (strlen($sid) > 16) { return $tarantool->select("sessions", $sid)[0][2]; } else { return 0; } } function get_top($limit, $iterator) { global $tarantool; ###################################################################################### # # box.space.stickers.index.secondary:select({primary}, {iterator = box.index.GE, offset=0, limit=10}) # ###################################################################################### $result = $tarantool->select("stickers", null, 'secondary', $limit, 0, $iterator); return $result; } function get_server_stats() { global $tarantool; $time = time() - 30*86400; // one month before $stickers = $tarantool->call('box.space.stickers:count')[0][0]; $tuple = $tarantool->select('server',1); $shows = $tuple[0][2]; $votes = $tuple[0][3]; $visitors = $tarantool->call('box.space.sessions.index.secondary:count', array($time, array('iterator' => Tarantool::ITERATOR_GE)) )[0][0]; # $shows, $votes, $visitors) = get_server_stats(); return array($stickers, $shows, $votes, $visitors); } ?>
<!-- --> <div class="voting container"> <div class="voting-zone"> <!-- --> <div class="sticker" onclick="myFunction()"> <form name="voteFormLeft" id="idForm" method ="POST" action="/" > <input class= "pic1" id="left_url" type="image" src="<?php echo $left_url?>" alt="Vote left" > <input type="hidden" name="vote_plus" value="<?php echo $left_token_plus?>"> <input type="hidden" name="vote_minus" value="<?php echo $left_token_minus?>"> </form> </div> <!-- --> <div class="sticker" onclick="myFunction()"> <form name="voteFormRight" id="idForm" method ="POST" action="/"> <input class= "pic2" id="right_url" type="image" src="<?php echo $right_url?>" alt="Vote right" > <input type="hidden" name="vote_plus" value="<?php echo $right_token_plus?>" > <input type="hidden" name="vote_minus" value="<?php echo $right_token_minus?>" > </form> </div> </div> </div> <!-- -->
Source: https://habr.com/ru/post/321252/