Skip to content

Repeatable read transaction prevents concurrent update from being applied #24399

@Legioth

Description

@Legioth

Description of the bug

When a shared signal is updated concurrently with an ongoing UIDL request, an effect associated with that UI will be run with the repeatable read transaction used for the ongoing request. If the repeatable read transaction contains the old value of the signal, then the effect will use that value instead of the new value and thus fail to update the UI based on the new value.

This can easily happen when the UIDL request triggers a backend change that uses an asynchronous message bus to reflect the change in a signal. If the message bus delivers the update before the UIDL request is completed, then the effect will read the old value and thus not update anything. If the message delivery is instead slightly slower, then the UIDL request is already finished and the repeatable read transaction is discarded. In that case, the effect will read the updated value and push out an UI update.

Expected behavior

I expect that the latest signal value is eventually shown regardless of what else goes on while the signal is updated.

I suspect the best way of doing this is to explicitly clear the automatic repeatable read transaction before running each pending access task.

Minimal reproducible example

@Route
public class MissingUpdate extends VerticalLayout {
    public MissingUpdate() {
        SharedNumberSignal signal = new SharedNumberSignal();
        add(new Span(() -> "Signal value: " + signal.getAsInt()));

        add(new Button("Update during round trip", click -> {
            // Prime repeatable read transaction
            signal.peek();
            try {
                Thread thread = Thread.startVirtualThread(() -> signal.incrementBy(1));
                // Keep request active until signal is updated
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }));

        add(new Button("Update after round trip", click -> {
            signal.peek();
            UI ui = UI.getCurrent();
            Thread.startVirtualThread(() -> {
                // Wait until request has been handled
                ui.accessSynchronously(() -> {});
                signal.incrementBy(1);
            });
        }));

        add(new Button("Read value", click -> {
            Notification.show("Value is " + signal.peek().intValue());
        }));
    }
}
  1. Open the view (in an application with @Push enabled)
  2. Click the "during" button
  3. Observe that the label is not updated
  4. Click the "read" button
  5. Observe that the notification shows the updated value
  6. Click the "after" button
  7. Observe that the label is updated

Versions

  • Vaadin / Flow version: Vaadin 25.1.5

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions