Основные операции с кешем#

Получение экземпляра кеша#

Все операции с кешем выполняются через экземпляр IgniteCache. Его можно получить для существующего кеша или динамически создать новый:

Ignite ignite = Ignition.ignite();  

// Получите экземпляр кеша `myCache`.
// У разных кешей разные дженерики (generics).
IgniteCache<Integer, String> cache = ignite.cache("myCache");
IIgnite ignite = Ignition.Start();  

// Получите экземпляр кеша `myCache`.
// Аргументы дженериков (generics) используются только для удобства пользователя.
// Можно работать с любым кешем в контексте аргументов дженериков.
// Попытка получить запись несовместимого типа
// приведет к исключению.
ICache<int, string> cache = ignite.GetCache<int, string>("myCache");
IIgnite ignite = Ignition.Start();  

// Получите экземпляр кеша `myCache`.
// Аргументы дженериков (generics) используются только для удобства пользователя.
// Можно работать с любым кешем в контексте аргументов дженериков.
// Попытка получить запись несовместимого типа
// приведет к исключению.
ICache<int, string> cache = ignite.GetCache<int, string>("myCache");

Динамическое создание кеша#

Пример, как создать кеш динамическим способом:

Ignite ignite = Ignition.ignite();

CacheConfiguration<Integer, String> cfg = new CacheConfiguration<>();

cfg.setName("myNewCache");
cfg.setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL);

// Создать кеш с указанным названием, если его еще нет.
IgniteCache<Integer, String> cache = ignite.getOrCreateCache(cfg);
IIgnite ignite = Ignition.Start();  

// Создать кеш с указанным названием, если его еще нет.
var cache = ignite.GetOrCreateCache<int, string>("myNewCache");
IIgnite ignite = Ignition.Start();  

// Создать кеш с указанным названием, если его еще нет.
var cache = ignite.GetOrCreateCache<int, string>("myNewCache");

Если во время изменения базовой топологии используются методы, которые создают кеши, сгенерируется исключение org.apache.ignite.IgniteCheckedException:

javax.cache.CacheException: class org.apache.ignite.IgniteCheckedException: Failed to start/stop cache, cluster state change is in progress.
        at org.apache.ignite.internal.processors.cache.GridCacheUtils.convertToCacheException(GridCacheUtils.java:1323)
        at org.apache.ignite.internal.IgniteKernal.createCache(IgniteKernal.java:3001)
        at org.apache.ignite.internal.processors.platform.client.cache.ClientCacheCreateWithNameRequest.process(ClientCacheCreateWithNameRequest.java:48)
        at org.apache.ignite.internal.processors.platform.client.ClientRequestHandler.handle(ClientRequestHandler.java:51)
        at org.apache.ignite.internal.processors.odbc.ClientListenerNioListener.onMessage(ClientListenerNioListener.java:173)
        at org.apache.ignite.internal.processors.odbc.ClientListenerNioListener.onMessage(ClientListenerNioListener.java:47)
        at org.apache.ignite.internal.util.nio.GridNioFilterChain$TailFilter.onMessageReceived(GridNioFilterChain.java:278)
        at org.apache.ignite.internal.util.nio.GridNioFilterAdapter.proceedMessageReceived(GridNioFilterAdapter.java:108)
        at org.apache.ignite.internal.util.nio.GridNioAsyncNotifyFilter$3.body(GridNioAsyncNotifyFilter.java:96)
        at org.apache.ignite.internal.util.worker.GridWorker.run(GridWorker.java:119)

        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
        at java.base/java.lang.Thread.run(Thread.java:834)

При появлении такого исключения выполните операцию повторно.

Удаление кеша (cache destroy)#

Чтобы удалить кеш со всех узлов кластера, используйте метод destroy().

Java#
Ignite ignite = Ignition.ignite();

IgniteCache<Long, String> cache = ignite.cache("myCache");

cache.destroy();

Атомарные операции#

Когда экземпляры кеша получены, с ним можно выполнять операции get/put.

IgniteCache<Integer, String> cache = ignite.cache("myCache");

// Храните ключи в кеше (значения окажутся на разных узлах кеша).
for (int i = 0; i < 10; i++)
    cache.put(i, Integer.toString(i));

for (int i = 0; i < 10; i++)
    System.out.println("Got [key=" + i + ", val=" + cache.get(i) + ']');
using (var ignite = Ignition.Start("examples/config/example-cache.xml"))
{
    var cache = ignite.GetCache<int, string>("cache_name");

    for (var i = 0; i < 10; i++)
    {
        cache.Put(i, i.ToString());
    }

    for (var i = 0; i < 10; i++)
    {
        Console.Write("Got [key=" + i + ", val=" + cache.Get(i) + ']');
    }
}
IgniteConfiguration cfg;
cfg.springCfgPath = "/path/to/configuration.xml";

try
{
    Ignite ignite = Ignition::Start(cfg);

    Cache<int32_t, std::string> cache = ignite.GetOrCreateCache<int32_t, std::string>(CACHE_NAME);

	// Храните ключи в кеше (значения окажутся на разных узлах кеша).
    for (int32_t i = 0; i < 10; i++)
    {
        cache.Put(i, std::to_string(i));
    }

    for (int i = 0; i < 10; i++)
    {
        std::cout << "Got [key=" << i << ", val=" + cache.Get(i) << "]" << std::endl;
    }
}
catch (IgniteError& err)
{
    std::cout << "An error occurred: " << err.GetText() << std::endl;
    return err.GetCode();
}

Внимание

Пакетные операции, например putAll() и removeAll(), выполняются как последовательность атомарных операций. При получении частичного отказа по операциям генерируется исключение CachePartialUpdateException. Оно содержит список ключей, которые получили отказ по операции обновления.

Чтобы обновить коллекцию записей с помощью одной операции, воспользуйтесь транзакциями — подробнее о них написано в разделе «Выполнение транзакций».

Примеры основных атомарных операций#

// Операция `put-if-absent` возвращает предыдущее значение.
String oldVal = cache.getAndPutIfAbsent(11, "Hello");

// Операция `put-if-absent` возвращает логический флаг успешного выполнения.
boolean success = cache.putIfAbsent(22, "World");

// Операция `replace-if-exists` (противоположная `getAndPutIfAbsent`) возвращает предыдущее
// значение.
oldVal = cache.getAndReplace(11, "New value");

// Операция `replace-if-exists` (противоположная операции `putIfAbsent`) возвращает логический флаг
// успешного выполнения.
success = cache.replace(22, "Other new value");

// Операция `replace-if-matches`.
success = cache.replace(22, "Other new value", "Yet-another-new-value");

// Операция `remove-if-matches`.
success = cache.remove(11, "Hello");
 using (var ignite = Ignition.Start("examples/config/example-cache.xml"))
{
    var cache = ignite.GetCache<string, int>("cache_name");  

	// Операция `put-if-absent` возвращает предыдущее значение.
    var oldVal = cache.GetAndPutIfAbsent("Hello", 11);

	// Операция `put-if-absent` возвращает логический флаг успешного выполнения.
    var success = cache.PutIfAbsent("World", 22);

	// Операция `replace-if-exists` (противоположная для `getAndPutIfAbsent`) возвращает предыдущее
	// значение.    
	oldVal = cache.GetAndReplace("Hello", 11);  

	// Операция `replace-if-exists` (противоположная операции `putIfAbsent`) возвращает логический флаг
	// успешного выполнения.      
	success = cache.Replace("World", 22);

	// Операция `replace-if-matches`.
    success = cache.Replace("World", 2, 22);  

	// Операция `remove-if-matches`.
    success = cache.Remove("Hello", 1);
}
IgniteConfiguration cfg;
cfg.springCfgPath = "/path/to/configuration.xml";

Ignite ignite = Ignition::Start(cfg);

Cache<std::string, int32_t> cache = ignite.GetOrCreateCache<std::string, int32_t>("myNewCache");  

// Операция `put-if-absent` возвращает предыдущее значение.
int32_t oldVal = cache.GetAndPutIfAbsent("Hello", 11);  

// Операция `put-if-absent` возвращает логический флаг успешного выполнения.
boolean success = cache.PutIfAbsent("World", 22);

// Операция `replace-if-exists` (противоположная для `getAndPutIfAbsent`) возвращает предыдущее
// значение.
 oldVal = cache.GetAndReplace("Hello", 11);

// Операция `replace-if-exists` (противоположная операции `putIfAbsent`) возвращает логический флаг
// успешного выполнения.
success = cache.Replace("World", 22);

// Операция `replace-if-matches`.
success = cache.Replace("World", 2, 22);

// Операция `remove-if-matches`.
success = cache.Remove("Hello", 1);

Асинхронное выполнение операций#

У большей части операций с кешем есть асинхронные аналоги — в их названиях есть суффикс Async.

// Синхронный метод `get`.
V get(K key);

// Асинхронный метод `get`.
IgniteFuture<V> getAsync(K key);
// Синхронный метод `get`.
TV Get(TK key);

// Асинхронный метод `get`.
Task<TV> GetAsync(TK key);
// Синхронный метод `get`.
V Get(K key);

// Асинхронный метод `get`.
Future<V> GetAsync(K key);

Асинхронные операции возвращают объект, который представляет результат операции. Можно ожидать завершения операции блокирующим или неблокирующим способом.

Чтобы дождаться результатов неблокирующим способом, зарегистрируйте обратный вызов с помощью метода IgniteFuture.listen() или IgniteFuture.chain(). Обратный вызов выполняется после завершения операции.

IgniteCompute compute = ignite.compute();

// Закройте операцию асинхронно.
IgniteFuture<String> fut = compute.callAsync(() -> "Hello World");

// Дождитесь завершения операции и выведите ее результат.
fut.listen(f -> System.out.println("Job result: " + f.get()));
class HelloworldFunc : IComputeFunc<string>
{
    public string Invoke()
    {
        return "Hello World";
    }
}

public static void AsynchronousExecution()
{
    var ignite = Ignition.Start();
    var compute = ignite.GetCompute();  

	// Закройте операцию асинхронно.
    var fut = compute.CallAsync(new HelloworldFunc());

	// Дождитесь завершения операции и выведите ее результат.
     fut.ContinueWith(Console.Write);
}
/*
 * Функциональный класс.
 */
class HelloWorld : public compute::ComputeFunc<void>
{
    friend struct ignite::binary::BinaryType<HelloWorld>;
public:
    /*
     * Конструктор по умолчанию.
     */
    HelloWorld()
    {
        // Без операции.
    }

    /**
     * Обратный вызов.
     */
    virtual void Call()
    {
        std::cout << "Job Result: Hello World" << std::endl;
    }

};

/**
 * Структура бинарного типа. Определяет набор функций, которые нужны для сериализации и десериализации типа.
 */
namespace ignite
{
    namespace binary
    {
        template<>
        struct BinaryType<HelloWorld>
        {
            static int32_t GetTypeId()
            {
                return GetBinaryStringHashCode("HelloWorld");
            }

            static void GetTypeName(std::string& dst)
            {
                dst = "HelloWorld";
            }

            static int32_t GetFieldId(const char* name)
            {
                return GetBinaryStringHashCode(name);
            }

            static int32_t GetHashCode(const HelloWorld& obj)
            {
                return 0;
            }

            static bool IsNull(const HelloWorld& obj)
            {
                return false;
            }

            static void GetNull(HelloWorld& dst)
            {
                dst = HelloWorld();
            }

            static void Write(BinaryWriter& writer, const HelloWorld& obj)
            {
                // Без операции.
            }

            static void Read(BinaryReader& reader, HelloWorld& dst)
            {
                // Без операции.
            }
        };
    }
}

int main()
{
    IgniteConfiguration cfg;
    cfg.springCfgPath = "/path/to/configuration.xml";

    Ignite ignite = Ignition::Start(cfg);

    // Получите связующий экземпляр.
    IgniteBinding binding = ignite.GetBinding();

    // Зарегистрируйте класс в качестве вычислительной функции.
    binding.RegisterComputeFunc<HelloWorld>();

    // Получите экземпляр вычислений.
    compute::Compute compute = ignite.GetCompute();

    // Объявите экземпляр функции.
    HelloWorld helloWorld;

    // Сделайте асинхронный вызов.
    compute.RunAsync(helloWorld);
}

Выполнение обратных вызовов и пулы потоков#

Если асинхронная операция завершается к моменту передачи методу IgniteFuture.listen() или IgniteFuture.chain() обратного вызова, он выполняется синхронно с помощью вызванного потока. В противном случае обратный вызов выполняется асинхронно после завершения операции.

Потоки из публичного пула DataGrid выполняют обратные вызовы для асинхронных вычислительных операций. Вызов синхронного кеша и вычислительных операций из обратного вызова может привести к взаимоблокировкам из-за проблемы нехватки пулов (pools starvation). Можно создать пользовательский пул потоков для вложенного выполнения асинхронных вычислительных операций.

Обратные вызовы для асинхронных операций с кешем выполняются с помощью ForkJoinPool#commonPool, если в IgniteConfiguration#asyncContinuationExecutor не настроен другой исполнитель задачи:

  • Исполнитель по умолчанию безопасен для операций внутри обратного вызова.

  • Поведение по умолчанию изменилось в DataGrid версии 4.2110. До этого обратные вызовы асинхронных операций с кешем выполнялись из системного пула операций (striped).

  • Чтобы восстановить поведение по умолчанию, которое было до DataGrid версии 4.2110, используйте IgniteConfiguration.setAsyncContinuationExecutor(Runnable::run):

    • Предыдущее поведение по умолчанию может немного повысить эффективность операций, так как обратные вызовы выполняются без перенаправлений и расписаний.

    • Небезопасно — нельзя продолжать операции с кешем, пока системные потоки выполняют обратные вызовы. Если операции с кешем вызываются из обратных вызовов, возможно появление взаимоблокировок.