diff --git a/CHANGELOG.md b/CHANGELOG.md index d2fe9d73..3c7f628a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,13 @@ - Fixed broken links in `tarantool-spring-data/README.md` and `testcontainers/README.md`: they pointed to `doc/` directories that do not exist and now point to the pages under `documentation/doc-src/pages`. +- Documented that the spring-data `Repository` layer needs a cluster with `crud` and `vshard`, + and listed three ways to get one. +- Explained in the spring-data configuration example that `auth-type` must match + `security.auth_type` of the cluster and that `pap-sha256` requires Enterprise Edition and TLS. +- Added the "Limitations and workarounds" page: no batch delete and no `IN` condition in `crud`, + examples of parallel requests instead, and derived-method keywords that are not supported. +- Added a table that maps Spring Boot minor lines to `tarantool-spring-data-*` modules. ## [1.7.2] - 2026-09-25 diff --git a/documentation/doc-src/pages/client/limitations.en.md b/documentation/doc-src/pages/client/limitations.en.md new file mode 100644 index 00000000..bf447e8c --- /dev/null +++ b/documentation/doc-src/pages/client/limitations.en.md @@ -0,0 +1,67 @@ +--- +title: Limitations and workarounds +--- + +## What is not supported and how to work around it + +The `crud` module on the Tarantool side has no batch delete (`delete_many`) and no `IN` condition: +conditions of a single `select` support only `==`, `<`, `<=`, `>`, `>=` and are combined by `AND`. +The client mirrors this: `TarantoolCrudSpace` has `insertMany`, `replaceMany` and `upsertMany`, but +no `deleteMany`. The workarounds below send several requests at once; requests of one client are +pipelined over its connections, so they do not wait for each other. + +???+ warning "Important" + + Neither workaround is atomic: each key is a separate operation, and keys can live on + different replica sets. Some requests may succeed while others fail; check every future. + The speed of these approaches relative to each other was not measured. + +### Delete several records by primary key + +```java +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import io.tarantool.client.crud.TarantoolCrudSpace; + +CompletableFuture[] futures = + keys.stream().map(space::delete).toArray(CompletableFuture[]::new); +CompletableFuture.allOf(futures).get(); +``` + +In Spring Data, `deleteAllById(ids)` sends one request per key and waits for each response before +sending the next request. + +Alternatively, write a stored procedure on the router that deletes the keys in a loop and call it with +`client.call(...)`: this is one network request from the application (also not atomic). + +### Select by `col IN (v1, v2, v3)` + +Send one `select` with the `==` condition per value in parallel and merge the results: + +```java +import io.tarantool.client.crud.Condition; +import io.tarantool.mapping.Tuple; + +List>>>> futures = + values.stream() + .map(v -> space.select(Condition.create("==", "col", v))) + .collect(Collectors.toList()); +CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get(); +List>> result = new ArrayList<>(); +for (CompletableFuture>>> f : futures) { + result.addAll(f.get()); +} +``` + +If `col` is the sharding key, each request goes to a single replica set; otherwise each `select` walks +all storages. For the primary key use `space.get(key)` for each key in parallel. Another option is +a stored procedure that takes the list of values and runs the selects on the server side. + +### Spring Data derived methods + +Supported keywords are listed in [Dynamic Queries](../spring-data/derived-methods.md). Keywords such +as `In`, `NotIn`, `Like` are not supported: such a method fails with +`InvalidDataAccessApiUsageException: Unsupported type ...`; a method with `And` or `Or` fails with +`UnsupportedOperationException`. Use the approaches above, or +[stored procedures](../spring-data/query-lua.md). diff --git a/documentation/doc-src/pages/client/limitations.md b/documentation/doc-src/pages/client/limitations.md new file mode 100644 index 00000000..b53b01ca --- /dev/null +++ b/documentation/doc-src/pages/client/limitations.md @@ -0,0 +1,67 @@ +--- +title: Ограничения и способы обхода +--- + +## Чего нет и как обойти + +В модуле `crud` на стороне Tarantool нет пакетного удаления (`delete_many`) и условия `IN`: +условия одного `select` поддерживают только `==`, `<`, `<=`, `>`, `>=` и объединяются по `AND`. +Клиент это отражает: в `TarantoolCrudSpace` есть `insertMany`, `replaceMany`, `upsertMany`, но +нет `deleteMany`. Обходные пути ниже отправляют несколько запросов сразу; запросы одного клиента идут +конвейером по его соединениям и не ждут друг друга. + +???+ warning "Важно" + + Ни один из способов не атомарен: каждый ключ — отдельная операция, ключи могут лежать на разных + наборах реплик. Часть запросов может пройти, часть упасть; проверяйте каждый future. + Скорость способов друг относительно друга не измерялась. + +### Удалить несколько записей по первичному ключу + +```java +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import io.tarantool.client.crud.TarantoolCrudSpace; + +CompletableFuture[] futures = + keys.stream().map(space::delete).toArray(CompletableFuture[]::new); +CompletableFuture.allOf(futures).get(); +``` + +В Spring Data `deleteAllById(ids)` отправляет по запросу на ключ и ждёт ответа на каждый, прежде +чем отправить следующий. + +Другой путь: хранимая процедура на роутере, которая удаляет ключи в цикле, и вызов через +`client.call(...)` — один сетевой запрос от приложения (тоже не атомарно). + +### Выборка `col IN (v1, v2, v3)` + +Отправьте параллельно по одному `select` с условием `==` на каждое значение и склейте результат: + +```java +import io.tarantool.client.crud.Condition; +import io.tarantool.mapping.Tuple; + +List>>>> futures = + values.stream() + .map(v -> space.select(Condition.create("==", "col", v))) + .collect(Collectors.toList()); +CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get(); +List>> result = new ArrayList<>(); +for (CompletableFuture>>> f : futures) { + result.addAll(f.get()); +} +``` + +Если `col` — ключ шардирования, каждый запрос идёт в один набор реплик; иначе каждый `select` обходит +все хранилища. Для первичного ключа — `space.get(key)` по каждому ключу параллельно. Ещё вариант — +хранимая процедура, принимающая список значений и выполняющая выборки на стороне сервера. + +### Производные методы Spring Data + +Поддерживаемые ключевые слова перечислены в [Динамические запросы](../spring-data/derived-methods.md). +Ключевые слова `In`, `NotIn`, `Like` не поддерживаются: такой метод завершается ошибкой +`InvalidDataAccessApiUsageException: Unsupported type ...`; метод с `And` или `Or` завершается +`UnsupportedOperationException`. Используйте подходы выше или +[хранимые процедуры](../spring-data/query-lua.md). diff --git a/documentation/doc-src/pages/spring-data/configuration.en.md b/documentation/doc-src/pages/spring-data/configuration.en.md index f300da28..c982dec8 100644 --- a/documentation/doc-src/pages/spring-data/configuration.en.md +++ b/documentation/doc-src/pages/spring-data/configuration.en.md @@ -110,7 +110,11 @@ To configure the client through a configuration file (for example, via 8. Time in ms after which to reconnect upon failed connection (`reconnect-after: 1000` - 1 second) 9. Sets a list of connection groups with descriptions of each - 10. Authentication protocol type + 10. Authentication protocol type. It must match the method the user's password is stored with, + i.e. `security.auth_type` (`box.cfg.auth_type`) of the cluster; otherwise the login fails with + `User not found or supplied credentials are invalid`. The default is `chap-sha1`, and it is + also the client default, so the parameter can be omitted. `pap-sha256` is available only in + Enterprise Edition (and TDB) and only over an encrypted (TLS) connection 11. The address to which the client connects 12. The port to which the client connects on `host` 13. The username for connecting to `Tarantool` diff --git a/documentation/doc-src/pages/spring-data/configuration.md b/documentation/doc-src/pages/spring-data/configuration.md index 0b6a8423..8b9df428 100644 --- a/documentation/doc-src/pages/spring-data/configuration.md +++ b/documentation/doc-src/pages/spring-data/configuration.md @@ -110,7 +110,11 @@ public class Config { 8. Время в мс, через которое необходимо сделать пересоединение при неудачном подключении (`reconnect-after: 1000` - 1 секунда) 9. Задает список групп подключения с описанием каждой из них - 10. Тип протокола аутентификации + 10. Тип протокола аутентификации. Должен совпадать с методом, которым хранится пароль + пользователя, то есть с `security.auth_type` (`box.cfg.auth_type`) кластера; иначе вход + завершится ошибкой `User not found or supplied credentials are invalid`. По умолчанию + `chap-sha1` — это же умолчание клиента, параметр можно не указывать. `pap-sha256` доступен + только в Enterprise Edition (и TDB) и только по шифрованному (TLS) соединению 11. Адрес, к которому подключается клиент 12. Порт, к которому подключается клиент на `host` 13. Имя пользователя для подключения к `Tarantool` diff --git a/documentation/doc-src/pages/spring-data/derived-methods.en.md b/documentation/doc-src/pages/spring-data/derived-methods.en.md index 31b6ae32..3f8304be 100644 --- a/documentation/doc-src/pages/spring-data/derived-methods.en.md +++ b/documentation/doc-src/pages/spring-data/derived-methods.en.md @@ -56,6 +56,11 @@ public interface PersonRepository extends PagingAndSortingRepository