Skip to content

refactor: remove PreExecCheck leaky abstraction and fix sync-over-async in ValkeyTransaction#359

Open
makubo-aws wants to merge 1 commit intovalkey-io:mainfrom
makubo-aws:refactor/remove-pre-exec-check-leaky-abstraction
Open

refactor: remove PreExecCheck leaky abstraction and fix sync-over-async in ValkeyTransaction#359
makubo-aws wants to merge 1 commit intovalkey-io:mainfrom
makubo-aws:refactor/remove-pre-exec-check-leaky-abstraction

Conversation

@makubo-aws
Copy link
Copy Markdown

Summary

Fixes #266

Problems

1. Leaky abstraction
ValkeyBatch defined a virtual bool PreExecCheck() hook called from ExecuteImpl(). The base implementation was a no-op (return true), and ValkeyTransaction overrode it to evaluate preconditions before submitting the MULTI/EXEC block. Batches have no concept of preconditions — this was transaction-specific logic leaking into the base class.

2. Sync-over-async
ValkeyTransaction.PreExecCheck() called b.ExecuteImpl().GetAwaiter().GetResult() — the last remaining sync-over-async call in the codebase. This risks deadlocks in environments with a synchronization context (e.g. ASP.NET, UI frameworks).

Changes

ValkeyBatch.cs

  • Remove virtual bool PreExecCheck()
  • Remove the if (PreExecCheck()) { ... } else { _tcs.SetResult(null); } guard from ExecuteImpl(). ExecuteImpl() now unconditionally executes the batch, which is correct — conditions are a transaction concern only.

ValkeyTransaction.cs

  • Remove protected override bool PreExecCheck()
  • Move condition evaluation into ExecuteAsync() directly, using await instead of .GetAwaiter().GetResult()
  • Short-circuit early (set _tcs result to null and return false) as soon as any condition fails, avoiding unnecessary work
  • Rename inner batch variable from b to conditionBatch for clarity

ValkeyBatch.PreExecCheck() was a virtual hook that existed solely to
support transaction preconditions. Batches have no concept of
preconditions, so this was transaction-specific logic leaking into the
base class.

Additionally, ValkeyTransaction.PreExecCheck() called
ExecuteImpl().GetAwaiter().GetResult() — the last remaining
sync-over-async call in the codebase — which risks deadlocks in
environments with a synchronization context (e.g. ASP.NET).

Changes:
- Remove virtual PreExecCheck() from ValkeyBatch and the
  if (PreExecCheck()) guard from ExecuteImpl(). ExecuteImpl() now
  unconditionally executes the batch, which is the correct behaviour
  for ValkeyBatch (conditions are a transaction concern only).
- Move condition evaluation into ValkeyTransaction.ExecuteAsync()
  directly, using await instead of .GetAwaiter().GetResult().
- Short-circuit early (set _tcs result to null and return false) as
  soon as any condition fails, avoiding unnecessary work.
- Rename the inner batch variable from b to conditionBatch for clarity.

Fixes valkey-io#266
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor: Remove PreExecCheck leaky abstraction from ValkeyBatch and eliminate sync-over-async in ValkeyTransaction

1 participant