Apache Ignite — Cache — Parte 1
Apache Ignite é uma plataforma para computação distribuída desenvolvida pela GridGain, posteriormente doada à Apache Foundation. Esta é uma série de artigos acerca da plataforma Ignite, cujo propõe-se contemplar o funcionamento alto nível, abordando algumas questões internas de implementação.
O Ignite está inserido em diversos contextos: cache, mensageria, streaming, monitoração, eventos, computação distribuída. Todos os módulos serão abordados nesta série. Como subir um cluster e node Ignite está documentado aqui.
Arquitetura de memória
O gerenciamento de memória é um dos diferenciais do Ignite, os dados são mantidos tanto em memória (RAM) e disco.
Caso a instância seja configurada como cache-aside, ou seja, não interaja com nenhuma camada de persistência (JDBC, Spring Data, Micronaut Data), ele utilizada os discos dos nodes como detentor da verdade — source of truth — em caso de inconsistência. Caso contrário, um processo chamado change-data-capture process (CDC) é responsável pela sincronização eventual dos dados no cluster.
Quando alocados em memória, parte é destinada on-heap, ou seja, é mantida em memória destinada à instância Java Virtual Machine (JVM) do próprio Ignite — cujo é uma aplicação Java —, o que pode ser desnecessariamente custoso, visto que ao executar o garbage collector ele analisa todo o heap, o que pode roubar alguns bons ciclos de CPU da aplicação. A outra forma é off-heap, fora da memória destinada ao Java, e gerenciado exclusivamente pelo Ignite. Segue a representação:
Cache
Features
- Estruturas de dados em cache
As interfaces IgniteSet<T> extends Set<T>
e IgniteQueue<T> extends BlockingQueue<T>
possibilitam criar estruturas de dados completamente distribuídas através dos nós, com backup, e particionamento próprio de maneira transparente ao usuário. Visto que estas estruturas herdam das implementações Java, todas operações nativas da linguagem, estão facilmente disponíveis.
- Implementação para ambas as operações síncronas e assíncronas
Todas as operações assíncronas implementam a interface IgniteFuture<V>
cujo implementação é semelhante ao CompletableFuture do Java, com a possibilidade de inserir função callback, ou encadear operações.
- Métricas avançadas
A API de cache expõe suas métricas, a quantidade de cache miss, cache hit, put time, get time, tempo de balanceamento de dados, tamanho do heap. Isso torna a experiência de usuário bastante transparente, bem como provê uma visão macro ao desenvolvedor.
- Serialização/deserialização otimizada
IgniteCache<Integer, Organization> cache = ignite.getOrCreateCache("cacheName");
Organization org = new Organization(
"Microsoft", new Address("1096 Eddy Street, San Francisco, CA"), OrganizationType.PRIVATE));
cache.put(1, org);
// Get cache that will get values as binary objects.
IgniteCache<Integer, BinaryObject> binaryCache = cache.withKeepBinary();// Get recently created organization as a binary object.
BinaryObject binary = binaryCache.get(1);// That is how magic happens
String name = binary.field("name");
A implementação BinaryObject.field()
nos permite deserializar campos específicos, otimizando a utilização de CPU, visto que apenas o dado será utilizado será processado.
Estruturas de dados distribuídas em cache
- Queue
IgniteQueue<String> queue = Ignition.ignite().queue("queueName", 0, null);int TIMES = 10;for (int i = 0; i < RETRIES; i++) {
String item = UUID.randomUUID() + "_" + i;
queue.put(item);
println("Queue item has been added: " + item);
}// IgniteQueue is fully compatible with Java library.
for (String item : queue)
println("Queue item: " + item);// Take items from queue head.
for (int i = 0; i < TIMES; i++)
println("Queue item has been read from queue head: " + queue.take());// Take items from queue head once again.
for (int i = 0; i < TIMES; i++)
println("Queue item has been read from queue head: " + queue.poll());
- Set
IgniteSet<String> set = Ignition.ignite().set("setName", null);for (int i = 0; i < 5; i++) {
String item = UUID.randomUUID() + "_" + i;
set.add(item);
}for (String item : set)
println("Set item: " + item);println(set.contains("1"));println("Set size: " + set.size());println("Removed: " + set.remove("0"));