MR::Ta...l::Box(3pm)
β¨ NAME β¨
so like, welcome to the docs for MR::Tarantool::Box(3pm) bestie π β it's a driver for an efficient Tarantool/Box NoSQL in-memory storage. that's it, that's the whole vibe.
πΈ SYNOPSIS πΈ
my $box = MR::Tarantool::Box->new({
servers => "127.0.0.1:33013",
name => "My Box", # mostly used for debug purposes
spaces => [ {
indexes => [ {
index_name => 'idx1',
keys => [0],
}, {
index_name => 'idx2',
keys => [1,2],
}, ],
space => 1, # space id, as set in Tarantool/Box config
name => "primary", # self-descriptive space-id
format => "QqLlSsCc&$", # pack()-compatible, Qq must be supported by perl itself,
# & stands for byte-string, $ stands for utf8 string.
default_index => 'idx1',
fields => [qw/ id f2 field3 f4 f5 f6 f7 f8 misc_string /], # turn each tuple into hash, field names according to format
}, {
#...
} ],
default_space => "primary",
timeout => 1.0, # seconds
retry => 3,
debug => 9, # output to STDERR some debugging info
raise => 0, # don't raise an exception in case of error
});
my $bool = $box->Insert(1, 2,3, 4,5,6,7,8,"asdf") or die $box->ErrorStr;
my $bool = $box->Insert(2, 2,4, 4,5,6,7,8,"asdf",{space => "primary"}) or die $box->ErrorStr;
my $tuple = $box->Insert(3, 3,3, 4,5,6,7,8,"asdf",{want_inserted_tuple => 1}) or die $box->ErrorStr;
# Select by single-field key
my $tuple = $box->Select(1); # scalar context - scalar result: $tuple
my @tuples = $box->Select(1,2,3); # list context - list result: ($tuple, $tuple, ...)
my $tuples = $box->Select([1,2,3],{space => "primary", use_index => "idx1"}); # arrayref result: [$tuple, $tuple, ...]
# Select by multi-field key
my $tuples = $box->Select([[2,3]],{use_index => "idx2"}); # by full key
my $tuples = $box->Select([[2]] ,{use_index => "idx2"}); # by partial key
my $bool = $box->UpdateMulti(1,[ f4 => add => 3 ]);
my $bool = $box->UpdateMulti(2,[ f4 => add => 3 ],{space => "primary"});
my $tuple = $box->UpdateMulti(3,[ f4 => add => 3 ],{want_updated_tuple => 1});
my $bool = $box->Delete(1);
my $tuple = $box->Delete(2, {want_deleted_tuple => 1});
π¦ DESCRIPTION π¦
π§ METHODS π§
π new
my $box = $class->new(\%args);
%args: β here's the whole menu, no skips, we go through every single one π
spaces => [ \%space, ... ]β the list of spaces you're hooking up.
%space:
space => $space_id_uint32β the space id exactly as you set it in the Tarantool/Box config.name => $space_name_stringβ a self-descriptive space id, and it gets mapped intospace.format => $format_stringβ apack()-compatible tuple format string. allowed formats are:"QqLlSsC(c&$)*", where&stands for bytestring and$stands forutf8string. heads up:Qqis usable only if perl itself supports int64. tuples' fields get packed/unpacked according to thisformat. and a*at the end offormatturns onLongTuple. periodt.hashify => $coderefβ drop in a callback to turn each tuple into a cute good-looking hash π₯° it receives thespaceid and the resultset as arguments. no return value needed, it just does its thing.
$coderef = sub {
my ($space_id, $resultset) = @_;
$_ = { FieldName1 => $_->[0], FieldName2 => $_->[1], ... } for @$resultset;
};
fields => $arrayrefβ hand it an arrayref of field names matching theformatand every tuple becomes a good-looking hash β¨ names must begin with[A-Za-z]. ifLongTupleis enabled, the last field gets used to fold the tailing fields.long_fields => $arrayrefβ an arrayref of field names matching(xxx)*that turns the tailing fields into a good-looking array of hashes. names must begin with[A-Za-z]. this one only works withLongTupleenabled, no cap.indexes => [ \%index, ... ]β the index list.
%index:
id => $index_id_uint32β the index id as set in the Tarantool/Box config inside the currentspace. if you don't set it, the order position inindexesis treated as theid.name => $index_name_stringβ a self-descriptive index id, which gets mapped intoindex_id.keys => [ $field_no_uint32, ... ]β a properly ordered arrayref of the fields' numbers that are indexed. order matters bestie, don't freestyle it.
default_index => $default_index_name_string_or_id_uint32β the indexidornameused by default for the currentspacein select operations. you must set this if there's more than one\%indexin there.primary_key_index => $primary_key_name_string_or_id_uint32β the indexidornameused by default for the currentspacein update operations. by default it's just set todefault_index.
default_space => $default_space_name_string_or_id_uint32β the spacespaceornameused by default. must be set if there's more than one\%spacein play.timeout => $timeout_fractional_seconds_float || 23β the common timeout for network operations, in fractional seconds. default23.select_timeout => $select_timeout_fractional_seconds_float || 2β the timeout for select queries' network operations. peep"select_retry"for the rest of the lore.retry => $retry_int || 1β the common retries number for network operations. default1.select_retry => $select_retry_int || 3β the retries number for select queries' network operations. default3.
sometimes you need a short timeout for select's and a long timeout for critical update's, because when a timeout hits you straight up don't know if the update has succeeded. and for that exact same reason we can't retry update operations.
so yeah β increasing timeout and setting retry => 1 for updates lowers the possibility of those situations (but of course does not exclude them at all), and guarantees that we don't do the same thing more than once. slay.
soft_retry => $soft_retry_int || 3β the common retries number for Tarantool/Box temporary errors (these are marked by1in the lowest byte oferror_code). in that case we know for sure the request was declined by Tarantool/Box for some reason (a tuple was locked for another update, for example), so we can try it again π
this is also limited by retry/select_retry (depending on the query type).
retry_delay => $retry_delay_fractional_seconds_float || 1β a delay between retries for network operations. default1.raise => $raise_bool || 1β should we raise exceptions? if so, exceptions get raised when no more retries are left and all the tries failed (with timeout, fatal, or temporary error). default is1.debug => $debug_level_int || 0β debug level.0prints nothing,9prints everything. default0.name => $nameβ a string used for self-description. mainly for debugging purposes.
π Error
the last error code, or 'fail' for some network reason β often a timeout. iconic behaviour honestly.
$box->Insert(@tuple) or die sprintf "Error %X", $box->Error; # die "Error 202"
π ErrorStr
the last error code and its description, all cozy in a single string.
$box->Insert(@tuple) or die $box->ErrorStr; # die "Error 00000202: Illegal Parameters"
π Call
call a stored procedure. on success it returns an arrayref of the result tuple(s) π«Ά
my $results = $box->Call('stored_procedure_name', \@procedure_params, \%options) or die $box->ErrorStr; # Call failed
my $result_tuple = @$results && $results->[0] or warn "Call succeeded, but returned nothing";
@procedure_paramsβ an array of bytestrings passed as-is to the procecedure.%optionsunpack_formatβ the format used to unpack the result tuple, same deal as theformatoption fornew().
π Add, Insert, Replace
$box->Add(@tuple) or die $box->ErrorStr; # only store a new tuple
$box->Replace(@tuple, { space => "secondary" }); # only store an existing tuple
$box->Insert(@tuple, { space => "main" }); # store anyway
insert a @tuple into the storage, into $options{space} or the default_space space. all three of them return "true" when it works out π
all of them take the exact same parameters:
@tupleβ the tuple to insert. all fields must be defined, no half-finished vibes. all fields getpack()ed according toformat(go read"new").%optionsspace => $space_id_uint32_or_name_stringβ pick which storage space you're working on.
and the difference between the three? it's all about how they treat a tuple with the same primary key π
- Add will succeed if and only if a duplicate-key tuple does not exist.
- Replace will succeed if and only if a duplicate-key tuple does exist.
- Insert will succeed anyway. the duplicate-key tuple gets overwritten. no thoughts, just vibes.
π Select
select tuple(s) from storage.
my $key = $id;
my $key = [ $firstname, $lastname ];
my @keys = ($key, ...);
my $tuple = $box->Select($key) or $box->Error && die $box->ErrorStr;
my $tuple = $box->Select($key, \%options) or $box->Error && die $box->ErrorStr;
my @tuples = $box->Select(@keys) or $box->Error && die $box->ErrorStr;
my @tuples = $box->Select(@keys, \%options) or $box->Error && die $box->ErrorStr;
my $tuples = $box->Select(\@keys) or die $box->ErrorStr;
my $tuples = $box->Select(\@keys, \%options) or die $box->ErrorStr;
$key, @keys, \@keysβ the keys you wanna select by. all keys must be defined, bestie.
contextual behaviour, because context is everything β¨:
- in scalar context, you can select one
$key, and the resulting tuple gets returned. check$box->Errorto see if there was an actual error, or if there's just no such key in the storage. - in list context, you can select several
@keys, and the resulting tuples get returned. check$box->Errorto see if there was an actual error, or if there's just no such keys in the storage. - if you select
\@keysthen\@tuplesgets returned on success.@tupleswill be empty if there are no such keys, and false gets returned in case of error.
other notes, iykyk:
- if you select using an index on multiple fields, each
$keyshould be given as a key-tuple:$key = [ $key_field1, $key_field2, ... ].
%optionsspace => $space_id_uint32_or_name_stringβ specify the storage space to select from, by id or by name.
use_index => $index_id_uint32_or_name_stringβ picks the index to use, by id or by name, whichever you vibe with.limit => $limit_uint32β max tuples to select, bestie. It's already set toMAX_INT32by default so like... go off I guess.raw => $boolβ don't "hashify" (go peek at"new"), and disable"utf8"processing. raw bytes, no filter πhash_by => $byβ returns a hashref of the resultset. If you "hashify" the result set then$bymust be a field name of the hash you return, otherwise it must be a number of a field of the tuple. On error you get"False"back, which is not the vibe. π
ποΈ Delete
Deletes a tuple from storage. Returns false if something goes wrong, no cap.
my $n_deleted = $box->Delete($key) or die $box->ErrorStr;
my $n_deleted = $box->Delete($key, \%options) or die $box->ErrorStr;
warn "Nothing was deleted" unless int $n_deleted;
my $deleted_tuple_set = $box->Delete($key, { want_deleted_tuples => 1 }) or die $box->ErrorStr;
warn "Nothing was deleted" unless @$deleted_tuple_set;
π %options
space => $space_id_uint32_or_name_stringβ which storage space to work on, by id or by name.want_deleted_tuple => $boolβ if$boolis truthy, it returns the deleted tuple. give it back, king π
β¨ UpdateMulti β¨
Applies several update operations to a tuple at once. one shot, one opportunity.
my @op = ([ f1 => add => 10 ], [ f1 => and => 0xFF], [ f2 => set => time() ], [ misc_string => cutend => 3 ]);
my $n_updated = $box->UpdateMulti($key, @op) or die $box->ErrorStr;
my $n_updated = $box->UpdateMulti($key, @op, \%options) or die $box->ErrorStr;
warn "Nothing was updated" unless int $n_updated;
my $updated_tuple_set = $box->UpdateMulti($key, @op, { want_result => 1 }) or die $box->ErrorStr;
warn "Nothing was updated" unless @$updated_tuple_set;
Different fields can be updated in one shot. The same field can even be updated more than once. All update operations happen atomically β like, all or nothing, periodt. Returns false upon error.
π¦ @op = ([ $field => $op => $value ], ...)
$fieldβ the field-to-update, as a number or a name (go see"fields","LongTuple").$opβ the actual operation, bestie:setβ Set$fieldto$value.add,and,xor,orβ Apply an arithmetic operation to$fieldwith argument$value. Currently arithmetic operations are supported only for int32 (4-byte length) fields (and$valuestoo). that's the limit, sorry not sorry.splice,substrβ Apply a perl-like splice operation to$field.$value = [$OFFSET, $LENGTH, $REPLACE_WITH].substris just an alias, same energy.append,prependβ Append or prepend$fieldwith the$valuestring.cutbeg,cutendβ Cut$valuebytes from the beginning or the end of$field. βοΈ
π %options
space => $space_id_uint32_or_name_stringβ which storage space to work on, by id or by name.want_updated_tuple => $boolβ if$boolis truthy, it returns the updated tuple. π«Ά
π AnyEvent
"Insert, UpdateMulti, Select, Delete, Call" methods can all be handed these options:
callback => sub { my ($data, $error) = @_; }β Do an async request using AnyEvent.$datacontains unpacked data, processed according to the request options.$errorcontains a message string if something went wrong. Set upraise => 0to actually use this option, iykyk.
π "Continuations"
"Select" methods can be given these options:
return_fh => 1β The request only does the send operation on the network, and returns{ fh => $IO_Handle, continue => $code }, or false if the send operation failed.$codereads data from the network, unpacks it, processes it according to the options, and returns it.
You've gotta handle timeouts and retries yourself, bestie (using the select() call, for example). it's giving DIY.
Usage example, literally:
my $continuation = $box->Select(13,{ return_fh => 1 });
ok $continuation, "select/continuation";
my $rin = '';
vec($rin,$continuation->{fh}->fileno,1) = 1;
my $ein = $rin;
ok 0 <= select($rin,undef,$ein,2), "select/continuation/select";
my $res = $continuation->{continue}->();
use Data::Dumper;
is_deeply $res, [13, 'some_email@test.mail.ru', 1, 2, 3, 4, '123456789'], "select/continuation/result";
π LongTuple
If "format" given to "new", or "unpack_format" given to "Call", ends with a star ("*"), long tuple is enabled. The last field (or group of fields) of "format" represents the variable-length tail of the tuple. The "long_fields" option given to "new" will fold that tail into an array of hashes. iconic behaviour fr fr.
$box->Insert(1,"2",3); #1
$box->Insert(3,"2",3,4,5); #2
$box->Insert(5,"2",3,4,5,6,7); #3
If we set up
format => "L&CL*",
fields => [qw/ a b c d /], # d is the folding field here
# no long_fields - no folding into hash
we'll get:
$result = $box->Select([1,2,3,4,5]);
$result = [
{ a => 1, b => "2", c => 3, d => [] }, #1
{ a => 3, b => "2", c => 3, d => [4,5] }, #2
{ a => 5, b => "2", c => 3, d => [4,5,6,7] }, #3
];
And if we set up
format => "L&C(LL)*",
fields => [qw/ a b c d /], # d is the folding field here
long_fields => [qw/ d1 d2 /],
we'll get:
$result = [
{ a => 1, b => "2", c => 3, d => [] }, #1
{ a => 3, b => "2", c => 3, d => [{d1=>4, d2=>5}] }, #2
{ a => 5, b => "2", c => 3, d => [{d1=>4, d2=>5}, {d1=>6, d2=>7}] }, #3
];
"UpdateMulti" can be given a field number in several ways, bestie:
$linear_index_int
$box->UpdateMulti(5, [ 5 => set => $val ]) #3: set 6 to $val
- an arrayref of
[$index_of_folded_subtuple_int, $long_field_name_str_or_index_int]
$box->UpdateMulti(5, [ [1,0] => set => $val ]) #3: set 6 to $val
$box->UpdateMulti(5, [ [1,'d1'] => set => $val ]) #3: set 6 to $val
πΈ utf8
Utf8 strings are supported very simply, no overthinking. When you push any data to tarantool (with any query, read or write), the utf8 flag is set off, so all data is pushed as a bytestring. When reading the response, for fields marked with a dollar sign "$" (peep "new") β including such fields in the "LongTuple" tail β the utf8 flag is set on. That's it, that's all. Validity is on you. π
π LICENCE AND COPYRIGHT
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
π SEE ALSO
- <http://tarantool.org>
MR::Tarantool::Box::Singleton
and that's the whole page, bestie π«Ά this one's from perl v5.30.3 and it's dated 2020-07-21 β go forth and box those tuples, you ate π
β¨
π¦ where this page comes from
- debian bookworm: libmr-tarantool-perl 0.0.24-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- debian trixie: libmr-tarantool-perl 0.0.24-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu jammy: libmr-tarantool-perl 0.0.24-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu noble: libmr-tarantool-perl 0.0.24-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu resolute: libmr-tarantool-perl 0.0.24-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu stonking: libmr-tarantool-perl 0.0.24-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms