> Markdown version of [Transactions](https://vaadin.com/docs/next/flow/ui-state/transactions). Section index: [llms.txt](https://vaadin.com/docs/next/flow/llms.txt)

# Transactions

Transactions allow grouping multiple signal operations into a single atomic unit. All operations in a transaction either succeed or fail together, ensuring data consistency.

## <a id="basic-transactions"></a>Basic Transactions

Use `Signal.runInTransaction()` to execute multiple signal operations atomically:

```java
Signal.runInTransaction(() -> {
    // All operations here will be committed atomically
    firstNameSignal.set("John");
    lastNameSignal.set("Doe");
    ageSignal.set(30);
});
```

If any operation fails, none of the changes are applied. Observers see either the complete update or nothing at all - never a partial update.

## <a id="transaction-benefits"></a>Transaction Benefits

Transactions provide several guarantees:

- **Atomicity**

  All changes in a transaction succeed or fail together

- **Consistency**

  No observer sees partial updates

- **Isolation**

  Changes are not visible until the transaction commits

## <a id="verification-methods"></a>Verification Methods

Transactions support verification methods that ensure certain conditions are met before committing changes.

### <a id="verifying-expected-values"></a>Verifying Expected Values

The `verifyValue()` method verifies that a signal has a specific expected value:

```java
Signal.runInTransaction(() -> {
    // Transaction fails if current status is not "pending"
    statusSignal.verifyValue("pending");

    // Only executed if verification passes
    statusSignal.set("processing");
    processedBySignal.set(currentUser);
});
```

### <a id="verifying-list-item-position"></a>Verifying List Item Position

The `verifyPosition()` method verifies that an item is at a specific position in a list signal:

```java
Signal.runInTransaction(() -> {
    SharedValueSignal<Todo> firstItem = todoList.get().get(0);

    // Verify the item we want to update is still first
    todoList.verifyPosition(firstItem, ListPosition.first());

    // Update only if verification passes
    firstItem.set(new Todo("Updated first item", false));
});
```

### <a id="verifying-child-exists"></a>Verifying Child Exists

The `verifyChild()` method on `SharedListSignal` verifies that a child signal is still part of the list:

```java
Signal.runInTransaction(() -> {
    SharedValueSignal<Todo> itemSignal = todoList.get().get(0);

    // Verify the child signal still exists in the list before updating
    todoList.verifyChild(itemSignal);

    itemSignal.set(updatedValue);
});
```

## <a id="operation-results"></a>Operation Results

Signal operations return result objects that provide information about the operation and allow chaining.

### <a id="signaloperation"></a>SignalOperation

The base interface for all signal operations. Operations return a `CompletableFuture` that resolves when the operation completes:

```java
SignalOperation<String> op = stringSignal.set("new value");

// Get the result as a CompletableFuture
CompletableFuture<SignalOperation.ResultOrError<String>> result = op.result();

// Check if the operation has completed
if (result.isDone()) {
    // Get the result (blocks if not yet complete)
    SignalOperation.ResultOrError<String> resultOrError = result.get();
    if (resultOrError.successful()) {
        System.out.println("Operation completed successfully");
    }
}
```

### <a id="cancelableoperation"></a>CancelableOperation

Some operations that may retry (like `update()`) return a `CancelableOperation` that can be canceled:

```java
CancelableOperation<Integer> updateOp = counter.update(v -> v + 1);

// If needed, cancel the retry loop
updateOp.cancel();
```

The `update()` operation uses a compare-and-set approach. If another change occurs concurrently, the operation retries with the new value. Calling `cancel()` stops this retry loop.

> **Note:** A call to `cancel()` may not always be effective, as a succeeding operation might already be on its way to the server.

### <a id="insertoperation"></a>InsertOperation

List insert operations return an `InsertOperation` that provides immediate access to the inserted signal:

```java
SharedListSignal<Todo> todoList = new SharedListSignal<>(Todo.class);

InsertOperation<SharedValueSignal<Todo>> op = todoList.insertLast(new Todo("New task", false));

// Get the signal for the inserted item immediately
SharedValueSignal<Todo> insertedSignal = op.signal();

// You can start working with the signal right away
insertedSignal.update(todo -> new Todo(todo.text(), true));
```

This is useful when you need to reference the newly inserted item immediately after insertion.

### <a id="transactionoperation"></a>TransactionOperation

When running transactions, `Signal.runInTransaction()` returns a `TransactionOperation` that tracks the entire transaction. `TransactionOperation` extends `SignalOperation<Void>`, so you can check the transaction status via the inherited `result()` method:

```java
TransactionOperation<Void> txOp = Signal.runInTransaction(() -> {
    firstNameSignal.set("John");
    lastNameSignal.set("Doe");
});

// Check transaction status via the inherited result()
CompletableFuture<SignalOperation.ResultOrError<Void>> result = txOp.result();

if (result.isDone()) {
    System.out.println("Transaction committed successfully");
}
```

Transactions can also return a value. The `returnValue()` method provides direct access to the value returned by the transaction callback:

```java
TransactionOperation<String> txOp = Signal.runInTransaction(() -> {
    statusSignal.verifyValue("pending");
    statusSignal.set("confirmed");
    return "Order confirmed";
});

String returnValue = txOp.returnValue(); // "Order confirmed"
```

## <a id="bypassing-transactions"></a>Bypassing Transactions

Effect and computed signal callbacks run inside a read-only transaction to prevent accidental changes. If you need to modify a signal from within an effect (and you’re certain there’s no risk of infinite loops), use `runWithoutTransaction()`:

```java
Signal.effect(component, () -> {
    String value = sourceSignal.get();

    // WARNING: This might lead to infinite loops.
    // Do this only if absolutely necessary.
    Signal.runWithoutTransaction(() -> {
        derivedSignal.set(transformValue(value));
    });
});
```

> **Warning:** Using `runWithoutTransaction()` bypasses safety checks. Only use it when you’re certain the update won’t cause an infinite loop.

## <a id="best-practices"></a>Best Practices

### <a id="use-transactions-for-related-updates"></a>Use Transactions for Related Updates

When multiple signals should be updated together, use a transaction:

```java
// Good: Related updates in a transaction
Signal.runInTransaction(() -> {
    orderStatusSignal.set("shipped");
    shippedAtSignal.set(Instant.now());
    trackingNumberSignal.set(tracking);
});

// Avoid: Separate updates that might leave inconsistent state
orderStatusSignal.set("shipped");
shippedAtSignal.set(Instant.now()); // What if this fails?
trackingNumberSignal.set(tracking);
```

### <a id="use-verification-for-conditional-updates"></a>Use Verification for Conditional Updates

When updates depend on current state, use verification:

```java
Signal.runInTransaction(() -> {
    // Ensure we're updating the expected state
    statusSignal.verifyValue("pending");
    quantitySignal.verifyValue(expectedQuantity);

    statusSignal.set("confirmed");
    quantitySignal.set(newQuantity);
});
```

### <a id="prefer-update-for-single-signal-atomic-updates"></a>Prefer update() for Single-Signal Atomic Updates

For atomic updates based on current value, use `update()` instead of a transaction:

```java
// Preferred for single-signal updates
counter.update(v -> v + 1);

// Transaction is overkill for single updates
Signal.runInTransaction(() -> {
    counter.set(counter.get() + 1);
});
```

### <a id="keep-transactions-small"></a>Keep Transactions Small

Transactions hold resources while executing. Keep them focused on the minimal set of related operations:

```java
// Good: Small, focused transaction
Signal.runInTransaction(() -> {
    userSignal.set(updatedUser);
    lastModifiedSignal.set(Instant.now());
});

// Avoid: Large transactions with unrelated operations
Signal.runInTransaction(() -> {
    userSignal.set(updatedUser);
    lastModifiedSignal.set(Instant.now());
    // Don't include unrelated updates
    analyticsCounterSignal.incrementBy(1);
    logSignal.set(logMessage);
});
```
