diff --git a/.gitignore b/.gitignore index 97d442eb02d69fc8d40e8a5da3fd7da7ac00f513..42fb64587f5f9b871fcd0dd622795669bb16e442 100644 --- a/.gitignore +++ b/.gitignore @@ -65,7 +65,7 @@ third_party/luajit/src/lj_recdef.h third_party/luajit/src/lj_vm.s *.reject extra/txt2c -src/box/*.lua.c +src/box/lua/*.lua.c doc/www-data/*.html doc/www-data/*.ru.html doc/www-data.in/download diff --git a/doc/user/errcode.xml b/doc/user/errcode.xml index 4838ef0b3ae90bf016d173bb04baf686fa54facd..173c9b77c7e44a1812a2713759f203655ea9acd7 100644 --- a/doc/user/errcode.xml +++ b/doc/user/errcode.xml @@ -77,6 +77,14 @@ file <filename xlink:href="https://github.com/mailru/tarantool/blob/master/inclu </para></listitem> </varlistentry> + <varlistentry> + <term xml:id="ER_FIBER_STACK" xreflabel="ER_FIBER_STACK">ER_FIBER_STACK</term> + <listitem><para>Recursion limit reached when creating a new fiber. This is + usually an indicator of a bug in a stored procedure, recursively invoking itself + ad infinitum. + </para></listitem> + </varlistentry> + </variablelist> </appendix> diff --git a/doc/user/replication.xml b/doc/user/replication.xml index a4e5c8d01d2a84449c5112f2d38d07e1765c66c2..f97bcee166fa6e66a8f668021506be775b3b081b 100644 --- a/doc/user/replication.xml +++ b/doc/user/replication.xml @@ -177,8 +177,8 @@ <para> Recover the old master. If there are updates that didn't make it to the new master, they have to be applied - manually. You can use <olink targetptr="cat-option"/> - option to read server logs. + manually. You can use Tarantool command line client + to read server logs. </para> </listitem> </itemizedlist> diff --git a/doc/user/stored-procedures.xml b/doc/user/stored-procedures.xml index dcb4eb1b5094e3665d8d7c60fd677aa57db970d3..e261ce51f773f165ac227084106900e19038094e 100644 --- a/doc/user/stored-procedures.xml +++ b/doc/user/stored-procedures.xml @@ -45,7 +45,7 @@ Found 1 tuple: In the language of the administrative console <olink targetptr="lua-command" /> evaluates an arbitrary Lua chunk. CALL is the SQL standard statement, so its syntax - was adopted by Tarantool command line client + was adopted by Tarantool command line client to invoke the CALL command of the binary protocol. </para> <para> @@ -319,7 +319,7 @@ localhost> lua type(i), type(i*2), type(i/2), i, i*2, i/2 <variablelist xml:id="box" xreflabel="box"> <varlistentry> <term> - <emphasis role="lua">box.process(op, request)</emphasis> + <emphasis role="lua" xml:id="box.process">box.process(op, request)</emphasis> </term> <listitem> <para> @@ -422,7 +422,7 @@ localhost> lua box.select(5, 1, 'firstname', 'lastname') <varlistentry> <term> - <emphasis role="lua">box.insert(space_no, ...)</emphasis> + <emphasis role="lua" xml:id="box.insert">box.insert(space_no, ...)</emphasis> </term> <listitem><simpara></simpara></listitem> </varlistentry> @@ -430,7 +430,7 @@ localhost> lua box.select(5, 1, 'firstname', 'lastname') <varlistentry> <term> <emphasis role="lua" xml:id="box.select_limit" xreflabel="box.select_limit"> - box.select(space_no, index_no, offset, limit, ...) + box.select_limit(space_no, index_no, offset, limit, ...) </emphasis> </term> <listitem> @@ -448,7 +448,7 @@ localhost> lua box.select(5, 1, 'firstname', 'lastname') <varlistentry> <term> - <emphasis role="lua">box.replace(space_no, ...)</emphasis> + <emphasis role="lua" xml:id="box.replace">box.replace(space_no, ...)</emphasis> </term> <listitem> <para> @@ -467,7 +467,7 @@ localhost> lua box.select(5, 1, 'firstname', 'lastname') <varlistentry> <term> - <emphasis role="lua">box.update(space_no, key, format, ...)</emphasis> + <emphasis role="lua" xml:id="box.update">box.update(space_no, key, format, ...)</emphasis> </term> <listitem> <para> @@ -540,7 +540,7 @@ localhost> lua box.update(0, 2, '#p', 2, 'Bienvenue tout le monde!') <varlistentry> <term> - <emphasis role="lua">box.delete(space_no, ...)</emphasis> + <emphasis role="lua" xml:id="box.delete">box.delete(space_no, ...)</emphasis> </term> <listitem><para> Delete a tuple identified by a primary key. @@ -1619,14 +1619,14 @@ and <code>box.fiber.testcancel()</code> is checked whenever such an event occurs <variablelist xml:id="box.fiber"> <varlistentry> <term> - <emphasis role="lua">box.fiber.id(fiber) </emphasis> + <emphasis role="lua" xml:id="box.fiber.id">box.fiber.id(fiber) </emphasis> </term> <listitem><simpara>Return a numeric id of the fiber.</simpara></listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua">box.fiber.self() </emphasis> + <emphasis role="lua" xml:id="box.fiber.self">box.fiber.self() </emphasis> </term> <listitem><simpara>Return <code>box.fiber</code> userdata object for the currently scheduled fiber.</simpara></listitem> @@ -1634,14 +1634,14 @@ and <code>box.fiber.testcancel()</code> is checked whenever such an event occurs <varlistentry> <term> - <emphasis role="lua">box.fiber.find(id) </emphasis> + <emphasis role="lua" xml:id="box.fiber.find">box.fiber.find(id) </emphasis> </term> <listitem><simpara>Locate a fiber userdata object by id.</simpara></listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua">box.fiber.create(function) </emphasis> + <emphasis role="lua" xml:id="box.fiber.create">box.fiber.create(function) </emphasis> </term> <listitem><simpara> Create a fiber for <code>function</code>. @@ -1653,7 +1653,7 @@ and <code>box.fiber.testcancel()</code> is checked whenever such an event occurs <varlistentry> <term> - <emphasis role="lua">box.fiber.resume(fiber, ...) </emphasis> + <emphasis role="lua" xml:id="box.fiber.resume">box.fiber.resume(fiber, ...) </emphasis> </term> <listitem><simpara>Resume a created or suspended fiber.</simpara></listitem> @@ -1671,13 +1671,14 @@ and <code>box.fiber.testcancel()</code> is checked whenever such an event occurs If the fiber is attached, whatever arguments are passed to this call, are passed on to the calling fiber. If the fiber is detached, <code>box.fiber.yield()</code> - returns back everything passed into it. + returns back everything passed into it after temporarily + yielding control back to the scheduler. </para></listitem> </varlistentry> <varlistentry> <term> - <emphasis role="lua">box.fiber.detach()</emphasis> + <emphasis role="lua" xml:id="box.fiber.detach">box.fiber.detach()</emphasis> </term> <listitem><simpara> Detach the current fiber. This is a cancellation point. This is a yield point. @@ -1686,7 +1687,19 @@ and <code>box.fiber.testcancel()</code> is checked whenever such an event occurs <varlistentry> <term> - <emphasis role="lua">box.fiber.sleep(time)</emphasis> + <emphasis role="lua" xml:id="box.fiber.wrap">box.fiber.wrap(function, ...)</emphasis> + </term> + <listitem><simpara> + This is a quick way to create and start a detached + fiber. The fiber function is passed in the first + argument, the function arguments follow. The fiber is + created detached and resumed immediately. + </simpara></listitem> + </varlistentry> + + <varlistentry> + <term> + <emphasis role="lua" xml:id="box.fiber.sleep">box.fiber.sleep(time)</emphasis> </term> <listitem><simpara> Yield to the sched fiber and sleep <code>time</code> seconds. @@ -1696,7 +1709,20 @@ and <code>box.fiber.testcancel()</code> is checked whenever such an event occurs <varlistentry> <term> - <emphasis role="lua">box.fiber.cancel(fiber)</emphasis> + <emphasis role="lua" xml:id="box.fiber.status">box.fiber.status(fiber)</emphasis> + </term> + <listitem><simpara> + Returns the status of the fiber. If no argument is + provided, the current fiber's status is returned. The + status can be either <quote>dead</quote>, + <quote>suspended</quote>, <quote>attached</quote> + or <quote>running</quote>. + </simpara></listitem> + </varlistentry> + + <varlistentry> + <term> + <emphasis role="lua" xml:id="box.fiber.cancel">box.fiber.cancel(fiber)</emphasis> </term> <listitem><simpara> Cancel a <code>fiber</code>. @@ -1707,7 +1733,7 @@ and <code>box.fiber.testcancel()</code> is checked whenever such an event occurs <varlistentry> <term> - <emphasis role="lua">box.fiber.testcancel()</emphasis> + <emphasis role="lua" xml:id="box.fiber.testcancel">box.fiber.testcancel()</emphasis> </term> <listitem><simpara> Check if the current fiber has been canceled and @@ -2215,6 +2241,238 @@ end </variablelist> </section> +<section xml:id="sp-box-net-box"> + <title>Package <code>box.net.box</code> — working with networked Tarantool peers</title> + <simpara> + Library <code>box.net</code> contains connectors + to remote database systems, such as MariaDB or PostgreSQL. + The first supported system is Tarantool itself. + </simpara> +<variablelist xml:id="box.net.box"> + <simpara> + The basic object provided by <code>box.net.box</code> + library is a connection. A connection is created by + calling <code>box.net.box.new()</code>. To + execute remote requests, simply invoke methods of the + connection object, a physical connection is established + upon request and is re-established if necessary. + When done, issue <code>conn:close()</code>. Connection + objects are garbage collected just like any other objects + in Lua, so an explicit destruction is not mandatory. + However, since <code>close()</code> is a system call, it + is a good programming practice to close a connection + explicitly when it is no longer needed, to avoid lengthy + stalls of the garbage collector. + </simpara> + <simpara> + The library also provides a pre-created connection object + to the local server, <code>box.net.self</code>. This + connection is always <quote>established</quote>. The + purpose of this object is to make polymorphic use of + <code>box.net.box</code> API easier. There is an + important difference, however, between the embedded + connection and a remote one. With the embedded connection, + requests which do not modify data do not yield. When using + a remote connection, it is important to keep in mind that + any request can yield, and local database state may + have changed by the time it returns. + </simpara> + <simpara> + All <code>box.net.box</code> methods are fiber-safe. It's + safe to share and use the same connection object across + multiple concurrent fibers. In fact, it's perhaps the + best programming practice with Tarantool: when multiple + fibers use the same connection, all requests are pipelined + through the same network socket, but each fiber gets a + correct response back. Reducing the number of active sockets + lowers the overhead of system calls and increases the + overall server performance. There are, however cases, when + a single connection is not enough: when it's necessary to + prioritize requests, use different authentication ids, + etc. + </simpara> + <simpara> + All remote calls support execution timeouts. A specialized + wrapper, <code>box.net.box.timeout()</code> allows setting + a timeout. Using a wrapper object makes the remote + connection API compatible with the local one, removing + the need for a separate <code>timeout</code> argument, ignored + by the local version. Once sent, a + request can not be revoked from the remote server even if + a timeout expires: the expired timeout only aborts the + wait for the remote server response. + <code>box.net.box.timeout()</code>, + </simpara> + <simpara> + Object-oriented and functional APIs are equivalent: + <code>conn:close()</code> is identical to + <code>box.net.box.close(conn)</code>. + </simpara> + + <varlistentry> + <term> + <emphasis role="lua" xml:id="box.net.box.new"> + conn = box.net.box.new(host, port [, reconnect_interval])</emphasis> + </term> + <listitem> + <para> + Create a new connection. The connection is + established on demand, at the time of the first + request. It is re-established automatically after + a disconnect. Argument + <code>reconnect_interval</code> (in seconds) is + responsible for the amount of time the server + sleeps between failing attempts to reconnect. The + returned object supports methods for making remote + calls, such as select, update or delete. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><emphasis role="lua" xml:id="box.net.box.ping"> + conn:ping()</emphasis></term> + <listitem> + <para> + Execute a PING command. + Returns <code>true</code> on success, + <code>false</code> on error. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><emphasis role="lua" xml:id="box.net.box.close"> + conn:close()</emphasis></term> + <listitem> + <para> + Close a connection. The connection is also + closed when it's garbage collected. It's still + recommended to close connections explicitly, + to spare the garbage collector from heavy work + such as closing the socket. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><emphasis role="lua" xml:id="box.net.box.select"> + conn:select(space_no, index_no, ...)</emphasis></term> + <listitem> + <para> + See <code + xlink:href="#box.select">box.select(...)</code>. + Please note, that unlike a local + <code>box.select()</code> any call to a remote + server yields, thus local data may change while + remote <code>select()</code> is running. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><emphasis role="lua" xml:id="box.net.box.select_limit"> + conn:select_limit(space_no, index_no, offset, limit, ...)</emphasis></term> + <listitem> + <para> + See <code + xlink:href="#box.select_limit">box.select_limit(...)</code> + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><emphasis role="lua" xml:id="box.net.box.select_range"> + conn:select_range(space_no, index_no, limit, key, ...)</emphasis></term> + <listitem> + <para> + See <code + xlink:href="#box.select_range">box.select_range(...)</code>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><emphasis role="lua" xml:id="box.net.box.insert">conn:insert(space_no, ...)</emphasis></term> + <listitem> + <para> + See <code xlink:href="#box.insert">box.insert(...)</code>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><emphasis role="lua" xml:id="box.net.box.replace">conn:replace(space_no, ...)</emphasis></term> + <listitem> + <para> + See <code xlink:href="#box.replace">box.replace(...)</code>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><emphasis role="lua" xml:id="box.net.box.update">conn:update(...)</emphasis></term> + <listitem> + <para> + See <code + xlink:href="#box.update">box.update(...)</code>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><emphasis role="lua" xml:id="box.net.box.delete">conn:delete(space_no, key)</emphasis></term> + <listitem> + <para> + See <code xlink:href="#box.delete">box.delete(...)</code>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><emphasis role="lua" xml:id="box.net.box.call">conn:call(proc_name, ...)</emphasis></term> + <listitem> + <para>Call a remote stored procedure, such as + <code>box.select_reverse_range()</code>. + Please keep in mind that the call is using + the binary protocol to pack procedure arguments, + and since the latter is type-agnostic it's recommended + to pass all arguments of remote stored procedure as + strings, for example: +<programlisting> + conn:call("box.select_reverse_range", "1", "4", "10", "Smith") +</programlisting> + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><emphasis role="lua" xml:id="box.net.box.timeout">conn:timeout(timeout)</emphasis></term> + <listitem> + <para> + Returns a closure which is identical to the + invoked function, except the added timeout + functionality. +<programlisting> +-- wait for 'update' until it is finished +local tuple = conn:update('1', 'key', ...) + +-- send update but don't bother to wait for results +local other = conn:timeout(0):update('1', 'arg1', ...) +</programlisting> + </para> + </listitem> + </varlistentry> +</variablelist> + + <bridgehead renderas="sect4">Example</bridgehead> + <programlisting> +-- connect to the local server +local self = box.net.box.new('127.0.0.1', box.info.primary_port) +self:insert("1", "Hello", "World") +</programlisting> +</section> + <section xml:id="sp-box-cfg"> <title>Packages <code>box.cfg</code>, <code>box.info</code>, <code>box.slab</code> and diff --git a/doc/user/target.db b/doc/user/target.db index 8f0bfdff9fbb770498c2124d0831af42f49fa836..306bb5c8c4b695e4dcc519454eda6d7031d2f7d9 100644 --- a/doc/user/target.db +++ b/doc/user/target.db @@ -1,5 +1,5 @@ -<div element="book" href="#tarantool-user-guide" number="" targetptr="tarantool-user-guide"><ttl>Tarantool User Guide, version 1.4.9-40-g67f3d3c</ttl><xreftext>Tarantool User Guide, version 1.4.9-40-g67f3d3c</xreftext><div element="chapter" href="#preface" number="1" targetptr="preface"><ttl>Preface</ttl><xreftext>Chapter 1, <i>Preface</i></xreftext><div element="section" href="#tarantool-overview" number="" targetptr="tarantool-overview"><ttl>Tarantool: an overview</ttl><xreftext>the section called “Tarantool: an overviewâ€</xreftext></div><div element="section" href="#manual-conventions" number="" targetptr="manual-conventions"><ttl>Conventions</ttl><xreftext>the section called “Conventionsâ€</xreftext></div><div element="section" href="#reporting-bugs" number="" targetptr="reporting-bugs"><ttl>Reporting bugs</ttl><xreftext>the section called “Reporting bugsâ€</xreftext></div></div><div element="chapter" href="#getting-started" number="2" targetptr="getting-started"><ttl>Getting started</ttl><xreftext>Chapter 2, <i>Getting started</i></xreftext></div><div element="chapter" href="#data-and-persistence" number="3" targetptr="data-and-persistence"><ttl>Data model and data persistence</ttl><xreftext>Chapter 3, <i>Data model and data persistence</i></xreftext><div element="section" href="#dynamic-data-model" number="" targetptr="dynamic-data-model"><ttl>Dynamic data model</ttl><xreftext>the section called “Dynamic data modelâ€</xreftext></div><div element="section" href="#data-persistence" number="" targetptr="data-persistence"><ttl>Data persistence</ttl><xreftext>the section called “Data persistenceâ€</xreftext></div></div><div element="chapter" href="#language-reference" number="4" targetptr="language-reference"><ttl>Language reference</ttl><xreftext>Chapter 4, <i>Language reference</i></xreftext><div element="section" href="#data-manipulation" number="" targetptr="data-manipulation"><ttl>Data manipulation</ttl><xreftext>the section called “Data manipulationâ€</xreftext><div element="section" href="#memcached-protocol" number="" targetptr="memcached-protocol"><ttl>Memcached protocol</ttl><xreftext>the section called “Memcached protocolâ€</xreftext></div></div><div element="section" href="#administrative-console" number="" targetptr="administrative-console"><ttl>Administrative console</ttl><xreftext>the section called “Administrative consoleâ€</xreftext><obj element="term" href="#save-snapshot" number="" targetptr="save-snapshot"><ttl>???TITLE???</ttl><xreftext>SAVE SNAPSHOT</xreftext></obj><obj element="term" href="#reload-configuration" number="" targetptr="reload-configuration"><ttl>???TITLE???</ttl><xreftext>RELOAD CONFIGURATION</xreftext></obj><obj element="term" href="#show-configuration" number="" targetptr="show-configuration"><ttl>???TITLE???</ttl><xreftext>SHOW CONFIGURATION</xreftext></obj><obj element="term" href="#show-info" number="" targetptr="show-info"><ttl>???TITLE???</ttl><xreftext>SHOW INFO</xreftext></obj><obj element="term" href="#show-stat" number="" targetptr="show-stat"><ttl>???TITLE???</ttl><xreftext>SHOW STAT</xreftext></obj><obj element="term" href="#show-slab" number="" targetptr="show-slab"><ttl>???TITLE???</ttl><xreftext>SHOW SLAB</xreftext></obj><obj element="term" href="#show-palloc" number="" targetptr="show-palloc"><ttl>???TITLE???</ttl><xreftext>SHOW PALLOC</xreftext></obj><obj element="term" href="#save-coredump" number="" targetptr="save-coredump"><ttl>???TITLE???</ttl><xreftext>SAVE COREDUMP</xreftext></obj><obj element="term" href="#show-fiber" number="" targetptr="show-fiber"><ttl>???TITLE???</ttl><xreftext>SHOW FIBER</xreftext></obj><obj element="term" href="#lua-command" number="" targetptr="lua-command"><ttl>???TITLE???</ttl><xreftext>LUA ...</xreftext></obj></div><div element="section" href="#stored-procedures" number="" targetptr="stored-procedures"><ttl>Writing stored procedures in Lua</ttl><xreftext>the section called “Writing stored procedures in Luaâ€</xreftext><obj element="emphasis" href="#init.lua" number="" targetptr="init.lua"><ttl>???TITLE???</ttl><xreftext>init.lua</xreftext></obj><obj element="term" href="#tonumber64" number="" targetptr="tonumber64"><ttl>???TITLE???</ttl><xreftext>tonumber64</xreftext></obj><div element="section" href="#sp-box" number="" targetptr="sp-box"><ttl>Package <code class="code">box</code></ttl><xreftext>the section called “Package <code class="code">box</code>â€</xreftext><obj element="variablelist" href="#box" number="" targetptr="box"><ttl>???TITLE???</ttl><xreftext>box</xreftext></obj><obj element="emphasis" href="#box.select" number="" targetptr="box.select"><ttl>???TITLE???</ttl><xreftext>box.select</xreftext></obj><obj element="emphasis" href="#box.select_range" number="" targetptr="box.select_range"><ttl>???TITLE???</ttl><xreftext>box.select_range</xreftext></obj><obj element="emphasis" href="#box.select_reverse_range" number="" targetptr="box.select_reverse_range"><ttl>???TITLE???</ttl><xreftext>box.select_reverse_range</xreftext></obj></div><div element="section" href="#sp-box-tuple" number="" targetptr="sp-box-tuple"><ttl>Package <code class="code">box.tuple</code></ttl><xreftext>the section called “Package <code class="code">box.tuple</code>â€</xreftext><obj element="variablelist" href="#box.tuple" number="" targetptr="box.tuple"><ttl>???TITLE???</ttl><xreftext>box.tuple</xreftext></obj></div><div element="section" href="#sp-box-space" number="" targetptr="sp-box-space"><ttl>Package <code class="code">box.space</code></ttl><xreftext>the section called “Package <code class="code">box.space</code>â€</xreftext><obj element="variablelist" href="#box.space" number="" targetptr="box.space"><ttl>???TITLE???</ttl><xreftext>box.space</xreftext></obj><obj element="emphasis" href="#box.space.select_range" number="" targetptr="box.space.select_range"><ttl>???TITLE???</ttl><xreftext>box.space[i].select_range()</xreftext></obj><obj element="emphasis" href="#box.space.select_reverse_range" number="" targetptr="box.space.select_reverse_range"><ttl>???TITLE???</ttl><xreftext>box.space.select_reverse_range</xreftext></obj></div><div element="section" href="#sp-box-index" number="" targetptr="sp-box-index"><ttl>Package <code class="code">box.index</code></ttl><xreftext>the section called “Package <code class="code">box.index</code>â€</xreftext><obj element="variablelist" href="#box.index" number="" targetptr="box.index"><ttl>???TITLE???</ttl><xreftext>box.index</xreftext></obj><obj element="emphasis" href="#box.index.iterator" number="" targetptr="box.index.iterator"><ttl>???TITLE???</ttl><xreftext>box.index.iterator</xreftext></obj><obj element="table" href="#iterator-types" number="4.1" targetptr="iterator-types"><ttl>Iterator types</ttl><xreftext>Table 4.1, “Iterator typesâ€</xreftext></obj></div><div element="section" href="#sp-box-fiber" number="" targetptr="sp-box-fiber"><ttl>Package <code class="code">box.fiber</code></ttl><xreftext>the section called “Package <code class="code">box.fiber</code>â€</xreftext><obj element="variablelist" href="#box.fiber" number="" targetptr="box.fiber"><ttl>???TITLE???</ttl><xreftext>???TITLE???</xreftext></obj></div><div element="section" href="#sp-box-session" number="" targetptr="sp-box-session"><ttl>Package <code class="code">box.session</code></ttl><xreftext>the section called “Package <code class="code">box.session</code>â€</xreftext></div><div element="section" href="#sp-box-ipc" number="" targetptr="sp-box-ipc"><ttl>Package <code class="code">box.ipc</code> — inter procedure communication</ttl><xreftext>the section called “Package <code class="code">box.ipc</code> — inter procedure communicationâ€</xreftext><obj element="variablelist" href="#box.ipc" number="" targetptr="box.ipc"><ttl>???TITLE???</ttl><xreftext>???TITLE???</xreftext></obj></div><div element="section" href="#sp-box-socket" number="" targetptr="sp-box-socket"><ttl>Package <code class="code">box.socket</code> — TCP and UDP sockets</ttl><xreftext>the section called “Package <code class="code">box.socket</code> — TCP and UDP socketsâ€</xreftext><obj element="variablelist" href="#box.socket" number="" targetptr="box.socket"><ttl>???TITLE???</ttl><xreftext>???TITLE???</xreftext></obj><obj element="table" href="#idp398336" number="4.2"><ttl><code class="code">readline()</code> returns</ttl><xreftext>Table 4.2, “<code class="code">readline()</code> returnsâ€</xreftext></obj></div><div element="section" href="#sp-box-cfg" number="" targetptr="sp-box-cfg"><ttl>Packages <code class="code">box.cfg</code>, +<div element="book" href="#tarantool-user-guide" number="" targetptr="tarantool-user-guide"><ttl>Tarantool User Guide, version 1.5.0-103-g387e34f</ttl><xreftext>Tarantool User Guide, version 1.5.0-103-g387e34f</xreftext><div element="chapter" href="#preface" number="1" targetptr="preface"><ttl>Preface</ttl><xreftext>Chapter 1, <i>Preface</i></xreftext><div element="section" href="#tarantool-overview" number="" targetptr="tarantool-overview"><ttl>Tarantool: an overview</ttl><xreftext>the section called “Tarantool: an overviewâ€</xreftext></div><div element="section" href="#manual-conventions" number="" targetptr="manual-conventions"><ttl>Conventions</ttl><xreftext>the section called “Conventionsâ€</xreftext></div><div element="section" href="#reporting-bugs" number="" targetptr="reporting-bugs"><ttl>Reporting bugs</ttl><xreftext>the section called “Reporting bugsâ€</xreftext></div></div><div element="chapter" href="#getting-started" number="2" targetptr="getting-started"><ttl>Getting started</ttl><xreftext>Chapter 2, <i>Getting started</i></xreftext></div><div element="chapter" href="#data-and-persistence" number="3" targetptr="data-and-persistence"><ttl>Data model and data persistence</ttl><xreftext>Chapter 3, <i>Data model and data persistence</i></xreftext><div element="section" href="#dynamic-data-model" number="" targetptr="dynamic-data-model"><ttl>Dynamic data model</ttl><xreftext>the section called “Dynamic data modelâ€</xreftext></div><div element="section" href="#data-persistence" number="" targetptr="data-persistence"><ttl>Data persistence</ttl><xreftext>the section called “Data persistenceâ€</xreftext></div></div><div element="chapter" href="#language-reference" number="4" targetptr="language-reference"><ttl>Language reference</ttl><xreftext>Chapter 4, <i>Language reference</i></xreftext><div element="section" href="#data-manipulation" number="" targetptr="data-manipulation"><ttl>Data manipulation</ttl><xreftext>the section called “Data manipulationâ€</xreftext><div element="section" href="#memcached-protocol" number="" targetptr="memcached-protocol"><ttl>Memcached protocol</ttl><xreftext>the section called “Memcached protocolâ€</xreftext></div></div><div element="section" href="#administrative-console" number="" targetptr="administrative-console"><ttl>Administrative console</ttl><xreftext>the section called “Administrative consoleâ€</xreftext><obj element="term" href="#save-snapshot" number="" targetptr="save-snapshot"><ttl>???TITLE???</ttl><xreftext>SAVE SNAPSHOT</xreftext></obj><obj element="term" href="#reload-configuration" number="" targetptr="reload-configuration"><ttl>???TITLE???</ttl><xreftext>RELOAD CONFIGURATION</xreftext></obj><obj element="term" href="#show-configuration" number="" targetptr="show-configuration"><ttl>???TITLE???</ttl><xreftext>SHOW CONFIGURATION</xreftext></obj><obj element="term" href="#show-info" number="" targetptr="show-info"><ttl>???TITLE???</ttl><xreftext>SHOW INFO</xreftext></obj><obj element="term" href="#show-stat" number="" targetptr="show-stat"><ttl>???TITLE???</ttl><xreftext>SHOW STAT</xreftext></obj><obj element="term" href="#show-slab" number="" targetptr="show-slab"><ttl>???TITLE???</ttl><xreftext>SHOW SLAB</xreftext></obj><obj element="term" href="#show-palloc" number="" targetptr="show-palloc"><ttl>???TITLE???</ttl><xreftext>SHOW PALLOC</xreftext></obj><obj element="term" href="#save-coredump" number="" targetptr="save-coredump"><ttl>???TITLE???</ttl><xreftext>SAVE COREDUMP</xreftext></obj><obj element="term" href="#show-fiber" number="" targetptr="show-fiber"><ttl>???TITLE???</ttl><xreftext>SHOW FIBER</xreftext></obj><obj element="term" href="#lua-command" number="" targetptr="lua-command"><ttl>???TITLE???</ttl><xreftext>LUA ...</xreftext></obj></div><div element="section" href="#stored-procedures" number="" targetptr="stored-procedures"><ttl>Writing stored procedures in Lua</ttl><xreftext>the section called “Writing stored procedures in Luaâ€</xreftext><obj element="emphasis" href="#init.lua" number="" targetptr="init.lua"><ttl>???TITLE???</ttl><xreftext>init.lua</xreftext></obj><obj element="term" href="#tonumber64" number="" targetptr="tonumber64"><ttl>???TITLE???</ttl><xreftext>tonumber64</xreftext></obj><div element="section" href="#sp-box" number="" targetptr="sp-box"><ttl>Package <code class="code">box</code></ttl><xreftext>the section called “Package <code class="code">box</code>â€</xreftext><obj element="variablelist" href="#box" number="" targetptr="box"><ttl>???TITLE???</ttl><xreftext>box</xreftext></obj><obj element="emphasis" href="#box.process" number="" targetptr="box.process"><ttl>???TITLE???</ttl><xreftext>???</xreftext></obj><obj element="emphasis" href="#box.select" number="" targetptr="box.select"><ttl>???TITLE???</ttl><xreftext>box.select</xreftext></obj><obj element="emphasis" href="#box.insert" number="" targetptr="box.insert"><ttl>???TITLE???</ttl><xreftext>???</xreftext></obj><obj element="emphasis" href="#box.select_limit" number="" targetptr="box.select_limit"><ttl>???TITLE???</ttl><xreftext>box.select_limit</xreftext></obj><obj element="emphasis" href="#box.replace" number="" targetptr="box.replace"><ttl>???TITLE???</ttl><xreftext>???</xreftext></obj><obj element="emphasis" href="#box.update" number="" targetptr="box.update"><ttl>???TITLE???</ttl><xreftext>???</xreftext></obj><obj element="emphasis" href="#box.delete" number="" targetptr="box.delete"><ttl>???TITLE???</ttl><xreftext>???</xreftext></obj><obj element="emphasis" href="#box.select_range" number="" targetptr="box.select_range"><ttl>???TITLE???</ttl><xreftext>box.select_range</xreftext></obj><obj element="emphasis" href="#box.select_reverse_range" number="" targetptr="box.select_reverse_range"><ttl>???TITLE???</ttl><xreftext>box.select_reverse_range</xreftext></obj></div><div element="section" href="#sp-box-tuple" number="" targetptr="sp-box-tuple"><ttl>Package <code class="code">box.tuple</code></ttl><xreftext>the section called “Package <code class="code">box.tuple</code>â€</xreftext><obj element="variablelist" href="#box.tuple" number="" targetptr="box.tuple"><ttl>???TITLE???</ttl><xreftext>box.tuple</xreftext></obj></div><div element="section" href="#sp-box-space" number="" targetptr="sp-box-space"><ttl>Package <code class="code">box.space</code></ttl><xreftext>the section called “Package <code class="code">box.space</code>â€</xreftext><obj element="variablelist" href="#box.space" number="" targetptr="box.space"><ttl>???TITLE???</ttl><xreftext>box.space</xreftext></obj><obj element="emphasis" href="#box.space.select_range" number="" targetptr="box.space.select_range"><ttl>???TITLE???</ttl><xreftext>box.space[i].select_range()</xreftext></obj><obj element="emphasis" href="#box.space.select_reverse_range" number="" targetptr="box.space.select_reverse_range"><ttl>???TITLE???</ttl><xreftext>box.space.select_reverse_range</xreftext></obj></div><div element="section" href="#sp-box-index" number="" targetptr="sp-box-index"><ttl>Package <code class="code">box.index</code></ttl><xreftext>the section called “Package <code class="code">box.index</code>â€</xreftext><obj element="variablelist" href="#box.index" number="" targetptr="box.index"><ttl>???TITLE???</ttl><xreftext>box.index</xreftext></obj><obj element="emphasis" href="#box.index.iterator" number="" targetptr="box.index.iterator"><ttl>???TITLE???</ttl><xreftext>box.index.iterator(type, ...)</xreftext></obj><obj element="para" href="#iterator-consistency" number="" targetptr="iterator-consistency"><ttl>???TITLE???</ttl><xreftext/></obj><obj element="para" href="#iterator-types" number="" targetptr="iterator-types"><ttl>???TITLE???</ttl><xreftext/></obj><obj element="table" href="#idp439728" number="4.1"><ttl>Common iterator types</ttl><xreftext>Table 4.1, “Common iterator typesâ€</xreftext></obj><obj element="table" href="#idp460720" number="4.2"><ttl>TREE iterator types</ttl><xreftext>Table 4.2, “TREE iterator typesâ€</xreftext></obj><obj element="table" href="#idp888096" number="4.3"><ttl>BITSET iterator types</ttl><xreftext>Table 4.3, “BITSET iterator typesâ€</xreftext></obj></div><div element="section" href="#sp-box-fiber" number="" targetptr="sp-box-fiber"><ttl>Package <code class="code">box.fiber</code></ttl><xreftext>the section called “Package <code class="code">box.fiber</code>â€</xreftext><obj element="variablelist" href="#box.fiber" number="" targetptr="box.fiber"><ttl>???TITLE???</ttl><xreftext>???TITLE???</xreftext></obj><obj element="emphasis" href="#box.fiber.yield" number="" targetptr="box.fiber.yield"><ttl>???TITLE???</ttl><xreftext>box.fiber.yield(...)</xreftext></obj></div><div element="section" href="#sp-box-session" number="" targetptr="sp-box-session"><ttl>Package <code class="code">box.session</code></ttl><xreftext>the section called “Package <code class="code">box.session</code>â€</xreftext></div><div element="section" href="#sp-box-ipc" number="" targetptr="sp-box-ipc"><ttl>Package <code class="code">box.ipc</code> — inter procedure communication</ttl><xreftext>the section called “Package <code class="code">box.ipc</code> — inter procedure communicationâ€</xreftext><obj element="variablelist" href="#box.ipc" number="" targetptr="box.ipc"><ttl>???TITLE???</ttl><xreftext>???TITLE???</xreftext></obj></div><div element="section" href="#sp-box-socket" number="" targetptr="sp-box-socket"><ttl>Package <code class="code">box.socket</code> — TCP and UDP sockets</ttl><xreftext>the section called “Package <code class="code">box.socket</code> — TCP and UDP socketsâ€</xreftext><obj element="variablelist" href="#box.socket" number="" targetptr="box.socket"><ttl>???TITLE???</ttl><xreftext>???TITLE???</xreftext></obj><obj element="table" href="#idp712288" number="4.4"><ttl><code class="code">readline()</code> returns</ttl><xreftext>Table 4.4, “<code class="code">readline()</code> returnsâ€</xreftext></obj></div><div element="section" href="#sp-box-net-box" number="" targetptr="sp-box-net-box"><ttl>Package <code class="code">box.net.box</code> — working with networked Tarantool peers</ttl><xreftext>the section called “Package <code class="code">box.net.box</code> — working with networked Tarantool peersâ€</xreftext><obj element="variablelist" href="#box.net.box" number="" targetptr="box.net.box"><ttl>???TITLE???</ttl><xreftext>???TITLE???</xreftext></obj><obj element="emphasis" href="#box.net.box.new" number="" targetptr="box.net.box.new"><ttl>???TITLE???</ttl><xreftext>???</xreftext></obj><obj element="emphasis" href="#box.net.box.ping" number="" targetptr="box.net.box.ping"><ttl>???TITLE???</ttl><xreftext>???</xreftext></obj><obj element="emphasis" href="#box.net.box.close" number="" targetptr="box.net.box.close"><ttl>???TITLE???</ttl><xreftext>???</xreftext></obj><obj element="emphasis" href="#box.net.box.select" number="" targetptr="box.net.box.select"><ttl>???TITLE???</ttl><xreftext>???</xreftext></obj><obj element="emphasis" href="#box.net.box.select_limit" number="" targetptr="box.net.box.select_limit"><ttl>???TITLE???</ttl><xreftext>???</xreftext></obj><obj element="emphasis" href="#box.net.box.select_range" number="" targetptr="box.net.box.select_range"><ttl>???TITLE???</ttl><xreftext>???</xreftext></obj><obj element="emphasis" href="#box.net.box.insert" number="" targetptr="box.net.box.insert"><ttl>???TITLE???</ttl><xreftext>???</xreftext></obj><obj element="emphasis" href="#box.net.box.replace" number="" targetptr="box.net.box.replace"><ttl>???TITLE???</ttl><xreftext>???</xreftext></obj><obj element="emphasis" href="#box.net.box.update" number="" targetptr="box.net.box.update"><ttl>???TITLE???</ttl><xreftext>???</xreftext></obj><obj element="emphasis" href="#box.net.box.delete" number="" targetptr="box.net.box.delete"><ttl>???TITLE???</ttl><xreftext>???</xreftext></obj><obj element="emphasis" href="#box.net.box.call" number="" targetptr="box.net.box.call"><ttl>???TITLE???</ttl><xreftext>???</xreftext></obj><obj element="emphasis" href="#box.net.box.timeout" number="" targetptr="box.net.box.timeout"><ttl>???TITLE???</ttl><xreftext>???</xreftext></obj></div><div element="section" href="#sp-box-cfg" number="" targetptr="sp-box-cfg"><ttl>Packages <code class="code">box.cfg</code>, <code class="code">box.info</code>, <code class="code">box.slab</code> and <code class="code">box.stat</code>: server introspection</ttl><xreftext>the section called “Packages <code class="code">box.cfg</code>, <code class="code">box.info</code>, <code class="code">box.slab</code> and - <code class="code">box.stat</code>: server introspectionâ€</xreftext><obj element="code" href="#box.cfg" number="" targetptr="box.cfg"><ttl>???TITLE???</ttl><xreftext>???</xreftext></obj><obj element="code" href="#box.stat" number="" targetptr="box.stat"><ttl>???TITLE???</ttl><xreftext>???</xreftext></obj></div><div element="section" href="#sp-limitations" number="" targetptr="sp-limitations"><ttl>Limitation of stored procedures</ttl><xreftext>the section called “Limitation of stored proceduresâ€</xreftext></div></div><div element="section" href="#triggers" number="" targetptr="triggers"><ttl>Defining triggers in Lua</ttl><xreftext>the section called “Defining triggers in Luaâ€</xreftext><div element="section" href="#sp-box-session-triggers" number="" targetptr="sp-box-session-triggers"><ttl>Triggers on connect and disconnect</ttl><xreftext>session triggers</xreftext></div></div></div><div element="chapter" href="#replication" number="5" targetptr="replication"><ttl>Replication</ttl><xreftext>Chapter 5, <i>Replication</i></xreftext><div element="section" href="#replication-architecture" number="" targetptr="replication-architecture"><ttl>Replication architecture</ttl><xreftext>the section called “Replication architectureâ€</xreftext></div><div element="section" href="#setting-up-the-master" number="" targetptr="setting-up-the-master"><ttl>Setting up the master</ttl><xreftext>the section called “Setting up the masterâ€</xreftext></div><div element="section" href="#settin-up-a-replica" number="" targetptr="settin-up-a-replica"><ttl>Setting up a replica</ttl><xreftext>the section called “Setting up a replicaâ€</xreftext></div><div element="section" href="#recovering-from-a-degraded-state" number="" targetptr="recovering-from-a-degraded-state"><ttl>Recovering from a degraded state</ttl><xreftext>the section called “Recovering from a degraded stateâ€</xreftext></div></div><div element="chapter" href="#configuration-reference" number="6" targetptr="configuration-reference"><ttl>Configuration reference</ttl><xreftext>Chapter 6, <i>Configuration reference</i></xreftext><div element="section" href="#command-line-options" number="" targetptr="command-line-options"><ttl>Command line options</ttl><xreftext>the section called “Command line optionsâ€</xreftext><obj element="listitem" href="#help-option" number="" targetptr="help-option"><ttl>???TITLE???</ttl><xreftext/></obj><obj element="listitem" href="#version-option" number="" targetptr="version-option"><ttl>???TITLE???</ttl><xreftext/></obj><obj element="listitem" href="#config-option" number="" targetptr="config-option"><ttl>???TITLE???</ttl><xreftext/></obj><obj element="option" href="#init-storage-option" number="" targetptr="init-storage-option"><ttl>???TITLE???</ttl><xreftext>--init-storage</xreftext></obj><obj element="option" href="#cat-option" number="" targetptr="cat-option"><ttl>???TITLE???</ttl><xreftext>--cat</xreftext></obj></div><div element="section" href="#option-file" number="" targetptr="option-file"><ttl>The option file</ttl><xreftext>option file</xreftext><obj element="table" href="#idp1180064" number="6.1"><ttl>Basic parameters</ttl><xreftext>Table 6.1, “Basic parametersâ€</xreftext></obj><obj element="entry" href="#work_dir" number="" targetptr="work_dir"><ttl>???TITLE???</ttl><xreftext>work_dir</xreftext></obj><obj element="entry" href="#wal_dir" number="" targetptr="wal_dir"><ttl>???TITLE???</ttl><xreftext>wal_dir</xreftext></obj><obj element="entry" href="#snap_dir" number="" targetptr="snap_dir"><ttl>???TITLE???</ttl><xreftext>snap_dir</xreftext></obj><obj element="entry" href="#bind_ipaddr" number="" targetptr="bind_ipaddr"><ttl>???TITLE???</ttl><xreftext>bind_ipaddr</xreftext></obj><obj element="entry" href="#primary_port" number="" targetptr="primary_port"><ttl>???TITLE???</ttl><xreftext>primary_port</xreftext></obj><obj element="entry" href="#secondary_port" number="" targetptr="secondary_port"><ttl>???TITLE???</ttl><xreftext>secondary_port</xreftext></obj><obj element="entry" href="#admin_port" number="" targetptr="admin_port"><ttl>???TITLE???</ttl><xreftext>admin_port</xreftext></obj><obj element="entry" href="#custom_proc_title" number="" targetptr="custom_proc_title"><ttl>???TITLE???</ttl><xreftext>custom_proc_title</xreftext></obj><obj element="table" href="#idp1233728" number="6.2"><ttl>Configuring the storage</ttl><xreftext>Table 6.2, “Configuring the storageâ€</xreftext></obj><obj element="anchor" href="#slab_alloc_arena" number="" targetptr="slab_alloc_arena"><ttl>???TITLE???</ttl><xreftext>slab_alloc_arena</xreftext></obj><obj element="para" href="#space" number="" targetptr="space"><ttl>???TITLE???</ttl><xreftext>the section called “The option fileâ€</xreftext></obj><obj element="table" href="#idp1269856" number="6.3"><ttl>Binary logging and snapshots</ttl><xreftext>Table 6.3, “Binary logging and snapshotsâ€</xreftext></obj><obj element="entry" href="#rows_per_wal" number="" targetptr="rows_per_wal"><ttl>???TITLE???</ttl><xreftext>rows_per_wal</xreftext></obj><obj element="entry" href="#wal_mode" number="" targetptr="wal_mode"><ttl>???TITLE???</ttl><xreftext>wal_mode</xreftext></obj><obj element="table" href="#idp1305184" number="6.4"><ttl>Replication</ttl><xreftext>Table 6.4, “Replicationâ€</xreftext></obj><obj element="entry" href="#replication_port" number="" targetptr="replication_port"><ttl>???TITLE???</ttl><xreftext>replication_port</xreftext></obj><obj element="entry" href="#replication_source" number="" targetptr="replication_source"><ttl>???TITLE???</ttl><xreftext>replication_source</xreftext></obj><obj element="table" href="#idp1324288" number="6.5"><ttl>Networking</ttl><xreftext>Table 6.5, “Networkingâ€</xreftext></obj><obj element="table" href="#idp1343056" number="6.6"><ttl>Logging</ttl><xreftext>Table 6.6, “Loggingâ€</xreftext></obj><obj element="table" href="#idp1367952" number="6.7"><ttl>Memcached protocol support</ttl><xreftext>Table 6.7, “Memcached protocol supportâ€</xreftext></obj><obj element="anchor" href="#memcached_port" number="" targetptr="memcached_port"><ttl>???TITLE???</ttl><xreftext>memcached_port</xreftext></obj><obj element="anchor" href="#memcached_space" number="" targetptr="memcached_space"><ttl>???TITLE???</ttl><xreftext>memcached_space</xreftext></obj><obj element="anchor" href="#memcached_expire" number="" targetptr="memcached_expire"><ttl>???TITLE???</ttl><xreftext>memcached_expire</xreftext></obj></div></div><div element="chapter" href="#connectors" number="7" targetptr="connectors"><ttl>Connectors</ttl><xreftext>Chapter 7, <i>Connectors</i></xreftext><div element="section" href="#connector-c" number="" targetptr="connector-c"><ttl>C</ttl><xreftext>the section called “Câ€</xreftext></div><div element="section" href="#connector-node.js" number="" targetptr="connector-node.js"><ttl>node.js</ttl><xreftext>the section called “node.jsâ€</xreftext></div><div element="section" href="#connector-perl" number="" targetptr="connector-perl"><ttl>Perl</ttl><xreftext>the section called “Perlâ€</xreftext></div><div element="section" href="#connector-php" number="" targetptr="connector-php"><ttl>PHP</ttl><xreftext>the section called “PHPâ€</xreftext></div><div element="section" href="#connector-python" number="" targetptr="connector-python"><ttl>Python</ttl><xreftext>the section called “Pythonâ€</xreftext></div><div element="section" href="#connector-ruby" number="" targetptr="connector-ruby"><ttl>Ruby</ttl><xreftext>the section called “Rubyâ€</xreftext></div></div><div element="chapter" href="#os-install-notes" number="8" targetptr="os-install-notes"><ttl>System-specific installation notes</ttl><xreftext>Chapter 8, <i>System-specific installation notes</i></xreftext><div element="section" href="#Debian" number="" targetptr="Debian"><ttl>Debian GNU/Linux and Ubuntu</ttl><xreftext>the section called “Debian GNU/Linux and Ubuntuâ€</xreftext></div><div element="section" href="#rpm-based-distros" number="" targetptr="rpm-based-distros"><ttl>Fedora, RHEL, CentOS</ttl><xreftext>the section called “Fedora, RHEL, CentOSâ€</xreftext></div><div element="section" href="#FreeBSD" number="" targetptr="FreeBSD"><ttl>FreeBSD</ttl><xreftext>the section called “FreeBSDâ€</xreftext></div><div element="section" href="#mac-os-x" number="" targetptr="mac-os-x"><ttl>Mac OS X</ttl><xreftext>the section called “Mac OS Xâ€</xreftext></div></div><div element="appendix" href="#proctitle" number="A" targetptr="proctitle"><ttl>Server process titles</ttl><xreftext>Appendix A, <i>Server process titles</i></xreftext></div><div element="appendix" href="#errcode" number="B" targetptr="errcode"><ttl>List of error codes</ttl><xreftext>Appendix B, <i>List of error codes</i></xreftext><obj element="term" href="#ER_NONMASTER" number="" targetptr="ER_NONMASTER"><ttl>???TITLE???</ttl><xreftext>ER_NONMASTER</xreftext></obj><obj element="term" href="#ER_ILLEGAL_PARAMS" number="" targetptr="ER_ILLEGAL_PARAMS"><ttl>???TITLE???</ttl><xreftext>ER_ILLEGAL_PARAMS</xreftext></obj><obj element="term" href="#ER_TUPLE_IS_RO" number="" targetptr="ER_TUPLE_IS_RO"><ttl>???TITLE???</ttl><xreftext>ER_TUPLE_IS_RO</xreftext></obj><obj element="term" href="#ER_MEMORY_ISSUE" number="" targetptr="ER_MEMORY_ISSUE"><ttl>???TITLE???</ttl><xreftext>ER_MEMORY_ISSUE</xreftext></obj><obj element="term" href="#ER_WAL_IO" number="" targetptr="ER_WAL_IO"><ttl>???TITLE???</ttl><xreftext>ER_WAL_IO</xreftext></obj><obj element="term" href="#ER_INDEX_VIOLATION" number="" targetptr="ER_INDEX_VIOLATION"><ttl>???TITLE???</ttl><xreftext>ER_INDEX_VIOLATION</xreftext></obj><obj element="term" href="#ER_KEY_PART_COUNT" number="" targetptr="ER_KEY_PART_COUNT"><ttl>???TITLE???</ttl><xreftext>ER_KEY_PART_COUNT</xreftext></obj><obj element="term" href="#ER_NO_SUCH_SPACE" number="" targetptr="ER_NO_SUCH_SPACE"><ttl>???TITLE???</ttl><xreftext>ER_NO_SUCH_SPACE</xreftext></obj><obj element="term" href="#ER_NO_SUCH_INDEX" number="" targetptr="ER_NO_SUCH_INDEX"><ttl>???TITLE???</ttl><xreftext>ER_NO_SUCH_INDEX</xreftext></obj><obj element="term" href="#ER_PROC_LUA" number="" targetptr="ER_PROC_LUA"><ttl>???TITLE???</ttl><xreftext>ER_PROC_LUA</xreftext></obj></div></div> + <code class="code">box.stat</code>: server introspectionâ€</xreftext><obj element="code" href="#box.cfg" number="" targetptr="box.cfg"><ttl>???TITLE???</ttl><xreftext>???</xreftext></obj><obj element="code" href="#box.stat" number="" targetptr="box.stat"><ttl>???TITLE???</ttl><xreftext>???</xreftext></obj></div><div element="section" href="#sp-limitations" number="" targetptr="sp-limitations"><ttl>Limitation of stored procedures</ttl><xreftext>the section called “Limitation of stored proceduresâ€</xreftext></div></div><div element="section" href="#triggers" number="" targetptr="triggers"><ttl>Defining triggers in Lua</ttl><xreftext>the section called “Defining triggers in Luaâ€</xreftext><div element="section" href="#sp-box-session-triggers" number="" targetptr="sp-box-session-triggers"><ttl>Triggers on connect and disconnect</ttl><xreftext>session triggers</xreftext></div></div></div><div element="chapter" href="#replication" number="5" targetptr="replication"><ttl>Replication</ttl><xreftext>Chapter 5, <i>Replication</i></xreftext><div element="section" href="#replication-architecture" number="" targetptr="replication-architecture"><ttl>Replication architecture</ttl><xreftext>the section called “Replication architectureâ€</xreftext></div><div element="section" href="#setting-up-the-master" number="" targetptr="setting-up-the-master"><ttl>Setting up the master</ttl><xreftext>the section called “Setting up the masterâ€</xreftext></div><div element="section" href="#settin-up-a-replica" number="" targetptr="settin-up-a-replica"><ttl>Setting up a replica</ttl><xreftext>the section called “Setting up a replicaâ€</xreftext></div><div element="section" href="#recovering-from-a-degraded-state" number="" targetptr="recovering-from-a-degraded-state"><ttl>Recovering from a degraded state</ttl><xreftext>the section called “Recovering from a degraded stateâ€</xreftext></div></div><div element="chapter" href="#server-administration" number="6" targetptr="server-administration"><ttl>Server administration</ttl><xreftext>Chapter 6, <i>Server administration</i></xreftext><div element="section" href="#signal-handling" number="" targetptr="signal-handling"><ttl>Server signal handling</ttl><xreftext>the section called “Server signal handlingâ€</xreftext></div><div element="section" href="#os-install-notes" number="" targetptr="os-install-notes"><ttl>System-specific administration notes</ttl><xreftext>the section called “System-specific administration notesâ€</xreftext><div element="section" href="#Debian" number="" targetptr="Debian"><ttl>Debian GNU/Linux and Ubuntu</ttl><xreftext>the section called “Debian GNU/Linux and Ubuntuâ€</xreftext></div><div element="section" href="#rpm-based-distros" number="" targetptr="rpm-based-distros"><ttl>Fedora, RHEL, CentOS</ttl><xreftext>the section called “Fedora, RHEL, CentOSâ€</xreftext></div><div element="section" href="#FreeBSD" number="" targetptr="FreeBSD"><ttl>FreeBSD</ttl><xreftext>the section called “FreeBSDâ€</xreftext></div><div element="section" href="#mac-os-x" number="" targetptr="mac-os-x"><ttl>Mac OS X</ttl><xreftext>the section called “Mac OS Xâ€</xreftext></div></div></div><div element="chapter" href="#configuration-reference" number="7" targetptr="configuration-reference"><ttl>Configuration reference</ttl><xreftext>Chapter 7, <i>Configuration reference</i></xreftext><div element="section" href="#command-line-options" number="" targetptr="command-line-options"><ttl>Command line options</ttl><xreftext>the section called “Command line optionsâ€</xreftext><obj element="listitem" href="#help-option" number="" targetptr="help-option"><ttl>???TITLE???</ttl><xreftext/></obj><obj element="listitem" href="#version-option" number="" targetptr="version-option"><ttl>???TITLE???</ttl><xreftext/></obj><obj element="listitem" href="#config-option" number="" targetptr="config-option"><ttl>???TITLE???</ttl><xreftext/></obj><obj element="option" href="#init-storage-option" number="" targetptr="init-storage-option"><ttl>???TITLE???</ttl><xreftext>--init-storage</xreftext></obj></div><div element="section" href="#option-file" number="" targetptr="option-file"><ttl>The option file</ttl><xreftext>option file</xreftext><obj element="table" href="#idp1279216" number="7.1"><ttl>Basic parameters</ttl><xreftext>Table 7.1, “Basic parametersâ€</xreftext></obj><obj element="entry" href="#work_dir" number="" targetptr="work_dir"><ttl>???TITLE???</ttl><xreftext>work_dir</xreftext></obj><obj element="entry" href="#wal_dir" number="" targetptr="wal_dir"><ttl>???TITLE???</ttl><xreftext>wal_dir</xreftext></obj><obj element="entry" href="#snap_dir" number="" targetptr="snap_dir"><ttl>???TITLE???</ttl><xreftext>snap_dir</xreftext></obj><obj element="entry" href="#bind_ipaddr" number="" targetptr="bind_ipaddr"><ttl>???TITLE???</ttl><xreftext>bind_ipaddr</xreftext></obj><obj element="entry" href="#primary_port" number="" targetptr="primary_port"><ttl>???TITLE???</ttl><xreftext>primary_port</xreftext></obj><obj element="entry" href="#secondary_port" number="" targetptr="secondary_port"><ttl>???TITLE???</ttl><xreftext>secondary_port</xreftext></obj><obj element="entry" href="#admin_port" number="" targetptr="admin_port"><ttl>???TITLE???</ttl><xreftext>admin_port</xreftext></obj><obj element="entry" href="#custom_proc_title" number="" targetptr="custom_proc_title"><ttl>???TITLE???</ttl><xreftext>custom_proc_title</xreftext></obj><obj element="table" href="#idp1332880" number="7.2"><ttl>Configuring the storage</ttl><xreftext>Table 7.2, “Configuring the storageâ€</xreftext></obj><obj element="anchor" href="#slab_alloc_arena" number="" targetptr="slab_alloc_arena"><ttl>???TITLE???</ttl><xreftext>slab_alloc_arena</xreftext></obj><obj element="para" href="#space" number="" targetptr="space"><ttl>???TITLE???</ttl><xreftext>the section called “The option fileâ€</xreftext></obj><obj element="table" href="#idp1368816" number="7.3"><ttl>Binary logging and snapshots</ttl><xreftext>Table 7.3, “Binary logging and snapshotsâ€</xreftext></obj><obj element="entry" href="#rows_per_wal" number="" targetptr="rows_per_wal"><ttl>???TITLE???</ttl><xreftext>rows_per_wal</xreftext></obj><obj element="entry" href="#wal_mode" number="" targetptr="wal_mode"><ttl>???TITLE???</ttl><xreftext>wal_mode</xreftext></obj><obj element="table" href="#idp1404208" number="7.4"><ttl>Replication</ttl><xreftext>Table 7.4, “Replicationâ€</xreftext></obj><obj element="entry" href="#replication_port" number="" targetptr="replication_port"><ttl>???TITLE???</ttl><xreftext>replication_port</xreftext></obj><obj element="entry" href="#replication_source" number="" targetptr="replication_source"><ttl>???TITLE???</ttl><xreftext>replication_source</xreftext></obj><obj element="table" href="#idp1423248" number="7.5"><ttl>Networking</ttl><xreftext>Table 7.5, “Networkingâ€</xreftext></obj><obj element="table" href="#idp1442576" number="7.6"><ttl>Logging</ttl><xreftext>Table 7.6, “Loggingâ€</xreftext></obj><obj element="table" href="#idp1467472" number="7.7"><ttl>Memcached protocol support</ttl><xreftext>Table 7.7, “Memcached protocol supportâ€</xreftext></obj><obj element="anchor" href="#memcached_port" number="" targetptr="memcached_port"><ttl>???TITLE???</ttl><xreftext>memcached_port</xreftext></obj><obj element="anchor" href="#memcached_space" number="" targetptr="memcached_space"><ttl>???TITLE???</ttl><xreftext>memcached_space</xreftext></obj><obj element="anchor" href="#memcached_expire" number="" targetptr="memcached_expire"><ttl>???TITLE???</ttl><xreftext>memcached_expire</xreftext></obj></div></div><div element="chapter" href="#connectors" number="8" targetptr="connectors"><ttl>Connectors</ttl><xreftext>Chapter 8, <i>Connectors</i></xreftext><div element="section" href="#connector-c" number="" targetptr="connector-c"><ttl>C</ttl><xreftext>the section called “Câ€</xreftext></div><div element="section" href="#connector-node.js" number="" targetptr="connector-node.js"><ttl>node.js</ttl><xreftext>the section called “node.jsâ€</xreftext></div><div element="section" href="#connector-perl" number="" targetptr="connector-perl"><ttl>Perl</ttl><xreftext>the section called “Perlâ€</xreftext></div><div element="section" href="#connector-php" number="" targetptr="connector-php"><ttl>PHP</ttl><xreftext>the section called “PHPâ€</xreftext></div><div element="section" href="#connector-python" number="" targetptr="connector-python"><ttl>Python</ttl><xreftext>the section called “Pythonâ€</xreftext></div><div element="section" href="#connector-ruby" number="" targetptr="connector-ruby"><ttl>Ruby</ttl><xreftext>the section called “Rubyâ€</xreftext></div></div><div element="appendix" href="#proctitle" number="A" targetptr="proctitle"><ttl>Server process titles</ttl><xreftext>Appendix A, <i>Server process titles</i></xreftext></div><div element="appendix" href="#errcode" number="B" targetptr="errcode"><ttl>List of error codes</ttl><xreftext>Appendix B, <i>List of error codes</i></xreftext><obj element="term" href="#ER_NONMASTER" number="" targetptr="ER_NONMASTER"><ttl>???TITLE???</ttl><xreftext>ER_NONMASTER</xreftext></obj><obj element="term" href="#ER_ILLEGAL_PARAMS" number="" targetptr="ER_ILLEGAL_PARAMS"><ttl>???TITLE???</ttl><xreftext>ER_ILLEGAL_PARAMS</xreftext></obj><obj element="term" href="#ER_MEMORY_ISSUE" number="" targetptr="ER_MEMORY_ISSUE"><ttl>???TITLE???</ttl><xreftext>ER_MEMORY_ISSUE</xreftext></obj><obj element="term" href="#ER_WAL_IO" number="" targetptr="ER_WAL_IO"><ttl>???TITLE???</ttl><xreftext>ER_WAL_IO</xreftext></obj><obj element="term" href="#ER_INDEX_VIOLATION" number="" targetptr="ER_INDEX_VIOLATION"><ttl>???TITLE???</ttl><xreftext>ER_INDEX_VIOLATION</xreftext></obj><obj element="term" href="#ER_KEY_PART_COUNT" number="" targetptr="ER_KEY_PART_COUNT"><ttl>???TITLE???</ttl><xreftext>ER_KEY_PART_COUNT</xreftext></obj><obj element="term" href="#ER_NO_SUCH_SPACE" number="" targetptr="ER_NO_SUCH_SPACE"><ttl>???TITLE???</ttl><xreftext>ER_NO_SUCH_SPACE</xreftext></obj><obj element="term" href="#ER_NO_SUCH_INDEX" number="" targetptr="ER_NO_SUCH_INDEX"><ttl>???TITLE???</ttl><xreftext>ER_NO_SUCH_INDEX</xreftext></obj><obj element="term" href="#ER_PROC_LUA" number="" targetptr="ER_PROC_LUA"><ttl>???TITLE???</ttl><xreftext>ER_PROC_LUA</xreftext></obj></div></div> diff --git a/include/errcode.h b/include/errcode.h index b0dac16fb879226014fd6a3b0a52a2f0d03405df..2675b113e67590323243c9fda77cbdea93d5cc4f 100644 --- a/include/errcode.h +++ b/include/errcode.h @@ -78,7 +78,7 @@ enum { TNT_ERRMSG_MAX = 512 }; /* end of silverproxy error codes */ \ /* 24 */_(ER_UNUSED24, 2, "Unused24") \ /* 25 */_(ER_TUPLE_IS_EMPTY, 2, "UPDATE error: the new tuple has no fields") \ - /* 26 */_(ER_UNUSED26, 2, "Unused26") \ + /* 26 */_(ER_FIBER_STACK, 2, "Can not create a new fiber: recursion limit reached") \ /* 27 */_(ER_UNUSED27, 2, "Unused27") \ /* 28 */_(ER_UNUSED28, 2, "Unused28") \ /* 29 */_(ER_UNUSED29, 2, "Unused29") \ diff --git a/include/fiber.h b/include/fiber.h index 49dda87f2f3fd0d6a075271ca481c6b3978b2b30..a78a677e5fc07386dd14bf800a9598a9c9fc318b 100644 --- a/include/fiber.h +++ b/include/fiber.h @@ -106,6 +106,9 @@ fiber_name(struct fiber *f) return palloc_name(f->gc_pool); } +void +fiber_checkstack(); + void fiber_yield(void); void fiber_yield_to(struct fiber *f); diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt index bb02aaa38b9e6d57a441fb4f5e689cd77f9b8345..18d03b8993f1aa56a5500dd3662206ee6a38d724 100644 --- a/src/box/CMakeLists.txt +++ b/src/box/CMakeLists.txt @@ -18,7 +18,9 @@ function(lua_source filename) PARENT_SCOPE) endfunction() -lua_source(box.lua) +lua_source(lua/box.lua) +lua_source(lua/box_net.lua) +lua_source(lua/misc.lua) add_custom_target(generate_lua_sources} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/src/box diff --git a/src/box/box_lua.m b/src/box/box_lua.m index ed7741594bbaee1d956ac6bd430b4121cd7eecbc..9ed9c8ffa654a2d324a28db9570937687859dec9 100644 --- a/src/box/box_lua.m +++ b/src/box/box_lua.m @@ -47,8 +47,9 @@ #include "port.h" #include "tbuf.h" -/* contents of box.lua */ -extern const char box_lua[]; +/* contents of box.lua, misc.lua, box.net.lua respectively */ +extern char box_lua[], box_net_lua[], misc_lua[]; +const char *lua_sources[] = { box_lua, box_net_lua, misc_lua, NULL }; /** * All box connections share the same Lua state. We use @@ -1276,6 +1277,24 @@ box_lua_find(lua_State *L, const char *name, const char *name_end) } } + +/** + * A helper to find lua stored procedures for box.call. + * box.call iteslf is pure Lua, to avoid issues + * with infinite call recursion smashing C + * thread stack. + */ + +static int +lbox_call_loadproc(struct lua_State *L) +{ + const char *name; + size_t name_len; + name = lua_tolstring(L, 1, &name_len); + box_lua_find(L, name, name + name_len); + return 1; +} + /** * Invoke a Lua stored procedure from the binary protocol * (implementation of 'CALL' command code). @@ -1611,6 +1630,29 @@ lbox_pack(struct lua_State *L) return 1; } +const char * +box_unpack_response(struct lua_State *L, const void *s, const void *end) +{ + u32 tuple_count = pick_u32(&s, end); + + /* Unpack and push tuples. */ + while (tuple_count--) { + u32 bsize = pick_u32(&s, end); + u32 field_count = pick_u32(&s, end); + if (valid_tuple(s, end, field_count) != bsize) + luaL_error(L, "box.unpack(): can't unpack tuple"); + + struct tuple *t = tuple_alloc(bsize); + t->field_count = field_count; + memcpy(t->data, s, bsize); + + s += bsize; + lbox_pushtuple(L, t); + } + return s; +} + + static int lbox_unpack(struct lua_State *L) { @@ -1623,7 +1665,7 @@ lbox_unpack(struct lua_State *L) const void *end = str + str_size; const void *s = str; - int i = 0; + int save_stacksize = lua_gettop(L); char charbuf; u8 u8buf; @@ -1664,6 +1706,12 @@ lbox_unpack(struct lua_State *L) u32buf = pick_varint32(&s, end); lua_pushnumber(L, u32buf); break; + + case 'a': + case 'A': /* The rest of the data is a Lua string. */ + lua_pushlstring(L, s, end - s); + s = end; + break; case 'P': case 'p': /* pick_varint32 throws exception on error. */ @@ -1709,11 +1757,16 @@ lbox_unpack(struct lua_State *L) lua_pushnumber(L, u32buf); s += 5; break; + + case 'R': /* Unpack server response, IPROTO format. */ + { + s = box_unpack_response(L, s, end); + break; + } default: luaL_error(L, "box.unpack: unsupported " "format specifier '%c'", *f); } - i++; f++; } @@ -1725,13 +1778,14 @@ lbox_unpack(struct lua_State *L) format, s - str, str_size); } - return i; + return lua_gettop(L) - save_stacksize; #undef CHECK_SIZE } static const struct luaL_reg boxlib[] = { {"process", lbox_process}, + {"call_loadproc", lbox_call_loadproc}, {"raise", lbox_raise}, {"pack", lbox_pack}, {"unpack", lbox_unpack}, @@ -1753,9 +1807,13 @@ mod_lua_init(struct lua_State *L) box_index_init_iterator_types(L, -2); lua_pop(L, 1); tarantool_lua_register_type(L, iteratorlib_name, lbox_iterator_meta); - /* Load box.lua */ - if (luaL_dostring(L, box_lua)) - panic("Error loading box.lua: %s", lua_tostring(L, -1)); + + /* Load Lua extension */ + for (const char **s = lua_sources; *s; s++) { + if (luaL_dostring(L, *s)) + panic("Error loading Lua source %.160s...: %s", + *s, lua_tostring(L, -1)); + } assert(lua_gettop(L) == 0); diff --git a/src/box/box.lua b/src/box/lua/box.lua similarity index 57% rename from src/box/box.lua rename to src/box/lua/box.lua index 49e4651a7577149f84681e47b14e6afc9066d986..a84c8126d069e7c74b0cde8a59f9216e2755841c 100644 --- a/src/box/box.lua +++ b/src/box/lua/box.lua @@ -1,41 +1,20 @@ box.flags = { BOX_RETURN_TUPLE = 0x01, BOX_ADD = 0x02, BOX_REPLACE = 0x04 } + + -- -- -- function box.select_limit(space, index, offset, limit, ...) - local key_part_count = select('#', ...) - return box.process(17, - box.pack('iiiiiV', - space, - index, - offset, - limit, - 1, -- key count - key_part_count, ...)) + return box.net.self:select_limit(space, index, offset, limit, ...) end -function box.dostring(s, ...) - local chunk, message = loadstring(s) - if chunk == nil then - error(message, 2) - end - return chunk(...) -end -- -- -- function box.select(space, index, ...) - local key_part_count = select('#', ...) - return box.process(17, - box.pack('iiiiiV', - space, - index, - 0, -- offset - 4294967295, -- limit - 1, -- key count - key_part_count, ...)) + return box.net.self:select(space, index, ...) end -- @@ -44,7 +23,7 @@ end -- starts from the key. -- function box.select_range(sno, ino, limit, ...) - return box.space[tonumber(sno)].index[tonumber(ino)]:select_range(tonumber(limit), ...) + return box.net.self:select_range(sno, ino, limit, ...) end -- @@ -53,7 +32,7 @@ end -- starts from the key. -- function box.select_reverse_range(sno, ino, limit, ...) - return box.space[tonumber(sno)].index[tonumber(ino)]:select_reverse_range(tonumber(limit), ...) + return box.net.self:select_reverse_range(sno, ino, limit, ...) end -- @@ -61,113 +40,31 @@ end -- index is always 0. It doesn't accept compound keys -- function box.delete(space, ...) - local key_part_count = select('#', ...) - return box.process(21, - box.pack('iiV', - space, - box.flags.BOX_RETURN_TUPLE, -- flags - key_part_count, ...)) + return box.net.self:delete(space, ...) end -- insert or replace a tuple function box.replace(space, ...) - local field_count = select('#', ...) - return box.process(13, - box.pack('iiV', - space, - box.flags.BOX_RETURN_TUPLE, -- flags - field_count, ...)) + return box.net.self:replace(space, ...) end -- insert a tuple (produces an error if the tuple already exists) function box.insert(space, ...) - local field_count = select('#', ...) - return box.process(13, - box.pack('iiV', - space, - bit.bor(box.flags.BOX_RETURN_TUPLE, - box.flags.BOX_ADD), -- flags - field_count, ...)) + return box.net.self:insert(space, ...) end -- function box.update(space, key, format, ...) - local op_count = select('#', ...)/2 - return box.process(19, - box.pack('iiVi'..format, - space, - box.flags.BOX_RETURN_TUPLE, - 1, key, - op_count, - ...)) -end - --- Assumes that spaceno has a TREE int32 (NUM) or int64 (NUM64) primary key --- inserts a tuple after getting the next value of the --- primary key and returns it back to the user -function box.auto_increment(spaceno, ...) - spaceno = tonumber(spaceno) - local max_tuple = box.space[spaceno].index[0].idx:max() - local max = 0 - if max_tuple ~= nil then - max = max_tuple[0] - local fmt = 'i' - if #max == 8 then fmt = 'l' end - max = box.unpack(fmt, max) - else - -- first time - if box.space[spaceno].index[0].key_field[0].type == "NUM64" then - max = tonumber64(max) - end - end - return box.insert(spaceno, max + 1, ...) -end - --- --- Simple counter. --- -box.counter = {} - --- --- Increment counter identified by primary key. --- Create counter if not exists. --- Returns updated value of the counter. --- -function box.counter.inc(space, ...) - local key = {...} - local cnt_index = #key - - local tuple - while true do - tuple = box.update(space, key, '+p', cnt_index, 1) - if tuple ~= nil then break end - local data = {...} - table.insert(data, 1) - tuple = box.insert(space, unpack(data)) - if tuple ~= nil then break end - end - - return box.unpack('i', tuple[cnt_index]) + return box.net.self:update(space, key, format, ...) end --- --- Decrement counter identified by primary key. --- Delete counter if it decreased to zero. --- Returns updated value of the counter. --- -function box.counter.dec(space, ...) - local key = {...} - local cnt_index = #key - local tuple = box.select(space, 0, ...) - if tuple == nil then return 0 end - if box.unpack('i', tuple[cnt_index]) == 1 then - box.delete(space, ...) - return 0 - else - tuple = box.update(space, key, '-p', cnt_index, 1) - return box.unpack('i', tuple[cnt_index]) +function box.dostring(s, ...) + local chunk, message = loadstring(s) + if chunk == nil then + error(message, 2) end + return chunk(...) end function box.bless_space(space) diff --git a/src/box/lua/box_net.lua b/src/box/lua/box_net.lua new file mode 100644 index 0000000000000000000000000000000000000000..f7624b182b1d14661c43d19679f037510deb237c --- /dev/null +++ b/src/box/lua/box_net.lua @@ -0,0 +1,424 @@ +box.net = { +-- +-- The idea of box.net.box implementation is that +-- most calls are simply wrappers around 'process' +-- function. The embedded 'process' function sends +-- requests to the local server, the remote 'process' +-- routes requests to a remote. +-- + box = { + delete = function(self, space, ...) + local key_part_count = select('#', ...) + return self:process(21, + box.pack('iiV', + space, + box.flags.BOX_RETURN_TUPLE, -- flags + key_part_count, ...)) + end, + + replace = function(self, space, ...) + local field_count = select('#', ...) + return self:process(13, + box.pack('iiV', + space, + box.flags.BOX_RETURN_TUPLE, -- flags + field_count, ...)) + end, + + -- insert a tuple (produces an error if the tuple already exists) + insert = function(self, space, ...) + local field_count = select('#', ...) + return self:process(13, + box.pack('iiV', + space, + bit.bor(box.flags.BOX_RETURN_TUPLE, + box.flags.BOX_ADD), -- flags + field_count, ...)) + end, + + -- update a tuple + update = function(self, space, key, format, ...) + local op_count = select('#', ...)/2 + return self:process(19, + box.pack('iiVi'..format, + space, + box.flags.BOX_RETURN_TUPLE, + 1, key, + op_count, + ...)) + end, + + select_limit = function(self, space, index, offset, limit, ...) + local key_part_count = select('#', ...) + return self:process(17, + box.pack('iiiiiV', + space, + index, + offset, + limit, + 1, -- key count + key_part_count, ...)) + end, + + select = function(self, space, index, ...) + local key_part_count = select('#', ...) + return self:process(17, + box.pack('iiiiiV', + space, + index, + 0, -- offset + 4294967295, -- limit + 1, -- key count + key_part_count, ...)) + end, + + + ping = function(self) + return self:process(65280, '') + end, + + call = function(self, proc_name, ...) + local count = select('#', ...) + return self:process(22, + box.pack('iwaV', + 0, -- flags + string.len(proc_name), + proc_name, + count, + ...)) + end, + + select_range = function(self, sno, ino, limit, ...) + return self:call( + 'box.select_range', + tostring(sno), + tostring(ino), + tostring(limit), + ... + ) + end, + + select_reverse_range = function(self, sno, ino, limit, ...) + return self:call( + 'box.select_reverse_range', + tostring(sno), + tostring(ino), + tostring(limit), + ... + ) + end, + + -- To make use of timeouts safe across multiple + -- concurrent fibers do not store timeouts as + -- part of conection state, but put it inside + -- a helper object. + + timeout = function(self, timeout) + + local wrapper = {} + + setmetatable(wrapper, { + __index = function(wrp, name, ...) + local func = self[name] + if func ~= nil then + return + function(wr, ...) + self.request_timeout = timeout + return func(self, ...) + end + end + + error(string.format('Can not find "box.net.box.%s" function', + name)) + end + }); + + return wrapper + end, + }, + + + -- local tarantool + self = { + process = function(self, ...) + return box.process(...) + end, + + select_range = function(self, sno, ino, limit, ...) + return box.space[tonumber(sno)].index[tonumber(ino)] + :select_range(tonumber(limit), ...) + end, + + select_reverse_range = function(self, sno, ino, limit, ...) + return box.space[tonumber(sno)].index[tonumber(ino)] + :select_reverse_range(tonumber(limit), ...) + end, + + -- for compatibility with the networked version, + -- implement call + call = function(self, proc_name, ...) + local proc = box.call_loadproc(proc_name) + return proc(...) + end, + + ping = function(self) + return true + end, + + -- local tarantool doesn't provide timeouts + timeout = function(self, timeout) + return self + end, + + close = function(self) + return true + end + } +} + +-- +-- Make sure box.net.box.select(conn, ...) works +-- just as well as conn:select(...) +-- +setmetatable(box.net.self, { __index = box.net.box }) + +box.net.box.new = function(host, port, reconnect_timeout) + if reconnect_timeout == nil then + reconnect_timeout = 0 + else + reconnect_timeout = tonumber(reconnect_timeout) + end + + local remote = { + host = host, + port = port, + reconnect_timeout = reconnect_timeout, + closed = false, + + processing = { + last_sync = 0, + next_sync = function(self) + while true do + self.last_sync = self.last_sync + 1 + if self[ self.last_sync ] == nil then + return self.last_sync + end + + if self.last_sync > 0x7FFFFFFF then + self.last_sync = 0 + end + end + end, + + -- write channel + wch = box.ipc.channel(1), + + -- ready socket channel + rch = box.ipc.channel(1), + }, + + + + process = function(self, op, request) + local started = box.time() + local timeout = self.request_timeout + self.request_timeout = nil + + -- get an auto-incremented request id + local sync = self.processing:next_sync() + self.processing[sync] = box.ipc.channel(1) + request = box.pack('iiia', op, string.len(request), sync, request) + + + if timeout ~= nil then + timeout = tonumber(timeout) + if not self.processing.wch:put(request, timeout) then + self.processing[sync] = nil + return nil + end + + timeout = timeout - (box.time() - started) + else + self.processing.wch:put(request) + end + + local res + if timeout ~= nil then + res = self.processing[sync]:get(timeout) + else + res = self.processing[sync]:get() + end + self.processing[sync] = nil + + + -- timeout + if res == nil then + if op == 65280 then + return false + else + return nil + end + end + + -- results { status, response } received + if res[1] then + if op == 65280 then + return true + else + local rop, blen, sync, code, body = + box.unpack('iiiia', res[2]) + if code ~= 0 then + box.raise(code, body) + end + + -- boc.unpack('R') unpacks response body for us (tuple) + return box.unpack('R', body) + end + else + error(res[2]) + end + end, + + + try_connect = function(self) + if self.s ~= nil then + return true + end + self.s = box.socket.tcp() + if self.s == nil then + self:fatal("Can't create socket") + return false + end + + local s = { self.s:connect( self.host, self.port ) } + if s[1] == nil then + self:fatal("Can't connect to %s:%s: %s", + self.host, self.port, s[4]) + return false + end + + return true + end, + + read_response = function(self) + if self.s == nil then + return + end + local res = { self.s:recv(12) } + if res[4] ~= nil then + self:fatal("Can't read socket: %s", res[3]) + return + end + local header = res[1] + if string.len(header) ~= 12 then + self:fatal("Unexpected eof while reading header") + return + end + + local op, blen, sync = box.unpack('iii', header) + + local body = '' + if blen > 0 then + res = { self.s:recv(blen) } + if res[4] ~= nil then + self:fatal("Error while reading socket: %s", res[4]) + return + end + body = res[1] + if string.len(body) ~= blen then + self:fatal("Unexpected eof while reading body") + return + end + end + return sync, header .. body + end, + + rfiber = function(self) + while not self.closed do + while not self.closed do + if self:try_connect(self.host, self.port) then + break + end + -- timeout between reconnect attempts + box.fiber.sleep(self.reconnect_timeout) + end + + -- wakeup write fiber + self.processing.rch:put(true, 0) + + while not self.closed do + local sync, resp = self:read_response() + if sync == nil then + break + end + + if self.processing[sync] ~= nil then + self.processing[sync]:put({true, resp}, 0) + else + print("Unexpected response ", sync) + end + end + + end + self.irfiber = nil + end, + + + wfiber = function(self) + local request + while not self.closed do + while self.s == nil do + self.processing.rch:get(1) + end + if request == nil then + request = self.processing.wch:get(1) + end + if self.s ~= nil and request ~= nil then + local res = { self.s:send(request) } + if res[1] ~= string.len(request) then + self:fatal("Error while write socket: %s", res[4]) + end + request = nil + end + end + self.iwfiber = nil + end, + + fatal = function(self, message, ...) + message = string.format(message, ...) + self.s = nil + for sync, ch in pairs(self.processing) do + if type(sync) == 'number' then + ch:put({ false, message }, 0) + end + end + end, + + close = function(self) + if self.closed then + error("box.net.box: already closed") + end + self.closed = true + local message = 'box.net.box: connection was closed' + self.process = function() + error(message) + end + self:fatal(message) + + -- wake up write fiber + self.processing.rch:put(true, 0) + self.processing.wch:put(true, 0) + return true + end + } + + + setmetatable( remote, { __index = box.net.box } ) + + remote.irfiber = box.fiber.wrap(remote.rfiber, remote) + remote.iwfiber = box.fiber.wrap(remote.wfiber, remote) + + return remote +end + +-- vim: set et ts=4 sts diff --git a/src/box/lua/misc.lua b/src/box/lua/misc.lua new file mode 100644 index 0000000000000000000000000000000000000000..b5c6651e3fa0695f8a6d14b2e324b3716f295eae --- /dev/null +++ b/src/box/lua/misc.lua @@ -0,0 +1,72 @@ +-- +-- Simple counter. +-- +box.counter = {} + +-- +-- Increment counter identified by primary key. +-- Create counter if not exists. +-- Returns updated value of the counter. +-- +function box.counter.inc(space, ...) + local key = {...} + local cnt_index = #key + + local tuple + while true do + tuple = box.update(space, key, '+p', cnt_index, 1) + if tuple ~= nil then break end + local data = {...} + table.insert(data, 1) + tuple = box.insert(space, unpack(data)) + if tuple ~= nil then break end + end + + return box.unpack('i', tuple[cnt_index]) +end + +-- +-- Decrement counter identified by primary key. +-- Delete counter if it decreased to zero. +-- Returns updated value of the counter. +-- +function box.counter.dec(space, ...) + local key = {...} + local cnt_index = #key + + local tuple = box.select(space, 0, ...) + if tuple == nil then return 0 end + if box.unpack('i', tuple[cnt_index]) == 1 then + box.delete(space, ...) + return 0 + else + tuple = box.update(space, key, '-p', cnt_index, 1) + return box.unpack('i', tuple[cnt_index]) + end +end + + +-- vim: set et ts=4 sts +-- Assumes that spaceno has a TREE int32 (NUM) or int64 (NUM64) primary key +-- inserts a tuple after getting the next value of the +-- primary key and returns it back to the user +function box.auto_increment(spaceno, ...) + spaceno = tonumber(spaceno) + local max_tuple = box.space[spaceno].index[0].idx:max() + local max = 0 + if max_tuple ~= nil then + max = max_tuple[0] + local fmt = 'i' + if #max == 8 then fmt = 'l' end + max = box.unpack(fmt, max) + else + -- first time + if box.space[spaceno].index[0].key_field[0].type == "NUM64" then + max = tonumber64(max) + end + end + return box.insert(spaceno, max + 1, ...) +end + + +-- vim: set et ts=4 sts diff --git a/src/fiber.m b/src/fiber.m index 3a1db8efd82e916542febe1f4dd4e6090d9cc886..4066cd910bb4404762b3056cd84619030de03612 100644 --- a/src/fiber.m +++ b/src/fiber.m @@ -72,7 +72,7 @@ fiber_call(struct fiber *callee, ...) { struct fiber *caller = fiber; - assert(sp - call_stack < FIBER_CALL_STACK); + assert(sp + 1 - call_stack < FIBER_CALL_STACK); assert(caller); fiber = callee; @@ -89,6 +89,12 @@ fiber_call(struct fiber *callee, ...) va_end(fiber->f_data); } +void +fiber_checkstack() +{ + if (sp + 1 - call_stack >= FIBER_CALL_STACK) + tnt_raise(ClientError, :ER_FIBER_STACK); +} /** Interrupt a synchronous wait of a fiber inside the event loop. * We do so by keeping an "async" event in every fiber, solely diff --git a/src/lua/init.m b/src/lua/init.m index c128c554b84248c3e557888f024a20993c8f0971..be887758462e9ec718df237b5b15e3f8514a39c2 100644 --- a/src/lua/init.m +++ b/src/lua/init.m @@ -545,8 +545,9 @@ box_lua_fiber_run(va_list ap __attribute__((unused))) /** @retval true if check failed, false otherwise */ static bool -fiber_checkstack(struct lua_State *L) +lbox_fiber_checkstack(struct lua_State *L) { + fiber_checkstack(); struct fiber *f = fiber; const int MAX_STACK_DEPTH = 16; int depth = 1; @@ -564,7 +565,7 @@ lbox_fiber_create(struct lua_State *L) { if (lua_gettop(L) != 1 || !lua_isfunction(L, 1)) luaL_error(L, "fiber.create(function): bad arguments"); - if (fiber_checkstack(L)) + if (lbox_fiber_checkstack(L)) luaL_error(L, "fiber.create(function): recursion limit" " reached"); @@ -645,6 +646,52 @@ lbox_fiber_resume(struct lua_State *L) return nargs; } +static void +box_lua_fiber_run_detached(va_list ap) +{ + int coro_ref = va_arg(ap, int); + struct lua_State *L = va_arg(ap, struct lua_State *); + @try { + lua_call(L, lua_gettop(L) - 1, LUA_MULTRET); + } @catch (FiberCancelException *e) { + @throw; + } @catch (tnt_Exception *e) { + [e log]; + } @catch (id allOthers) { + lua_settop(L, 1); + if (lua_tostring(L, -1) != NULL) + say_error("%s", lua_tostring(L, -1)); + } @finally { + luaL_unref(L, LUA_REGISTRYINDEX, coro_ref); + } +} + +/** + * Create, resume and detach a fiber + * given the function and its arguments. + */ +static int +lbox_fiber_wrap(struct lua_State *L) +{ + if (lua_gettop(L) < 1 || !lua_isfunction(L, 1)) + luaL_error(L, "fiber.wrap(function, ...): bad arguments"); + fiber_checkstack(); + + struct fiber *f = fiber_new("lua", box_lua_fiber_run_detached); + /* Not a system fiber. */ + f->flags |= FIBER_USER_MODE; + struct lua_State *child_L = lua_newthread(L); + int coro_ref = luaL_ref(L, LUA_REGISTRYINDEX); + /* Move the arguments to the new coro */ + lua_xmove(L, child_L, lua_gettop(L)); + fiber_call(f, coro_ref, child_L); + if (f->fid) + lbox_pushfiber(L, f); + else + lua_pushnil(L); + return 1; +} + /** * Yield the current fiber. * @@ -708,7 +755,7 @@ fiber_is_caller(struct lua_State *L, struct fiber *f) { static int lbox_fiber_status(struct lua_State *L) { - struct fiber *f = lbox_checkfiber(L, 1); + struct fiber *f = lua_gettop(L) ? lbox_checkfiber(L, 1) : fiber; const char *status; if (f->fid == 0) { /* This fiber is dead. */ @@ -834,6 +881,7 @@ static const struct luaL_reg fiberlib[] = { {"testcancel", lbox_fiber_testcancel}, {"create", lbox_fiber_create}, {"resume", lbox_fiber_resume}, + {"wrap", lbox_fiber_wrap}, {"yield", lbox_fiber_yield}, {"status", lbox_fiber_status}, {"name", lbox_fiber_name}, diff --git a/test/big/lua.result b/test/big/lua.result index 9a5a11037619c6a2cfdb2ec3507ba1b25f23b6d6..d3ba846c9d65eaa17bae27235a79bfa824d0d686 100644 --- a/test/big/lua.result +++ b/test/big/lua.result @@ -31,6 +31,10 @@ Found 1 tuple: # insert into t1 values ('item 1', 'alabama', 'song') Insert OK, 1 row affected +lua box.select(1, 1, 'alabama') +--- + - 'item 1': {'alabama', 1735290739} +... insert into t1 values ('item 2', 'california', 'dreaming ') Insert OK, 1 row affected insert into t1 values ('item 3', 'california', 'uber alles') diff --git a/test/big/lua.test b/test/big/lua.test index 075919cd7334123e25b9836c26c26c1d6270dff5..db6058da97502e97115505962b0d2ab7b7af19c6 100644 --- a/test/big/lua.test +++ b/test/big/lua.test @@ -28,6 +28,7 @@ print """# # https://bugs.launchpad.net/tarantool/+bug/902091 #""" exec sql "insert into t1 values ('item 1', 'alabama', 'song')" +exec admin "lua box.select(1, 1, 'alabama')" exec sql "insert into t1 values ('item 2', 'california', 'dreaming ')" exec sql "insert into t1 values ('item 3', 'california', 'uber alles')" exec sql "insert into t1 values ('item 4', 'georgia', 'on my mind')" diff --git a/test/box/lua.result b/test/box/lua.result index 8564b5c24cb07918b21ffa1cee4669cd4bd55c67..c0b4c136ba9aa278919a840521494f8b5017d2da 100644 --- a/test/box/lua.result +++ b/test/box/lua.result @@ -10,42 +10,44 @@ lua print(' lua says: hello') --- lua says: hello ... -lua for n in pairs(box) do print(' - box.', n) end +lua local t = {} for n in pairs(box) do table.insert(t, ' - box.' .. tostring(n)) end table.sort(t) for i = 1, #t do print(t[i]) end t = nil --- - - box.fiber - - box.time64 - box.auto_increment + - box.bless_space + - box.call_loadproc + - box.cfg - box.counter - box.delete - - box.replace - - box.time - - box.update - - box.on_reload_configuration - - box.info - - box.uuid_hex - - box.slab - - box.pack - - box.raise - - box.bless_space - - box.ipc + - box.dostring - box.error - - box.space - - box.insert - - box.cfg - - box.tuple - - box.session - - box.select_limit + - box.fiber + - box.flags - box.index - - box.dostring + - box.info + - box.insert + - box.ipc + - box.net + - box.on_reload_configuration + - box.pack - box.process - - box.stat - - box.uuid + - box.raise + - box.replace - box.select - - box.flags - - box.unpack + - box.select_limit - box.select_range - box.select_reverse_range + - box.session + - box.slab - box.socket + - box.space + - box.stat + - box.time + - box.time64 + - box.tuple + - box.unpack + - box.update + - box.uuid + - box.uuid_hex ... lua box.pack() --- @@ -801,9 +803,39 @@ lua box.fiber.find(920) --- - nil ... -# A test case for Bug##933487 +lua f = function() box.fiber.wrap(f) end +--- +... +call f() +No match +lua f = function(a, b) box.fiber.wrap(function(arg) result = arg end, a..b) end +--- +... +lua f('hello ', 'world') +--- +... +lua result +--- + - hello world +... +lua f('bye ', 'world') +--- +... +lua result +--- + - bye world +... +lua box.fiber.wrap(function() result = box.fiber.status() end) +--- + - nil +... +lua result +--- + - running +... +# A test case for Bug#933487 # tarantool crashed during shutdown if non running LUA fiber -# was created +# was created # lua f = box.fiber.create(function () return true end) --- @@ -1882,7 +1914,7 @@ lua function bug1075677() local range = {} table.insert(range, 1>0) return range ... call bug1075677() Found 1 tuple: -[1702195828] +['true�'] lua bug1075677=nil --- ... @@ -1892,5 +1924,5 @@ lua box.tuple.new(false) ... lua box.tuple.new({false}) --- - - 'false': {} + - 'false\x00': {} ... diff --git a/test/box/lua.test b/test/box/lua.test index ccf1c400d3f5c3775bd2311923994d408d8ff9e0..f7de541fee9c4f2973eea05493bbcf01ad236558 100644 --- a/test/box/lua.test +++ b/test/box/lua.test @@ -6,8 +6,8 @@ import sys exec admin "lua" exec admin "lua 1" exec admin "lua print(' lua says: hello')" -# What's in the box? -exec admin "lua for n in pairs(box) do print(' - box.', n) end" +# What's in the box? +exec admin "lua local t = {} for n in pairs(box) do table.insert(t, ' - box.' .. tostring(n)) end table.sort(t) for i = 1, #t do print(t[i]) end t = nil" # Test box.pack() exec admin "lua box.pack()" exec admin "lua box.pack(1)" @@ -210,11 +210,34 @@ exec admin "lua collectgarbage('collect')" exec admin "lua box.fiber.find(900)" exec admin "lua box.fiber.find(910)" exec admin "lua box.fiber.find(920)" + +# +# Test box.fiber.wrap() +# +# This should try to infinitely create fibers, +# but hit the fiber stack size limit and fail +# with an error. +# +exec admin "lua f = function() box.fiber.wrap(f) end" +exec sql "call f()" +# +# Test argument passing +# +exec admin "lua f = function(a, b) box.fiber.wrap(function(arg) result = arg end, a..b) end" +exec admin "lua f('hello ', 'world')" +exec admin "lua result" +exec admin "lua f('bye ', 'world')" +exec admin "lua result" +# +# Test that the created fiber is detached +# +exec admin "lua box.fiber.wrap(function() result = box.fiber.status() end)" +exec admin "lua result" # # -print """# A test case for Bug##933487 +print """# A test case for Bug#933487 # tarantool crashed during shutdown if non running LUA fiber -# was created +# was created #""" exec admin "lua f = box.fiber.create(function () return true end)" exec admin "save snapshot" diff --git a/test/box/lua_misc.result b/test/box/lua_misc.result index e0392a4c4aa4b8eb38e3baa5edf430dcd972e59e..31495fe9f81dff39e6722736d0b62c40769479e5 100644 --- a/test/box/lua_misc.result +++ b/test/box/lua_misc.result @@ -106,11 +106,12 @@ box.error.ER_PROC_RET: 12290 box.error.ER_TUPLE_IS_TOO_LONG: 11010 box.error.ER_EXACT_MATCH: 11522 box.error.ER_SECONDARY: 770 -box.error.ER_OK: 0 box.error.ER_SPACE_DISABLED: 13314 -box.error.ER_TUPLE_NOT_FOUND: 12546 +box.error.ER_OK: 0 box.error.ER_TUPLE_FOUND: 14082 -box.error.ER_UNKNOWN_UPDATE_OP: 11266 +box.error.ER_TUPLE_NOT_FOUND: 12546 +box.error.ER_FIBER_STACK: 6658 +box.error.ER_SPLICE: 10754 box.error.ER_NO_SUCH_FIELD: 13826 box.error.ER_UNSUPPORTED: 2562 box.error.ER_INJECTION: 2306 @@ -121,9 +122,9 @@ box.error.ER_NO_SUCH_SPACE: 14594 box.error.ER_TUPLE_IS_EMPTY: 6402 box.error.ER_PROC_LUA: 13058 box.error.ER_NO_SUCH_PROC: 12802 -box.error.ER_SPLICE: 10754 +box.error.ER_UNKNOWN_UPDATE_OP: 11266 box.error.ER_KEY_PART_COUNT: 12034 -box.error.ER_WAL_IO: 9986 box.error.ER_FIELD_TYPE: 10242 +box.error.ER_WAL_IO: 9986 box.error.ER_MEMORY_ISSUE: 1793 ... diff --git a/test/box/net.box.result b/test/box/net.box.result new file mode 100644 index 0000000000000000000000000000000000000000..ad130a494bd09785e3ef56ac60e8765405da656a --- /dev/null +++ b/test/box/net.box.result @@ -0,0 +1,181 @@ +lua remote = box.net.box.new('localhost', box.cfg.primary_port, '0.5') +--- +... +lua type(remote) +--- + - table +... +lua remote:ping() +--- + - true +... +lua remote:ping() +--- + - true +... +lua box.net.box.ping(remote) +--- + - true +... +lua box.insert(0, 123, 'test1', 'test2') +--- + - 123: {'test1', 'test2'} +... +lua box.select(0, 0, 123) +--- + - 123: {'test1', 'test2'} +... +lua tuple = remote:select(0, 0, 123) +--- +... +lua remote:call('box.select', '0', '0', 123) +--- + - 123: {'test1', 'test2'} +... +lua tuple +--- + - 123: {'test1', 'test2'} +... +lua type(tuple) +--- + - userdata +... +lua #tuple +--- + - 3 +... +lua box.update(0, 123, '=p', 1, 'test1-updated') +--- + - 123: {'test1-updated', 'test2'} +... +lua remote:update(0, 123, '=p', 2, 'test2-updated') +--- + - 123: {'test1-updated', 'test2-updated'} +... +lua box.insert(0, 123, 'test1', 'test2') +--- +error: 'Duplicate key exists in unique index 0' +... +lua remote:insert(0, 123, 'test1', 'test2') +--- +error: 'Duplicate key exists in unique index 0' +... +lua remote:insert(0, 345, 'test1', 'test2') +--- + - 345: {'test1', 'test2'} +... +lua remote:select(0, 0, 345) +--- + - 345: {'test1', 'test2'} +... +lua remote:call('box.select', 0, 0, 345) +--- + - 345: {'test1', 'test2'} +... +lua box.select(0, 0, 345) +--- + - 345: {'test1', 'test2'} +... +lua remote:replace(0, 345, 'test1-replaced', 'test2-replaced') +--- + - 345: {'test1-replaced', 'test2-replaced'} +... +lua box.select(0, 0, 345) +--- + - 345: {'test1-replaced', 'test2-replaced'} +... +lua remote:select_limit(0, 0, 0, 1000, 345) +--- + - 345: {'test1-replaced', 'test2-replaced'} +... +lua box.select_range(0, 0, 1000) +--- + - 123: {'test1-updated', 'test2-updated'} + - 345: {'test1-replaced', 'test2-replaced'} +... +lua remote:select_range(0, 0, 1000) +--- + - 123: {'test1-updated', 'test2-updated'} + - 345: {'test1-replaced', 'test2-replaced'} +... +lua box.select(0, 0, 345) +--- + - 345: {'test1-replaced', 'test2-replaced'} +... +lua remote:select(0, 0, 345) +--- + - 345: {'test1-replaced', 'test2-replaced'} +... +lua remote:timeout(0.5):select(0, 0, 345) +--- + - 345: {'test1-replaced', 'test2-replaced'} +... +lua remote:call('box.fiber.sleep', '.01') +--- +... +lua remote:timeout(0.01):call('box.fiber.sleep', '10') +--- + - nil +... +lua pstart = box.time() +--- +... +lua parallel = {} +--- +... +lua function parallel_foo(id) box.fiber.sleep(math.random() * .05) return id end +--- +... +lua parallel_foo('abc') +--- + - abc +... +lua for i = 1, 20 do box.fiber.resume(box.fiber.create(function() box.fiber.detach() local s = string.format('%07d', i) local so = remote:call('parallel_foo', s) table.insert(parallel, tostring(s == so[0]) ) end)) end +--- +... +lua for i = 1, 20 do if #parallel == 20 then break end box.fiber.sleep(0.1) end +--- +... +lua unpack(parallel) +--- + - true + - true + - true + - true + - true + - true + - true + - true + - true + - true + - true + - true + - true + - true + - true + - true + - true + - true + - true + - true +... +lua #parallel +--- + - 20 +... +lua box.time() - pstart < 0.5 +--- + - true +... +lua remote:close() +--- + - true +... +lua remote:close() +--- +error: '[string "box.net = {..."]:399: box.net.box: already closed' +... +lua remote:ping() +--- +error: '[string "box.net = {..."]:404: box.net.box: connection was closed' +... diff --git a/test/box/net.box.test b/test/box/net.box.test new file mode 100644 index 0000000000000000000000000000000000000000..d807969d7aded9833c331357d613c5b5a95ebc9e --- /dev/null +++ b/test/box/net.box.test @@ -0,0 +1,63 @@ +# encoding: tarantool +# vim: set ft=python : + + +# exec admin "lua iotest()" +# exec admin "lua iotest()" +# exec admin "lua box.fiber.sleep(.5)" + + + +exec admin "lua remote = box.net.box.new('localhost', box.cfg.primary_port, '0.5')" +exec admin "lua type(remote)" +exec admin "lua remote:ping()" +exec admin "lua remote:ping()" +exec admin "lua box.net.box.ping(remote)" +exec admin "lua box.insert(0, 123, 'test1', 'test2')" +exec admin "lua box.select(0, 0, 123)" +exec admin "lua tuple = remote:select(0, 0, 123)" +exec admin "lua remote:call('box.select', '0', '0', 123)" +exec admin "lua tuple" +exec admin "lua type(tuple)" +exec admin "lua #tuple" + +exec admin "lua box.update(0, 123, '=p', 1, 'test1-updated')" +exec admin "lua remote:update(0, 123, '=p', 2, 'test2-updated')" + + +exec admin "lua box.insert(0, 123, 'test1', 'test2')" +exec admin "lua remote:insert(0, 123, 'test1', 'test2')" + +exec admin "lua remote:insert(0, 345, 'test1', 'test2')" +exec admin "lua remote:select(0, 0, 345)" +exec admin "lua remote:call('box.select', 0, 0, 345)" +exec admin "lua box.select(0, 0, 345)" + + +exec admin "lua remote:replace(0, 345, 'test1-replaced', 'test2-replaced')" +exec admin "lua box.select(0, 0, 345)" +exec admin "lua remote:select_limit(0, 0, 0, 1000, 345)" + +exec admin "lua box.select_range(0, 0, 1000)" +exec admin "lua remote:select_range(0, 0, 1000)" +exec admin "lua box.select(0, 0, 345)" +exec admin "lua remote:select(0, 0, 345)" +exec admin "lua remote:timeout(0.5):select(0, 0, 345)" + +exec admin "lua remote:call('box.fiber.sleep', '.01')" +exec admin "lua remote:timeout(0.01):call('box.fiber.sleep', '10')" + + +exec admin "lua pstart = box.time()" +exec admin "lua parallel = {}" +exec admin "lua function parallel_foo(id) box.fiber.sleep(math.random() * .05) return id end" +exec admin "lua parallel_foo('abc')" +exec admin "lua for i = 1, 20 do box.fiber.resume(box.fiber.create(function() box.fiber.detach() local s = string.format('%07d', i) local so = remote:call('parallel_foo', s) table.insert(parallel, tostring(s == so[0]) ) end)) end" +exec admin "lua for i = 1, 20 do if #parallel == 20 then break end box.fiber.sleep(0.1) end" +exec admin "lua unpack(parallel)" +exec admin "lua #parallel" +exec admin "lua box.time() - pstart < 0.5" + +exec admin "lua remote:close()" +exec admin "lua remote:close()" +exec admin "lua remote:ping()" diff --git a/test/lib/sql_ast.py b/test/lib/sql_ast.py index dedeadd77a6ff68036d7213d9116f2db8e5fb362..1d828a732079bb7d6a40e848cc20b4d788a92016 100644 --- a/test/lib/sql_ast.py +++ b/test/lib/sql_ast.py @@ -51,7 +51,7 @@ ER = { 23: "ER_RESERVED23" , 24: "ER_UNUSED24" , 25: "ER_TUPLE_IS_EMPTY" , - 26: "ER_UNUSED26" , + 26: "ER_FIBER_STACK" , 27: "ER_UNUSED27" , 28: "ER_UNUSED28" , 29: "ER_UNUSED29" ,