Skip to content

Commit 31c2dd9

Browse files
committed
Soft automatic schema reload
Now client keeps actual schema metadata and sends schemaId header to be checked against current Tarantool schema version. If client version mismatches DB version client does schema reloading in the background. Client operation interface was reworked in scope of support not only number identifiers for spaces and indexes but also their string names. Closes: #7, #137
1 parent 2b32e80 commit 31c2dd9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+2414
-438
lines changed

README.md

+46
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,52 @@ Supported options are follow:
177177
14. `operationExpiryTimeMillis` is a default request timeout in ms.
178178
Default value is `1000` (1 second).
179179

180+
## String space/index resolution
181+
182+
Each operation that requires space or index to be executed, can work with
183+
number ID as well as string name of a space or an index.
184+
Assume, we have `my_space` space with space ID `512` and its primary index
185+
`primary` with index ID `0`. Then, for instance, `select` operations can be
186+
performed using their names:
187+
188+
```java
189+
client.syncOps().select(512, 0, Collections.singletonList(1), 0, 1, Iterator.EQ);
190+
// or using more convenient way
191+
client.syncOps().select("my_space", "primary", Collections.singletonList(1), 0, 1, Iterator.EQ);
192+
```
193+
194+
Because _iproto_ has not yet supported string spaces and indexes, a client caches current server
195+
schema in memory. The client relies on protocol SCHEMA_ID and sends each request with respect to
196+
cached schema version. The schema is used primarily to resolve string names of spaces or indexes
197+
against its integer IDs.
198+
199+
### Schema update
200+
201+
1. Just after a (re-)connection to the Tarantool instance.
202+
The client cannot guarantee that new instance is the same and has same schema,
203+
thus, the client drops the cached schema and fetches new one.
204+
2. Receiving a schema version error as a response to our request.
205+
It's possible some request can be rejected by server because of schema
206+
mismatching between client and server. In this case the schema will be
207+
reloaded and the refused request will be resent using the updated schema
208+
version.
209+
3. Sending a DDL request and receiving a new version in a response.
210+
4. Sending a request against a non-existent space/index name.
211+
The client cannot exactly know whether name was not found because of
212+
it does not exist or it has not the latest schema version.
213+
214+
### Schema support caveats
215+
216+
1. Each schema reloading requires at least two extra requests to fetch spaces and
217+
indexes metadata respectively. There is also a ping request followed by reloading
218+
of the schema to check whether the client has outdated version (see point 4 in
219+
Schema update section).
220+
2. In some circumstance, requests can be rejected several times until both client's
221+
and server's versions matches. It may take significant amount of time or even be
222+
a cause of request timeout.
223+
3. The client guarantees an order of synchronous requests per thread. Other cases such
224+
as asynchronous or multi-threaded requests may be out of order before the execution.
225+
180226
## Spring NamedParameterJdbcTemplate usage example
181227

182228
The JDBC driver uses `TarantoolClient` implementation to provide a communication with server.
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,204 @@
11
package org.tarantool;
22

3+
import static org.tarantool.RequestArguments.cacheLookupValue;
4+
import static org.tarantool.RequestArguments.value;
35

4-
public abstract class AbstractTarantoolOps<Space, Tuple, Operation, Result>
5-
implements TarantoolClientOps<Space, Tuple, Operation, Result> {
6+
import org.tarantool.schema.TarantoolSchemaMeta;
7+
8+
import java.util.List;
9+
10+
public abstract class AbstractTarantoolOps<Result>
11+
implements TarantoolClientOps<List<?>, Object, Result> {
612

713
private Code callCode = Code.CALL;
814

9-
protected abstract Result exec(Code code, Object... args);
15+
protected abstract Result exec(TarantoolRequest request);
16+
17+
protected abstract TarantoolSchemaMeta getSchemaMeta();
18+
19+
public Result select(Integer space, Integer index, List<?> key, int offset, int limit, Iterator iterator) {
20+
return select(space, index, key, offset, limit, iterator.getValue());
21+
}
1022

11-
public Result select(Space space, Space index, Tuple key, int offset, int limit, Iterator iterator) {
23+
@Override
24+
public Result select(String space, String index, List<?> key, int offset, int limit, Iterator iterator) {
1225
return select(space, index, key, offset, limit, iterator.getValue());
1326
}
1427

15-
public Result select(Space space, Space index, Tuple key, int offset, int limit, int iterator) {
28+
@Override
29+
public Result select(Integer space, Integer index, List<?> key, int offset, int limit, int iterator) {
30+
return exec(
31+
new TarantoolRequest(
32+
Code.SELECT,
33+
value(Key.SPACE), value(space),
34+
value(Key.INDEX), value(index),
35+
value(Key.KEY), value(key),
36+
value(Key.ITERATOR), value(iterator),
37+
value(Key.LIMIT), value(limit),
38+
value(Key.OFFSET), value(offset)
39+
)
40+
);
41+
}
42+
43+
@Override
44+
public Result select(String space, String index, List<?> key, int offset, int limit, int iterator) {
45+
return exec(
46+
new TarantoolRequest(
47+
Code.SELECT,
48+
value(Key.SPACE), cacheLookupValue(() -> getSchemaMeta().getSpace(space).getId()),
49+
value(Key.INDEX), cacheLookupValue(() -> getSchemaMeta().getSpaceIndex(space, index).getId()),
50+
value(Key.KEY), value(key),
51+
value(Key.ITERATOR), value(iterator),
52+
value(Key.LIMIT), value(limit),
53+
value(Key.OFFSET), value(offset)
54+
)
55+
);
56+
}
57+
58+
@Override
59+
public Result insert(Integer space, List<?> tuple) {
60+
return exec(new TarantoolRequest(
61+
Code.INSERT,
62+
value(Key.SPACE), value(space),
63+
value(Key.TUPLE), value(tuple)
64+
)
65+
);
66+
}
67+
68+
@Override
69+
public Result insert(String space, List<?> tuple) {
70+
return exec(
71+
new TarantoolRequest(
72+
Code.INSERT,
73+
value(Key.SPACE), cacheLookupValue(() -> getSchemaMeta().getSpace(space).getId()),
74+
value(Key.TUPLE), value(tuple)
75+
)
76+
);
77+
}
78+
79+
@Override
80+
public Result replace(Integer space, List<?> tuple) {
81+
return exec(
82+
new TarantoolRequest(
83+
Code.REPLACE,
84+
value(Key.SPACE), value(space),
85+
value(Key.TUPLE), value(tuple)
86+
)
87+
);
88+
}
89+
90+
@Override
91+
public Result replace(String space, List<?> tuple) {
1692
return exec(
17-
Code.SELECT,
18-
Key.SPACE, space,
19-
Key.INDEX, index,
20-
Key.KEY, key,
21-
Key.ITERATOR, iterator,
22-
Key.LIMIT, limit,
23-
Key.OFFSET, offset
93+
new TarantoolRequest(
94+
Code.REPLACE,
95+
value(Key.SPACE), cacheLookupValue(() -> getSchemaMeta().getSpace(space).getId()),
96+
value(Key.TUPLE), value(tuple)
97+
)
2498
);
2599
}
26100

27-
public Result insert(Space space, Tuple tuple) {
28-
return exec(Code.INSERT, Key.SPACE, space, Key.TUPLE, tuple);
101+
@Override
102+
public Result update(Integer space, List<?> key, Object... operations) {
103+
return exec(
104+
new TarantoolRequest(
105+
Code.UPDATE,
106+
value(Key.SPACE), value(space),
107+
value(Key.KEY), value(key),
108+
value(Key.TUPLE), value(operations)
109+
)
110+
);
29111
}
30112

31-
public Result replace(Space space, Tuple tuple) {
32-
return exec(Code.REPLACE, Key.SPACE, space, Key.TUPLE, tuple);
113+
@Override
114+
public Result update(String space, List<?> key, Object... operations) {
115+
return exec(
116+
new TarantoolRequest(
117+
Code.UPDATE,
118+
value(Key.SPACE), cacheLookupValue(() -> getSchemaMeta().getSpace(space).getId()),
119+
value(Key.KEY), value(key),
120+
value(Key.TUPLE), value(operations)
121+
)
122+
);
123+
}
124+
125+
@Override
126+
public Result upsert(Integer space, List<?> key, List<?> defTuple, Object... operations) {
127+
return exec(
128+
new TarantoolRequest(
129+
Code.UPSERT,
130+
value(Key.SPACE), value(space),
131+
value(Key.KEY), value(key),
132+
value(Key.TUPLE), value(defTuple),
133+
value(Key.UPSERT_OPS), value(operations)
134+
)
135+
);
33136
}
34137

35-
public Result update(Space space, Tuple key, Operation... args) {
36-
return exec(Code.UPDATE, Key.SPACE, space, Key.KEY, key, Key.TUPLE, args);
138+
@Override
139+
public Result upsert(String space, List<?> key, List<?> defTuple, Object... operations) {
140+
return exec(
141+
new TarantoolRequest(
142+
Code.UPSERT,
143+
value(Key.SPACE), cacheLookupValue(() -> getSchemaMeta().getSpace(space).getId()),
144+
value(Key.KEY), value(key),
145+
value(Key.TUPLE), value(defTuple),
146+
value(Key.UPSERT_OPS), value(operations)
147+
)
148+
);
37149
}
38150

39-
public Result upsert(Space space, Tuple key, Tuple def, Operation... args) {
40-
return exec(Code.UPSERT, Key.SPACE, space, Key.KEY, key, Key.TUPLE, def, Key.UPSERT_OPS, args);
151+
@Override
152+
public Result delete(Integer space, List<?> key) {
153+
return exec(
154+
new TarantoolRequest(
155+
Code.DELETE,
156+
value(Key.SPACE), value(space),
157+
value(Key.KEY), value(key)
158+
)
159+
);
41160
}
42161

43-
public Result delete(Space space, Tuple key) {
44-
return exec(Code.DELETE, Key.SPACE, space, Key.KEY, key);
162+
@Override
163+
public Result delete(String space, List<?> key) {
164+
return exec(
165+
new TarantoolRequest(
166+
Code.DELETE,
167+
value(Key.SPACE), cacheLookupValue(() -> getSchemaMeta().getSpace(space).getId()),
168+
value(Key.KEY), value(key)
169+
)
170+
);
45171
}
46172

173+
@Override
47174
public Result call(String function, Object... args) {
48-
return exec(callCode, Key.FUNCTION, function, Key.TUPLE, args);
175+
return exec(
176+
new TarantoolRequest(
177+
callCode,
178+
value(Key.FUNCTION), value(function),
179+
value(Key.TUPLE), value(args)
180+
)
181+
);
49182
}
50183

184+
@Override
51185
public Result eval(String expression, Object... args) {
52-
return exec(Code.EVAL, Key.EXPRESSION, expression, Key.TUPLE, args);
186+
return exec(
187+
new TarantoolRequest(
188+
Code.EVAL,
189+
value(Key.EXPRESSION), value(expression),
190+
value(Key.TUPLE), value(args)
191+
)
192+
);
53193
}
54194

195+
@Override
55196
public void ping() {
56-
exec(Code.PING);
197+
exec(new TarantoolRequest(Code.PING));
57198
}
58199

59200
public void setCallCode(Code callCode) {
60201
this.callCode = callCode;
61202
}
203+
62204
}

src/main/java/org/tarantool/Iterator.java

+10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.tarantool;
22

3+
import java.util.Arrays;
4+
35
// Iterator info was taken from here https://github.com/tarantool/tarantool/blob/f66584c3bcdffe61d6d99a4868a9b72d62338a11/src/box/iterator_type.h#L62
46
public enum Iterator {
57
EQ(0), // key == x ASC order
@@ -24,4 +26,12 @@ public enum Iterator {
2426
public int getValue() {
2527
return value;
2628
}
29+
30+
public static Iterator valueOf(int value) {
31+
return Arrays.stream(Iterator.values())
32+
.filter(v -> value == v.getValue())
33+
.findFirst()
34+
.orElseThrow(IllegalArgumentException::new);
35+
}
36+
2737
}

src/main/java/org/tarantool/MsgPackLite.java

+7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.tarantool;
22

3+
import org.tarantool.util.TupleTwo;
4+
35
import java.io.DataInputStream;
46
import java.io.DataOutputStream;
57
import java.io.IOException;
@@ -226,6 +228,11 @@ public void pack(Object item, OutputStream os) throws IOException {
226228
pack(kvp.getKey(), out);
227229
pack(kvp.getValue(), out);
228230
}
231+
} else if (item instanceof TupleTwo) {
232+
TupleTwo<?, ?> tuple = (TupleTwo<?, ?>) item;
233+
out.write(1 | MP_FIXMAP);
234+
pack(tuple.getFirst(), out);
235+
pack(tuple.getSecond(), out);
229236
} else {
230237
throw new IllegalArgumentException("Cannot msgpack object of type " + item.getClass().getCanonicalName());
231238
}

0 commit comments

Comments
 (0)