All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.infinispan.server.resp.commands.tx.EXEC Maven / Gradle / Ivy

There is a newer version: 15.1.4.Final
Show newest version
package org.infinispan.server.resp.commands.tx;

import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.function.Function;

import org.infinispan.AdvancedCache;
import org.infinispan.commons.util.concurrent.CompletableFutures;
import org.infinispan.commons.util.concurrent.CompletionStages;
import org.infinispan.server.resp.Resp3Handler;
import org.infinispan.server.resp.RespCommand;
import org.infinispan.server.resp.RespErrorUtil;
import org.infinispan.server.resp.RespRequestHandler;
import org.infinispan.server.resp.commands.Resp3Command;
import org.infinispan.server.resp.commands.TransactionResp3Command;
import org.infinispan.server.resp.serialization.ByteBufferUtils;
import org.infinispan.server.resp.serialization.Resp3Response;
import org.infinispan.server.resp.serialization.RespConstants;
import org.infinispan.server.resp.tx.RespTransactionHandler;
import org.infinispan.server.resp.tx.TransactionCommand;

import io.netty.channel.ChannelHandlerContext;

/**
 * `EXEC` command.
 * 

* Retrieves the queued commands from the handler and executes them serially. If the handler returns a null * list, the transaction is aborted. The user installed a watch for a key, and the value was updated. *

* This implementation executes the operations outside a transaction context on the Infinispan level. Another user can * see the state changing before the queue finishes. Even though Redis does not have the concept of commit/rollback, * which means that everything is applied, the current implementation provides a weaker isolation level. * * @since 15.0 * @see Redis Documentation * @author José Bolina */ public class EXEC extends RespCommand implements Resp3Command, TransactionResp3Command { public EXEC() { super(1, 0, 0, 0); } @Override public CompletionStage perform(Resp3Handler handler, ChannelHandlerContext ctx, List arguments) { RespErrorUtil.customError("EXEC without MULTI", handler.allocator()); return handler.myStage(); } @Override public CompletionStage perform(RespTransactionHandler handler, ChannelHandlerContext ctx, List arguments) { Resp3Handler next = handler.respServer().newHandler(); CompletionStage cs = handler.performingOperations(ctx) .thenCompose(commands -> perform(commands, handler, next, ctx)); return next.stageToReturn(cs, ctx, ignore -> next); } private CompletionStage perform(List commands, RespTransactionHandler curr, Resp3Handler next, ChannelHandlerContext ctx) { // An error happened while in transaction context. This error means more of a setup error, failed enqueueing, etc. // One such example, trying to WATCH while in transaction context. This is not a command failed to execute yet. // See: https://redis.io/docs/interact/transactions/#errors-inside-a-transaction if (curr.hasFailed()) { return CompletableFuture.supplyAsync(() -> { RespErrorUtil.transactionAborted(curr.allocator()); return null; }, ctx.executor()); } // Using WATCH and keys changed. Abort transaction. if (commands == null) { // Should write `(nil)` since the transaction is aborted. return CompletableFuture.supplyAsync(() -> { Resp3Response.nulls(curr.allocator()); return null; }, ctx.executor()); } AdvancedCache cache = curr.cache(); // Redis has a serializable isolation. Without batching we have read-uncomitted. // Using the batching API we have a few more useful features. boolean batchEnabled = cache.getCacheConfiguration().invocationBatching().enabled(); if (!batchEnabled) { log.multiKeyOperationUseBatching(); } else { cache.startBatch(); } return CompletableFuture.supplyAsync(() -> { // Unfortunately, we need to manually write the prefix before proceeding with each operation. ByteBufferUtils.writeNumericPrefix(RespConstants.ARRAY, commands.size(), curr.allocator()); return orderlyExecution(next, ctx, commands, 0, CompletableFutures.completedNull()) .whenComplete((ignore, t) -> { if (batchEnabled) cache.endBatch(true); }); }, ctx.executor()).thenCompose(Function.identity()); } private CompletionStage orderlyExecution(Resp3Handler handler, ChannelHandlerContext ctx, List commands, int index, CompletionStage current) { if (index == commands.size()) { return current; } TransactionCommand command = commands.get(index); return CompletionStages.handleAndCompose(current, (r, ignore) -> orderlyExecution(handler, ctx, commands, index + 1, command.perform(handler, ctx))); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy