ID | IEP-28 |
Author | |
Sponsor | |
Created | 31-Oct-2018 |
Status | |
Motivation
The Apache Ignite cluster balance procedure with enabled persitence currently doesn't utilize network and storage device throughout to its full extent. The balance procedure processes cache data entries one by one which is not efficient enough for the cluster with enabled persistence.
Competitive Analysis
Profiling current balancing procedure
Rebalance procedure optimizations
Possible partition file sending approaches
The Apache Ignite can support cache rebalancing as transferring partition files using zero copy algorithm [1] based on an extension of communication SPI and Java NIO API. When the partition file has been transferred to the demander node there are two possible approaches can be implemented to preload entries from particular partition file.
Hot swap cache data storage
Disadvantages:
- A long and complex index reduild procedure that requires the development of additional crash recovery guarantees.
The index rebuild procedure starts immediately when the partition file is fully received from the supplier node. If the node crashes in the middle of the rebuilding index process it will have an inconsistent index state at the further node startup. To avoid this a new index-undo WAL record must be logged within rebuilding and used on node start to remove previously added index records.
Preload entries from loaded partition file
Disadvantages:
- The approach will require a new temporary FilePageStore to be initialized. It must be created as a part of the temporary cache group or in the separate temporary data region to provide reusing machinery of iteration over the full partition file.
Proposed Changes (Hot swap)
Process Overview
In the process of balancing data:
- demaner (receiver of partition files)
- supplier (sender of partition files).
The whole process is described in terms of rebalance single cache group and partition files would be rebalanced one-by-one:
- The demander node prepares the set of IgniteDhtDemandedPartitionsMap#full cache partitions to fetch;
- The demander node checks compatibility version (for example, 2.8) and starts recording all incoming cache updates to the new special storage – the temporary WAL storage;
- The demander node sends the GridDhtPartitionDemandMessage to the supplier node as usual;
- The supplier node receives GridDhtPartitionDemandMessage and starts the new checkpoint process and fixes cache partition file sizes;
- The supplier node creates an empty temporary file with .delta (e.g. part-0.bin.delta file) postfix for each cache partition file (in the same cache working directory or another configured);
- The supplier node starts tracking each pageId write attempt to these partition files
- When the write attempt happens, the thread that caused it reads the original copy of this page from the partition and flushes it to the corresponding .delta file;
- After it the thread writes the changed page data to the partition file;
- The supplier waits the checkpoint process ends;
- On the supplier for each cache partition file
- The process opens the partition file in read-only mode;
- Starts sending partition file (as it is with any concurrent writes) by chunks of predefined constant size (multiple of PageMemory size);
- After the partition file sent it starts sending corresponding .delta file;
- The demander node starts to listen to new type of incoming connections (a socket channel created event) from the supplier node;
- When the appropriate connection established the demander node for each cache partition file
- Receives file metadata information (corresponding cache group identifier, cache partition file name, file size)
- Writes data from the socket to the particular cache partition file from the beginning of the file
- After the original cache partition file received the node starts receiving corresponding .delta file
- The node reads data from the socket by chunks of PageMemory size and applies each received pageId to the partition file
- When the demander node receives the whole cache partition file
- The node begins the rebuild secondary indexes procedure over received partition file
- After it the thread begins to apply for data entries from the beginning of WAL-temporary storage;
- All async operations corresponding to this partition file still write to the end of temporary WAL;
- At the moment of WAL-temporary storage is ready to be empty
- Start the first checkpoint;
- Wait for the first checkpoint ends and own the cache partition;
- All operations now are switched to the partition file instead of writing to the temporary WAL;
- Schedule the temporary WAL storage deletion;
- The supplier node deletes all temporary files;
Сomponents to change
In terms of a high level overview a list of components that will be changed provided below.
CommunicationSpi
To benefit from zero copy we must delegate the file transferring to FileChannel#transferTo(long, long, java.nio.channels.WritableByteChannel) [2] because the fast path of transferTo method is only executed if the destination buffer inherits from an internal JDK class.
- The CommunicationSpi needs to support pipe connections between two nodes;
- The WritableByteChannel needs to be accesses on the supplier side;
- The ReadableByteChannel needs to be read on the demander side;
- The CommunicationListener must be extended to respond on new incoming pipe connections;
Preloader
A new implementation of cache entries preloader assume to be done. The new implementation must send and receive cache partition files over the CommunicationSpi channels by chunks of data with validation received items.
- The new layer over the cache partition file must support direct using of FileChannel#transferTo method over the CommunicationSpi pipe connection;
- The process manager must support transferring the cache partition file by chunks of predefined size (multiply to the page size) one by one;
- The connection bandwidth of the cache partition file transfer must have the ability to be limited at runtime;
Checkpointer
When the supplier node receives the cache partition file demand request it will send the file over the CommunicationSpi. The cache partition file can be concurrently updated by checkpoint thread during its transmission. To guarantee the file consistency Сheckpointer must use Copy-on-Write [3] tehnique and save a copy of updated chunk into the temporary file.
Catch-up temporary WAL
While the demander node is in the partition file transmission state it must save sequentially all cache entries corresponding to the MOVING partition into a new temporary storage. These entries will be applied later one by one on the newly received cache partition file. All asynchronous operations will be enrolled to the end of temporary storage during storage reads until it becomes fully read. The file-based FIFO approach assumes to be used by this process.
The temporary storage is chosen to be WAL-based. The storage must support to:
- Unlimited number of WAL-files to store temporary data records;
- Iterating over stored data records during an asynchronous writer thread insert new records;
- WAL-per-partiton approach is need to be used;
- Write operations to storage must have higher priority over read operations;
Expected problems to be solved
- We must stop updating indexes on demander when the data is ready to be transferred from the supplier node. All async cache updates on demander must not cause the index update;
- The previous partition metadata page and all stored meta information must be destroyed in PageMemory and restored from the new partition file;
Rebuild indexes
The node is ready to become partition owner when partition data is rebalanced and cache indexes are ready. For the message-based cluster rebalancing approach indexes are rebuilding simultaneously with cache data loading. For the file-based rebalancing approach, the index rebuild procedure must be finished before the partition state is set to the OWNING state.
Public API changes
The following changes needs to be made:
/**
* @return {@code True} if new type of direct connections supported.
*/
public default boolean channelConnectionSupported() {
return false;
}
/**
* @param remote Destination cluster node to communicate with.
* @param msg Configuration channel message.
* @throws IgniteSpiException If fails.
*/
public default IgniteSocketChannel channel(ClusterNode remote, T msg) throws IgniteSpiException {
throw new UnsupportedOperationException();
}
Failover and Recovery
Apache Ignite doesn't provide any recovery guarantees for the partitions with the MOVING state. The cache partitions will be fully loaded when the next rebalance procedure occurs.
Topology change
Each topology change event JOIN/LEFT/FAILED may or may not change cache affinity assignments of currently rebalacning caches. If assignments is not changed and the node is still needs partitions being rebalanced we can continue the current rebalance process (see for details IGNITE-7165).
Activation\deactivation
The rebalance procedure will be stopped if the deactivation event occurs. The single partition will be lost and will be preloaded on the next cluster rebalancing.
Unstable connection
A new connection must be established and the download process of partition file must be continued from the last successfully send cache partition chunk.
Crash recovery
To provide basic recovery guarantees we must to:
- Start the checkpoint process when the temporary WAL becomes empty;
- Wait for the first checkpoint ends and set OWNING status to partition;
Recovery from different stages:
- Supplier crashes when sending partition
- Demander crashes when receiving partition
- Demander crashes when applying temp WAL
Phase-2
The SSL must be disabled to take an advantage of Java NIO zero-copy file transmission using FileChannel#transferTo method. If we need to use SSL the file must be splitted on chunks the same way to send them over the socket channel with ByteBuffer. As the SSL engine generally needs a direct ByteBuffer to do encryption we can't avoid copying buffer payload from the kernel level to the application level.
Risks and Assumptions
A few notes can be mentioned:
- Zero-copy limitations – If operating system does not support zero copy, sending a file with FileChannel#transferTo might fail or yield worse performance. For example, sending a large file doesn't work well enough on Windows;
- Writing WAL io wait time – Under the heavy load of partition file transmission, writing to the temporary WAL storage may be slowing down. Since the loss of data of temporary WAL storage has no risks we can consider store the whole storage into the memory.
Discussion Links
http://apache-ignite-developers.2346864.n4.nabble.com/DISCUSSION-Design-document-Rebalance-caches-by-transferring-partition-files-td38388.html
Reference Links
- Zero Copy I: User-Mode Perspective – https://www.linuxjournal.com/article/6345
- Example: Efficient data transfer through zero copy – https://www.ibm.com/developerworks/library/j-zerocopy/index.html
- Copy-on-write – https://en.wikipedia.org/wiki/Copy-on-write
Tickets
key |
summary |
type |
updated |
assignee |
reporter |
priority |
status |
resolution |
JQL and issue key arguments for this macro require at least one Jira application link to be configured
|