---
title: 'PolyJuice: Learned Concurrency Control'
url: https://www.emergentmind.com/topics/polyjuice
type: topic
---

# PolyJuice: Learned Concurrency Control

Searching arXiv for the specified Polyjuice paper and related usage of the name.
Polyjuice is a learned concurrency control framework for in-memory OLTP systems that specializes concurrency control policy to a target workload through offline training rather than committing to a fixed hand-designed protocol such as optimistic concurrency control, two-phase locking, or IC3-style schemes. It represents concurrency control as a mapping $\pi : S \rightarrow A$ from the execution context of an individual access to a fine-grained action, and searches this policy space with offline evolutionary training to maximize commit throughput while preserving serializability through a fixed Silo-like validation procedure at commit time [2105.10329].

## 1. Problem setting and design objective

Concurrency control is a primary performance determinant in in-memory databases, but the performance of standard protocols varies sharply with workload characteristics such as contention, read/write mix, and transaction structure. In the reported TPC-C example, OCC is best at low contention, while it is outperformed under high contention; accordingly, no single concurrency control algorithm is consistently optimal across operating regimes [2105.10329].

Polyjuice targets this limitation directly. Rather than fixing one protocol or switching among a small set of known algorithms by heuristic rules, it defines a workload-specialized control layer over static stored procedures. The system assumes an in-memory, single-machine, multi-core OLTP engine with shared memory, worker threads, and transaction logic expressed as C++ stored procedures using a Silo-like API: `BeginTx`, `Get`, `Put`, and `CommitTx`. Transaction types are known a priori, and the learned concurrency control focuses on read-write transactions; read-only transactions are handled separately using snapshot or Silo’s mechanism [2105.10329].

A central motivation is the inadequacy of coarse-grained hybridization. Prior adaptive systems such as MOCC, Sundial, Tebaldi, CormCC, and Callas partition either by data or by transaction type, then select among a few known protocols. That design leaves no mechanism for mixing concurrency control decisions within a single transaction or even within different accesses to the same object. Polyjuice instead treats concurrency control decisions at the granularity of each static access within each transaction type, allowing per-access choices such as waiting, reading committed or visible uncommitted data, exposing writes early or late, and validating early or only at commit [2105.10329].

This suggests that Polyjuice should be understood less as a single algorithm than as a framework for synthesizing workload-specific algorithms from a richer search space than conventional protocol design typically considers.

## 2. Policy space and formalization

Polyjuice defines state as the execution context of a particular data access. The state comprises the transaction type `tx_type` and a static access identifier `acc_id`, where `acc_id` is derived from the program order of `Get`, `Put`, and `CommitTx` calls. If there are $n$ transaction types and the $i$-th type has $d_i$ static accesses, then the state-space size is

$$
|S| = \sum_{i=1}^{n} d_i
$$

and each state corresponds to a row of a policy table [2105.10329].

The action associated with each row has four components. First, a **wait** action specifies, for each transaction type $X$, either a target access-id $a_X$ or `NO_WAIT`; when a transaction depends on another transaction of type $X$, it waits until that transaction has executed through the specified access-id. Second, a **read-version** action selects either `CLEAN_READ`, which returns the latest committed version, or `DIRTY_READ`, which returns the latest visible uncommitted version from access lists. Third, a **write-visibility** action selects either `PRIVATE`, which keeps writes buffered locally, or `PUBLIC`, which exposes all buffered writes to per-record access lists. Fourth, an **early-validation** action determines whether validation is performed after the access [2105.10329].

The objective is to maximize commit throughput under a fixed workload:

$$
\pi^{*} = \arg\max_{\pi} J(\pi)
$$

where $J(\pi)$ is the empirically measured throughput of policy $\pi$. Polyjuice is conceptually inspired by reinforcement learning, but the implemented training method is offline search via evolutionary algorithms rather than online RL [2105.10329].

The per-row action space is combinatorial. Given $n$ transaction types with access counts $d_1,\dots,d_n$, the number of possible wait combinations is $d_1 d_2 \dots d_n$, and the binary choices for read-version, write-visibility, and early-validation contribute an additional factor of $2 \times 2 \times 2$. Thus the number of possible action combinations per row is

$$
a = d_1 d_2 \dots d_n \times 2 \times 2 \times 2
$$

and the full policy-space size is exponential in the number of states, namely $a^{|S|}$ [2105.10329].

Polyjuice also learns a backoff policy for retries after aborts. Its backoff state is defined by transaction type, execution status, and number of prior aborts, and the learned action is a multiplicative update to the per-type backoff time:

$$
\text{backoff} =
\begin{cases}
\text{backoff} \times (1 + \alpha_{t,i,\text{aborted}}), & \text{on abort} \\
\text{backoff} / (1 + \alpha_{t,i,\text{committed}}), & \text{on commit}
\end{cases}
$$

where the discrete $\alpha$ parameters are learned offline [2105.10329].

## 3. Execution model, data structures, and correctness

Polyjuice is implemented on top of Silo, replacing Silo’s concurrency control component with a policy-based engine. Runtime state is intentionally minimal for policy lookup: transaction type and access-id. Each worker thread maintains a pointer to the current in-memory policy table, and each access performs an array lookup to retrieve its corresponding action tuple [2105.10329].

The framework relies on several data structures. Per record, it maintains the latest committed value and a per-object access list containing visible uncommitted writes and reads. Per transaction, it maintains a local read-set `T.rset`, write-set `T.wset`, buffered operations `T.buffer`, a dependency set `T.dep`, and backoff state. Writes can remain private in `T.buffer` or become public when flushed to access lists; reads and writes are appended to access lists only after a successful validation point to avoid complicated undo and cascading-abort logic [2105.10329].

Execution is organized per access. For a `PUT`, Polyjuice looks up the record, retrieves the policy row for `(tx_type, acc_id)`, applies `WaitUntil(action.waits)`, appends the write to the transaction buffer and write-set, and, if the policy makes writes `PUBLIC`, performs early validation before flushing buffered operations to access lists. For a `GET`, the system similarly retrieves the policy row, performs waiting, chooses between committed and visible uncommitted versions according to the read-version action, appends the read to the buffer and read-set, and optionally executes early validation. Early validation itself uses the policy’s validation view, waits on dependencies as needed, and performs an OCC-style validation limited to accesses since the last validation point; on failure, it rolls back to the last successful validation point [2105.10329].

At commit time, Polyjuice always runs a fixed Silo-like validation protocol. The transaction first ensures that dirty-read dependencies are recorded in `T.deps`, then waits for all dependent transactions to commit or abort, then locks each record in `T.wset`, checks version-id stability for each record in `T.rset`, checks lock conflicts, and aborts on any failure. If validation succeeds, the transaction applies `T.wset` to the database with unique version-ids and releases locks [2105.10329].

Correctness is not learned. The learned policy governs interleaving, visibility, and early validation, but serializability is ensured by the fixed commit protocol. The paper sketches a reduction to Silo’s correctness argument: for any Polyjuice-committed transaction $T$, there exists a hypothetical Silo transaction $T'$ that starts after all of $T$’s dependencies have committed, observes the same committed state, and passes Silo’s validation iff $T$ passes Polyjuice’s. The appendix formulates the argument through serialization-graph dependencies and proves that any serialization-graph edge $T_i \to T_j$ implies

$$
\text{end-of-lock-stage}(T_i) < \text{end-of-lock-stage}(T_j)
$$

which rules out cycles [2105.10329].

## 4. Offline training and policy search

Polyjuice is trained offline rather than inside the production database. The intended workflow is to gather logs of executed transactions and inputs during peak workloads, replay them on a separate training machine, evaluate candidate policies over fixed-length episodes, and evolve the policy table toward higher throughput. The emulation is approximate because interleavings differ from production, but the paper argues that access patterns and contention structure remain sufficiently similar for effective policy search [2105.10329].

Each individual in the evolutionary algorithm consists of a concurrency control policy table and a backoff policy table. Fitness is commit throughput under the emulated workload. The training loop uses a population of candidate policies, warm-starts from known algorithms encoded in the policy space, mutates policy cells, evaluates resulting policies, and selects the top-performing candidates for the next generation. Mutation flips binary values such as `CLEAN_READ` versus `DIRTY_READ` or perturbs integer wait access-ids by a uniformly sampled delta from `[-2, 2]`, clipped to a valid range. The mutation rate and magnitude are decayed over time to promote convergence [2105.10329].

Warm-starting is important because standard protocols can be embedded directly in the policy space. OCC is approximated by `NO_WAIT`, `CLEAN_READ`, `PRIVATE`, and no early validation until commit. An approximated `2PL*` uses immediate public visibility for writes, committed-only reads, and waiting until dependent transactions commit. IC3- or Callas-like protocols are represented by combinations of early public writes, dirty reads, piece-level waiting, and early validation at boundaries. Polyjuice can therefore emulate familiar algorithms, but its search space also contains hybrid policies that do not correspond to any named protocol [2105.10329].

The paper also implemented a policy-gradient alternative using softmax-parameterized discrete choices and REINFORCE-like updates, but evolutionary search performed better. On TPC-C with 1 warehouse and 48 threads, the RL policy reached about 178K TPS after about 100 iterations, whereas the EA policy reached about 309K TPS after about 100 iterations. Training cost is nontrivial but bounded: for TPC-C with 1 warehouse and 48 threads, an iteration with 40 policies and 30-second runs takes about 80 seconds, and the reported training regime typically uses about 300 iterations [2105.10329].

A plausible implication is that Polyjuice’s practicality depends on workloads whose high-contention operating points recur often enough that overnight or background retraining amortizes the search cost.

## 5. Learned behavior and empirical performance

Polyjuice is evaluated on TPC-C, TPC-E, and a synthetic microbenchmark on a single machine with 2 NUMA nodes, a 56-core Intel Xeon Gold 6238R @ 2.20 GHz, and 188GB RAM per NUMA node. Runs last 30 seconds, each configuration is repeated five times, and the reported result is the median [2105.10329].

On TPC-C, Polyjuice is compared with Silo, 2PL, IC3, Tebaldi, and simulated CormCC. Under high contention with 2 warehouses and 48 threads, Polyjuice reaches 907K txn/sec, while IC3 and Tebaldi are about 580K, and the paper reports an improvement of about 56% over the best existing algorithm. Over different TPC-C and TPC-E configurations, the overall improvement range over the best existing baseline is 15% to 56%. Gains are largest under moderate to high contention and shrink as contention decreases; at 48 warehouses, Polyjuice converges to an OCC-like policy and remains about 8% slower than Silo because of extra metadata and bookkeeping overhead [2105.10329].

The factor analysis clarifies where performance comes from. Starting from an OCC-equivalent policy, adding early validation improves throughput by about 70% at 1 warehouse, and adding fine-grained waits increases throughput from 116K to 309K TPS. At 8 warehouses, early validation alone raises throughput from 467K to 1177K TPS, while additional dimensions have smaller marginal effect. This indicates that early validation is critical for reducing wasted work, whereas fine-grained waiting becomes especially important under heavier contention [2105.10329].

TPC-E highlights a different mechanism. Under high contention induced by Zipf parameter $\theta = 2$ to $4$ on `SECURITY` updates, Polyjuice outperforms the best baseline by 42% to 55%. The paper attributes much of this gain to learned backoff: Silo’s exponential backoff penalizes `TRADE_ORDER` too heavily, whereas Polyjuice learns that for some transaction types it is better not to increase backoff even under aborts. At $\theta = 3$, Polyjuice achieves about $18.5\times$ speedup from 1 to 48 threads, compared with about $12.3\times$ for IC3, $16.6\times$ for 2PL, and $9.4\times$ for Silo [2105.10329].

The TPC-C case study illustrates the system’s most characteristic behavior. In a dependency chain involving `NewOrder` and `Payment`, Polyjuice learns to make the `WAREHOUSE` read in `NewOrder` dirty but the `CUSTOMER` read clean. This shortens waits relative to IC3 by allowing `Payment` to wait only until an earlier `STOCK` access rather than a later `CUSTOMER` access, while simultaneously avoiding unnecessary serialization constraints that would arise if all reads were dirty. This mixed policy—dirty and clean reads within the same transaction type, combined with specific wait points—does not correspond to a conventional named protocol [2105.10329].

## 6. Robustness, deployment considerations, and relation to other work

Polyjuice policies are workload-specific rather than universal. The paper evaluates generalization by training on one TPC-C configuration and testing on others. Policies perform best when trained on the exact workload; degradation is moderate for nearby workloads but can be severe for distant ones. For example, a policy trained for 1 warehouse and evaluated at 48 warehouses can become significantly suboptimal, reaching 71% of Silo’s performance [2105.10329].

This workload specificity shapes the deployment model. The paper analyzes an e-commerce trace using a `conflict_rate` metric computed per hour from overlapping requests on the same product ID. Comparing today’s peak `conflict_rate` with tomorrow’s peak over 197 days, only 3 days had error greater than 20%, and most days were within 15%. The proposed operational rule is to retrain only when predicted `conflict_rate` differs by more than 15% from that of the workload on which the current policy was trained; under this rule, only 15 retraining instances were needed over 196 days. Policy switching itself is lightweight: switching from OCC to a 1-warehouse-optimized policy during a TPC-C run takes about 3 seconds for all threads to adopt the new policy, with monotonically improving throughput and no spike in aborts [2105.10329].

The same evidence also defines the main failure modes. Workloads that change faster than Polyjuice can train are not suitable. If the training workload is unrepresentative, performance may degrade substantially. Because replayed training traces preserve inputs but not exact execution outputs, training may mis-estimate contention patterns when workload behavior is extremely sensitive to returned values and complex data-dependent control flow [2105.10329].

In relation to prior work, Polyjuice subsumes a broad range of concurrency control designs as points in its policy space. It can encode OCC, approximate `2PL*`, and IC3/Callas-style behavior, while differing from MOCC, Sundial, Tebaldi, CormCC, and other adaptive schemes by searching over per-access micro-decisions rather than selecting one protocol per data partition or per transaction type. The framework is also positioned within the broader learned database literature, but unlike learned indexes or learned optimizers, runtime inference is a table lookup rather than a continuous model evaluation, and correctness is preserved by leaving validation outside the learned component [2105.10329].

A common source of confusion is the name itself. “Polyjuice” also refers to a GPT-2–based counterfactual generator for NLP [2101.00288]; that system is unrelated to concurrency control. In database systems usage, Polyjuice specifically denotes the learned, workload-specialized concurrency control framework introduced in 2021 [2105.10329].

Source: https://www.emergentmind.com/topics/polyjuice