Skip to content

Commit 672a687

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 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 b53e0ba commit 672a687

29 files changed

+1650
-209
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,18 @@ makes possible for the client to configure a socket provider.
122122
* `ComposableAsyncOps` - return the operation result as a `CompletionStage`
123123
* `FireAndForgetOps` - returns the query ID
124124

125+
Each operation that requires space or index to be executed, can work with
126+
number ID as well as string name of a space or an index.
127+
Assume, we have `my_space` space with space ID `512` and its primary index
128+
`primary` with index ID `0`. Then, for instance, `select` operations can be
129+
performed as the following:
130+
131+
```java
132+
client.syncOps().select(512, 0, Collections.singletonList(1), 0, 1, Iterator.EQ);
133+
// or using more convenient way
134+
client.syncOps().select("my_space", "primary", Collections.singletonList(1), 0, 1, Iterator.EQ);
135+
```
136+
125137
Feel free to override any method of `TarantoolClientImpl`. For example, to hook
126138
all the results, you could override this:
127139

Lines changed: 255 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,294 @@
11
package org.tarantool;
22

3+
import static org.tarantool.AbstractTarantoolOps.ArgumentFactory.allResolved;
4+
import static org.tarantool.AbstractTarantoolOps.ArgumentFactory.resolved;
5+
import static org.tarantool.AbstractTarantoolOps.ArgumentFactory.unresolved;
36

4-
public abstract class AbstractTarantoolOps<Space, Tuple, Operation, Result>
5-
implements TarantoolClientOps<Space, Tuple, Operation, Result> {
7+
import java.util.Arrays;
8+
import java.util.Objects;
9+
import java.util.function.BiFunction;
10+
import java.util.function.Function;
11+
import java.util.stream.Stream;
12+
13+
public abstract class AbstractTarantoolOps<Tuple, Operation, Result>
14+
implements TarantoolClientOps<Tuple, Operation, Result> {
15+
16+
private final Function<Object, Object> defaultSpaceResolver =
17+
space -> resolveSpace((String) space);
18+
private final BiFunction<Object, Object, Object> defaultSpaceIndexResolver =
19+
(space, index) -> resolveSpaceIndex((String) space, (String) index);
620

721
private Code callCode = Code.CALL;
822

9-
protected abstract Result exec(Code code, Object... args);
23+
protected Result exec(Code code, Object... args) {
24+
return exec(code, allResolved(args));
25+
}
26+
27+
protected Result exec(long timeoutMillis, Code code, Object... args) {
28+
return exec(timeoutMillis, code, allResolved(args));
29+
}
30+
31+
protected abstract Result exec(Code code, ResolvableOpArgument... args);
32+
33+
protected abstract Result exec(long timeoutMillis, Code code, ResolvableOpArgument... args);
34+
35+
protected abstract int resolveSpace(String space);
36+
37+
protected abstract int resolveSpaceIndex(String space, String index);
1038

11-
public Result select(Space space, Space index, Tuple key, int offset, int limit, Iterator iterator) {
39+
@Override
40+
public Result select(Integer space, Integer index, Tuple key, int offset, int limit, Iterator iterator) {
1241
return select(space, index, key, offset, limit, iterator.getValue());
1342
}
1443

15-
public Result select(Space space, Space index, Tuple key, int offset, int limit, int iterator) {
16-
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
44+
@Override
45+
public Result select(String space, String index, Tuple key, int offset, int limit, Iterator iterator) {
46+
return select(space, index, key, offset, limit, iterator.getValue());
47+
}
48+
49+
@Override
50+
public Result select(Integer space, Integer index, Tuple key, int offset, int limit, int iterator) {
51+
return doSelect(resolved(space), resolved(index), key, offset, limit, iterator);
52+
}
53+
54+
@Override
55+
public Result select(String space, String index, Tuple key, int offset, int limit, int iterator) {
56+
return doSelect(
57+
unresolved(space, defaultSpaceResolver),
58+
unresolved(index, indexName -> defaultSpaceIndexResolver.apply(space, indexName)),
59+
key, offset, limit, iterator
2460
);
2561
}
2662

27-
public Result insert(Space space, Tuple tuple) {
28-
return exec(Code.INSERT, Key.SPACE, space, Key.TUPLE, tuple);
63+
@Override
64+
public Result insert(Integer space, Tuple tuple) {
65+
return doInsert(resolved(space), tuple);
66+
}
67+
68+
@Override
69+
public Result insert(String space, Tuple tuple) {
70+
return doInsert(unresolved(space, defaultSpaceResolver), tuple);
2971
}
3072

31-
public Result replace(Space space, Tuple tuple) {
32-
return exec(Code.REPLACE, Key.SPACE, space, Key.TUPLE, tuple);
73+
@Override
74+
public Result replace(Integer space, Tuple tuple) {
75+
return doReplace(resolved(space), tuple);
3376
}
3477

35-
public Result update(Space space, Tuple key, Operation... args) {
36-
return exec(Code.UPDATE, Key.SPACE, space, Key.KEY, key, Key.TUPLE, args);
78+
@Override
79+
public Result replace(String space, Tuple tuple) {
80+
return doReplace(unresolved(space, defaultSpaceResolver), tuple);
3781
}
3882

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);
83+
@Override
84+
public Result update(Integer space, Tuple key, Operation... args) {
85+
return doUpdate(resolved(space), key, args);
4186
}
4287

43-
public Result delete(Space space, Tuple key) {
44-
return exec(Code.DELETE, Key.SPACE, space, Key.KEY, key);
88+
@Override
89+
public Result update(String space, Tuple key, Operation... tuple) {
90+
return doUpdate(unresolved(space, defaultSpaceResolver), key, tuple);
4591
}
4692

93+
@Override
94+
public Result upsert(Integer space, Tuple key, Tuple defTuple, Operation... ops) {
95+
return doUpsert(resolved(space), key, defTuple, ops);
96+
}
97+
98+
@Override
99+
public Result upsert(String space, Tuple key, Tuple defTuple, Operation... ops) {
100+
return doUpsert(unresolved(space, defaultSpaceResolver), key, defTuple, ops);
101+
}
102+
103+
@Override
104+
public Result delete(Integer space, Tuple key) {
105+
return doDelete(resolved(space), key);
106+
}
107+
108+
@Override
109+
public Result delete(String space, Tuple key) {
110+
return doDelete(unresolved(space, defaultSpaceResolver), key);
111+
}
112+
113+
@Override
47114
public Result call(String function, Object... args) {
48115
return exec(callCode, Key.FUNCTION, function, Key.TUPLE, args);
49116
}
50117

118+
@Override
51119
public Result eval(String expression, Object... args) {
52120
return exec(Code.EVAL, Key.EXPRESSION, expression, Key.TUPLE, args);
53121
}
54122

123+
@Override
55124
public void ping() {
56125
exec(Code.PING);
57126
}
58127

59128
public void setCallCode(Code callCode) {
60129
this.callCode = callCode;
61130
}
131+
132+
private Result doDelete(ResolvableOpArgument space, Tuple key) {
133+
return exec(Code.DELETE, resolved(Key.SPACE), space, resolved(Key.KEY), resolved(key));
134+
}
135+
136+
private Result doUpsert(ResolvableOpArgument space, Tuple key, Tuple defTuple, Operation... ops) {
137+
return exec(
138+
Code.UPSERT,
139+
resolved(Key.SPACE), space,
140+
resolved(Key.KEY), resolved(key),
141+
resolved(Key.TUPLE), resolved(defTuple),
142+
resolved(Key.UPSERT_OPS), resolved(ops)
143+
);
144+
}
145+
146+
private Result doUpdate(ResolvableOpArgument space, Tuple key, Operation... ops) {
147+
return exec(
148+
Code.UPDATE,
149+
resolved(Key.SPACE), space,
150+
resolved(Key.KEY), resolved(key),
151+
resolved(Key.TUPLE), resolved(ops)
152+
);
153+
}
154+
155+
private Result doReplace(ResolvableOpArgument space, Tuple tuple) {
156+
return exec(Code.REPLACE, resolved(Key.SPACE), space, resolved(Key.TUPLE), resolved(tuple));
157+
}
158+
159+
private Result doInsert(ResolvableOpArgument space, Tuple tuple) {
160+
return exec(Code.INSERT, resolved(Key.SPACE), space, resolved(Key.TUPLE), resolved(tuple));
161+
}
162+
163+
private Result doSelect(ResolvableOpArgument space,
164+
ResolvableOpArgument index,
165+
Tuple key,
166+
int offset,
167+
int limit,
168+
int iterator) {
169+
return exec(
170+
Code.SELECT,
171+
resolved(Key.SPACE), space,
172+
resolved(Key.INDEX), index,
173+
resolved(Key.KEY), resolved(key),
174+
resolved(Key.ITERATOR), resolved(iterator),
175+
resolved(Key.LIMIT), resolved(limit),
176+
resolved(Key.OFFSET), resolved(offset)
177+
);
178+
}
179+
180+
public static class ArgumentFactory {
181+
182+
public static ResolvableOpArgument resolved(Object value) {
183+
return new ResolvedArgument(value);
184+
}
185+
186+
public static ResolvableOpArgument unresolved(Object key, Function<Object, Object> resolver) {
187+
return new UnresolvedArgument(key, resolver);
188+
}
189+
190+
public static ResolvableOpArgument[] allResolved(Object[] values) {
191+
return Stream.of(values).map(ArgumentFactory::resolved).toArray(ResolvableOpArgument[]::new);
192+
}
193+
194+
}
195+
196+
/**
197+
* Wrapper over the target argument which possibly can't
198+
* be resolved at the moment.
199+
*/
200+
public interface ResolvableOpArgument {
201+
202+
/**
203+
* Checks whether an argument can be obtained instantly calling
204+
* {@link #getValue()}.
205+
*
206+
* @return {@literal true} if value can be retrieved right now.
207+
*/
208+
boolean hasValue();
209+
210+
/**
211+
* Gets a target argument value. It raises
212+
* an exception if the value is unavailable.
213+
* Availability can be checked via {@link #hasValue()}.
214+
*
215+
* @return wrapped argument value
216+
* @throws RuntimeException if the value is unavailable.
217+
*/
218+
Object getValue();
219+
220+
}
221+
222+
/**
223+
* Simple wrapper that holds the original value.
224+
*/
225+
public static class ResolvedArgument implements ResolvableOpArgument {
226+
227+
private final Object value;
228+
229+
private ResolvedArgument(Object value) {
230+
Objects.requireNonNull(value);
231+
this.value = value;
232+
}
233+
234+
@Override
235+
public boolean hasValue() {
236+
return true;
237+
}
238+
239+
@Override
240+
public Object getValue() {
241+
return value;
242+
}
243+
244+
@Override
245+
public String toString() {
246+
return "ResolvedArgument{" +
247+
"value=" + ((value instanceof Object[]) ? Arrays.toString((Object[])value) : value) +
248+
'}';
249+
}
250+
251+
}
252+
253+
/**
254+
* Wrapper that evaluates the value each time
255+
* it is requested.
256+
* <p>
257+
* It works like a function, where {@code argument = f(key)}.
258+
*/
259+
public static class UnresolvedArgument implements ResolvableOpArgument {
260+
261+
private final Object key;
262+
private final Function<Object, Object> resolver;
263+
264+
private UnresolvedArgument(Object key, Function<Object, Object> resolver) {
265+
Objects.requireNonNull(key);
266+
Objects.requireNonNull(resolver);
267+
268+
this.key = key;
269+
this.resolver = resolver;
270+
}
271+
272+
@Override
273+
public boolean hasValue() {
274+
try {
275+
resolver.apply(key);
276+
} catch (Exception ignored) {
277+
return false;
278+
}
279+
return true;
280+
}
281+
282+
@Override
283+
public Object getValue() {
284+
return resolver.apply(key);
285+
}
286+
287+
@Override
288+
public String toString() {
289+
return "UnresolvedArgument{" +
290+
"key=" + key +
291+
'}';
292+
}
293+
}
62294
}

src/main/java/org/tarantool/TarantoolBase.java

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import java.util.List;
1010
import java.util.concurrent.atomic.AtomicLong;
1111

12-
public abstract class TarantoolBase<Result> extends AbstractTarantoolOps<Integer, List<?>, Object, Result> {
12+
public abstract class TarantoolBase<Result> extends AbstractTarantoolOps<List<?>, Object, Result> {
1313
protected String serverVersion;
1414
protected MsgPackLite msgPackLite = MsgPackLite.INSTANCE;
1515
protected AtomicLong syncId = new AtomicLong();
@@ -42,16 +42,6 @@ protected void closeChannel(SocketChannel channel) {
4242
}
4343
}
4444

45-
protected void validateArgs(Object[] args) {
46-
if (args != null) {
47-
for (int i = 0; i < args.length; i += 2) {
48-
if (args[i + 1] == null) {
49-
throw new NullPointerException(((Key) args[i]).name() + " should not be null");
50-
}
51-
}
52-
}
53-
}
54-
5545
public void setInitialRequestSize(int initialRequestSize) {
5646
this.initialRequestSize = initialRequestSize;
5747
}

src/main/java/org/tarantool/TarantoolClient.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@
77
import java.util.concurrent.TimeUnit;
88

99
public interface TarantoolClient {
10-
TarantoolClientOps<Integer, List<?>, Object, List<?>> syncOps();
10+
TarantoolClientOps<List<?>, Object, List<?>> syncOps();
1111

12-
TarantoolClientOps<Integer, List<?>, Object, Future<List<?>>> asyncOps();
12+
TarantoolClientOps<List<?>, Object, Future<List<?>>> asyncOps();
1313

14-
TarantoolClientOps<Integer, List<?>, Object, CompletionStage<List<?>>> composableAsyncOps();
14+
TarantoolClientOps<List<?>, Object, CompletionStage<List<?>>> composableAsyncOps();
1515

16-
TarantoolClientOps<Integer, List<?>, Object, Long> fireAndForgetOps();
16+
TarantoolClientOps<List<?>, Object, Long> fireAndForgetOps();
1717

1818
TarantoolSQLOps<Object, Long, List<Map<String, Object>>> sqlSyncOps();
1919

0 commit comments

Comments
 (0)