Server Metrics and InnoDB
Context
This is why you need to learn about MySQL server metrics, especially InnoDB metrics:
That view is not unreasonable (or uncommon) because MySQL metrics are often discussed but never taught.
Even in my career with MySQL, I have never read or heard an exposition of MySQL metrics—and I have worked with people who created them.
The lack of pedagogy for MySQL metrics is due to a false presumption that metrics do not require understanding or interpretation because their meaning is self-evident.
That presumption has a semblance of truth when considering a single metric in isolation, like Threads_running: the number of threads running—what more is there to know?
But isolation is the fallacy: MySQL performance is revealed through a spectrum of MySQL metrics.
Replication
Context
This is why you need to learn the basics of MySQL replication, especially the cause of replication lag:
Replication lag is data loss. Seriously. Do not dismiss replication lag.
Transactions and Data Locks
Context
This is why you need to learn about MySQL transactions and data locks, which are closely related:
SELECT statement.
From our point of view as programmers, transactions appear conceptual: BEGIN, execute queries, and COMMIT.
Then we trust MySQL (and InnoDB) to uphold the ACID properties: atomicity, consistency, isolation, and durability.
When the application workload—queries, indexes, data, and access patterns—is well optimized, transactions are a nonissue with respect to performance.
(Most database topics are a nonissue when the workload is well optimized.)
But behind the scenes, transactions invoke a whole new world of considerations because upholding ACID properties while maintaining performance is not an easy feat.
Fortunately, MySQL shines at executing transactions.
Common Challenges
Context
This is why you need to learn about common challenges when using MySQL:
Copyright 2021 Daniel Nichter
No reproduction of this excerpt without permission
Key Points
- Split-brain occurs when two or more MySQL instances in the same replication topology are written to
- Split-brain is a detriment to data integrity—the data can no longer be trusted
- Data drift occurs when a replica becomes out of sync with the source
- Data drift is real but the origin of the drift is virtually impossible to pinpoint
- Data drift can be detected with pt-table-checksum
- ORMs can generate very poor queries and overall performance
- Schemas always change, so an online schema change (OSC) tool is must-have
- There are three popular OSC tools: pt-online-schema-change, gh-ost, and Spirit
- MySQL has non-standard SQL statements, options, and clauses
- Applications do not fail gracefully by default; it takes effort to fail gracefully
- High performance MySQL is difficult
Pitfalls
- Not taking into account the key points above
Hack MySQL Articles
Additional Resources
| Resource | Type | About |
|---|---|---|
| InnoDB and Online DDL | MySQL manual | Since schema changes are routine for developers, it’s important to understand how MySQL (and InnoDB) handle them because there are many edge cases depending on the specific type of change. |
| Spirit | Open source tool | The fastest, most advanced online schema change (OSC) change tool available. |
| gh-ost | Open source tool | The predecessor of Spirit. Both are solid tools, but use Spirit if possible. |
| pt-table-checksum | Open source tool | Still the only tool that can detection data drift on replicas |
Before gh-ost and Spirit, pt-online-schema-change (pt-osc) was the de facto standard tool. It's still widely used and referenced today, but as the author of pt-osc I strongly advise that you use Spirit instead. The only reason to use pt-osc is for rare (and risky) edge cases that Spirit (and usually gh-ost) do not support for good reason.
Cloud Performance
Context
This is why you need to learn about the reality of using MySQL in the cloud:
I wish it were as simple as “optimize the workload and you’re done”, but MySQL in the cloud raises unique considerations. The goal is to know and mitigate these cloud considerations so that you can focus on MySQL, not the cloud. After all, the cloud is nothing special: behind the proverbial curtain, it’s physical servers in a data center running programs like MySQL.
May 14, 2025
Create a unit testing framework for PostgreSQL using the pgTAP extension
Chapter 2: Serializability Theory (Concurrency Control Book)
Chapter 2 of Concurrency Control and Recovery in Database Systems (1987) by Bernstein, Hadzilacos, and Goodman is a foundational treatment of serializability theory. It is precise, formal, yet simple and elegant, a rare combination for foundational theory in a systems domain. Databases got lucky here: serializability theory is both powerful and clean.
The chapter builds up the theory step by step, introducing:
- Histories
- Serializable histories
- The Serializability Theorem
- Recoverability and its variants
- Generalized operations beyond reads/writes
- View equivalence
Each section motivates definitions clearly, presents tight formalism, and illustrates ideas with well-chosen examples.
2.1 Histories
This section lays the groundwork. It starts slow, and doesn't do anything fancy. It first defines what it means for the operations within a transaction to form a well-founded partial order. This intra-transaction ordering extends naturally to inter-transaction operations, forming a superset relation. Building on this, the book then defines a history as a partial order over operations from a set of transactions. Operations include reads (r₁[x]), writes (w₁[x]), commits (c₁), and aborts (a₁). Here the subscripts denote these operations all belong to transaction T₁. A history models the interleaving of these operations from different transactions, respecting the per-transaction order and ensuring all conflicting operations are ordered.
The abstraction here is elegant. The model omits values, conditionals, assignments, anything not visible to the scheduler. Only the structure of dependencies matters. This minimalism is a strength: it gives just enough to reason about correctness. The section is also careful to handle incomplete histories (i.e., prefixes), which is crucial for modeling crashes and recovery.
The discussion is very scheduler-centric. Since the scheduler is the component responsible for enforcing serializability, the model is tailored to what the scheduler can observe and act on. But this leads to missed opportunities as I mention below in Section 2.6 View equivalence.
2.2 Serializable Histories
This is where things get real. The goal is to define when a concurrent execution (history) is “as good as” some serial execution. The section has a simple game plan: define equivalence between histories (conflict equivalence), define serial histories, and finally say a history is serializable if it is equivalent to some serial history.
The chapter introduces the serialization graph (SG). Nodes are committed transactions. Edges capture conflicts: if T_i writes x before T_j reads or writes x, we add an edge T_i → T_j.
The formalism is tight, but not heavy. A good example is Fig 2-1 (p. 30), which compares equivalent and non-equivalent histories. (Minor typo: second w₁[x] in H₃ should be w₁[y].)
Theorem 2.1 (Serializability Theorem): A history is serializable iff its serialization graph is acyclic.
That's a beautiful punchline: serializability reduces to a cycle check in the conflict graph.
Credit is where it is due. In this case it is Jim Gray again! At the end of the section there is an acknowledgment stating that: The definition of equivalence and serializability used here and the Serializability Theorem are from "Gray, J.N., Lorie, R.A., Putzulo, G.R., Traiger, I.L. Granularity of Locks and Degrees of Consistency in a Shared Database. Research Report, IBM, September, 1975."
2.3 The Serializability Theorem
This section proves the big theorem about equivalence to a serial execution.
Intuitively, if SG(H) is acyclic, we can topologically sort it into a serial history equivalent to H. And if SG(H) has a cycle, then H cannot be serialized.
The figure shows an example. Edges in SG(H) constrain any valid serial order. A cycle means contradictory constraints, and hence, no valid serial order.
2.4 Recoverable Histories
Serializability ensures correctness in a crash-free world. But in practice, systems crash, transactions abort, and partial executions matter. To account for faults, this section defines increasingly strict notions:
Recoverable (RC): If T_j reads from T_i, then T_i must commit before T_j.
Avoids Cascading Aborts (ACA): T_j can only read from committed transactions.
Strict (ST): Reads and overwrites can happen only after the previous writer commits or aborts.
Theorem 2.2: ST ⊂ ACA ⊂ RC
The inclusion hierarchy is illustrated in Fig 2-2. The intersection of these properties with SR defines realistic schedules that can tolerate failures and support recovery.
Note that RC, ACA, and ST are prefix commit-closed properties, that is if they hold for history H, they also hold for any prefix of H. Nice and clean.
2.5 Operations Beyond Reads and Writes
I was happy to see this section. The authors show how the theory generalizes to operations beyond reads and writes, as long as we redefine the notion of conflict appropriately.
Two operations conflict if the order in which they execute affects: the final state of the database, or the values returned by operations. Fig. 2-3 gives the compatibility matrix to capture conflict relations between Read/Write/Inc/Dec. Increment(x) adds 1 to data item x and Decrement(x) subtracts 1 from x. An Inc or Dec does not return a value to the transaction that issues it.
The example history H₁₁ shows how to construct SG(H) even with these new operations. The theory lifts cleanly. Since SG(H_11) is acyclic, the generalized Serializability Theorem says that H_11 is SR. It is equivalent to the serial history T1 T3 T2 T4 which can be obtained by topologically sorting SG(H_11). DAGs FTW!
Still, this is a limited discussion. It's not expressive enough to model more general commutativity. But the seeds of CRDTs are here. In CRDTs, you model operations based on whether they commute under all interleavings. There is an on-going research direction trying to build a nice theory around more general operations and monotonicity concepts.
2.6 View Equivalence
This section introduces view serializability (VSR), a more permissive alternative to conflict serializability. Two histories are view equivalent if:
- Every read in one history reads from the same write as in the other.
- The final write to each data item is the same in both histories.
VSR allows more schedules than CSR. Consider the example below. T3 overwriting both x and y saves the day and masks the conflicts in T1 and T2. Interesting special case!
The book says that testing for VSR is NP-complete, and that kills its usefulness for schedulers. The authors argue that all practical schedulers use conflict serializability, and view serializability is too expensive to enforce. Yet they acknowledge it’s conceptually useful, especially for reasoning about multicopy databases (Chapters 5 and 8).
Reading this section, I had an interesting question. Is view serializability the same as client-centric consistency? They seem very similar. Client-centric consistency, as discussed in Crooks et al., PODC 2017, defines correctness in terms of what reads observe. VSR is similarly observational.
So while VSR wasn’t practical for scheduling, it found a second life in defining observational consistency in distributed systems. I think there's more fruit on this tree.
Configuring PgBouncer auth_type with Trust and HBA: Examples and Known Issues
Announcing the CedarDB Community Edition
Almost a year ago, we launched our company with our “Ode to Postgres”, with the goal of making the fruits of the highly successful Umbra research project available to you and everyone else. Umbra was created to incorporate all the lessons learned from the first 40 years of database systems into a new system built from the ground up to take advantage of modern hardware such as multi-core CPUs, fast SSDs, and cloud storage.
May 13, 2025
Amazon CloudWatch Database Insights applied in real scenarios
May 12, 2025
InnoDB Cluster Setup: Building a 3-Node High Availability Architecture
Query Booster: How Tinybird optimizes table schemas for you
Query Booster: How Tinybird optimizes table schemas for you
May 09, 2025
What is Convex & Why Should Developers Care?
Patroni: The Key PostgreSQL Component for Enterprise High Availability
May 08, 2025
Modular verification of MongoDB Transactions using TLA+
Joint work with Will Schultz.
A transaction groups multiple operations into an all-or-nothing logical-box to reduce the surface area exposed to concurrency control and fault recovery, simplifying the application programmer's job. Transactions support ACID guarantees: atomicity, consistency, isolation, durability. Popular isolation levels include, Read Committed (RC), Snapshot Isolation (SI), and Serializability (SER), which offer increasing protection against concurrency anomalies.
MongoDB Transactions
MongoDB’s transaction model has evolved incrementally.
- v3.2 (2015): Introduced single-document transactions using MVCC in the WiredTiger storage engine.
- v4.0 (2018): Extended support to multi-document transactions within a replica set (aka shard).
- v4.2 (2019): Enabled fully distributed transactions across shards.
Replica Set Transactions. All transaction operations are first performed on the primary using the WiredTiger transaction workflow/algorithm. Before commit of the transaction, all the updates are Raft-replicated with secondaries using the assigned timestamp to ensure consistent ordering. MongoDB uses Hybrid Logical Clocks (HLCs). The read timestamp reflects the latest stable snapshot. The commit timestamp is issued atomically and advances the cluster time. The default ReadConcern="Snapshot" ensures reads reflect a majority-committed snapshot with a given timestamp without yielding. And the WriteConcern="Majority" guarantees writes are durably replicated to a majority.
Distributed Transactions. MongoDB txns are general interactive transactions, rather than limited one-shot transactions as in DynamoDB. Clients interact through mongos, the transaction router, for executing a transaction. Mongos assigns the transaction a cluster-wide read timestamp and dispatches operations to relevant shard primaries. Each shard primary sets its local WiredTiger read timestamp and handles operations. If a conflict (e.g., write-write race) occurs, the shard aborts the transaction and informs mongos.
If the transaction is not aborted and the client asks for a commit, the mongos asks the first shard contacted for the transaction to coordinate the 2-phase commit (2PC). Handing off the commit-coordination to a Raft-replicated primary helps ensure durability/recoverability of the transaction. The shard primary launches a standard 2PC:
- Sends "prepare" to all participant shards.
- Each shard Raft-replicates a prepare oplog entry with a local prepare timestamp.
- The coordinator picks the max prepare timestamp returned from the shards as the global commit timestamp.
- Participant shards Raft-replicate commit at this timestamp and acknowledge back.
Using Alex Miller's Execution, Validation, Ordering, Persistence (EVOP) framework to describe MongoDB's distributed transactions, we get the following figure. MongoDB overlaps execution and validation. Execution stages local changes at each participant shard. All the while, Validation checks for write-write and prepare conflicts. Ordering and Persistence come later. The global commit timestamp provides the ordering for the transactions. Shards expose changes atomically using this timestamp.
MongoDB provides an MVCC+OCC flavor that prefers aborts over blocking. WiredTiger acquires locks on keys at first write access, causing later conflicting transactions to abort. This avoids delays from waiting, reducing contention and improving throughput under high concurrency.
TLA+ Modeling
Distributed transactions are difficult to reason about. MongoDB’s protocol evolved incrementally, tightly coupling the WiredTiger storage layer, replication, and sharding infrastructure. Other sources of complexity include aligning time across clusters, speculative majority reads, recovery protocol upon router failure, chunk migration by the catalog, interactions with DDL operations, and fault-tolerance.
To reason about MongoDB's distributed transaction protocol formally, we developed the first TLA+ specification of multi-shard database transactions at scale. Our spec is available publicly on GitHub. This spec captures the transaction behavior and isolation guarantees precisely.
Our TLA+ model is modular. It consists of MultiShardTxn, which encodes the high-level sharded transaction protocol, and Storage, which models replication and storage behavior at each shard. This modularity pays off big-time as we discuss in the model-based verification section below.
We validate isolation using a state-based checker based on the theory built by Crooks et al., PODC’17 and the TLA+ library implemented by Soethout. The library would take a log of operations and verify whether the transactions satisfy snapshot isolation, read committed, etc. This is a huge boost for checking/validating transaction isolation.
Our TLA+ model helps us explore how RC/WC selection for MongoDB tunable consistency levels affect transaction isolation guarantees. As MongoDB already tells its customers, MongoDB's "ReadConcern: majority" does not guarantee snapshot isolation. If you use it instead of "ReadConcern:Snapshot", you may get fractured reads: a transaction may observe some, but not all, of another transaction's effects.
Let's illustrate this with a simplified two-shard, two-transaction model from an earlier spec. T1 writes to K1 and K2 (sharded to S1 and S2, respectively) and commits via two-phase commit. T2 reads K1 before T1 writes it and K2 after T1 has committed. Due to `readConcern: majority`, it reads the old value of K1 and the new value of K2, violating snapshot isolation. The read is fractured.
You can explore this violation trace using a browser-based TLA+ trace explorer that Will Schultz built by following this link. The Spectacle tool loads the TLA+ spec from GitHub, interprets it using JavaScript interpreter, and shows/visualizes step-by-step state changes. You can step backwards and forwards using the buttons, and explore enabled actions. This makes model outputs accessible to engineers unfamiliar with TLA+. You can share a violation trace simply by sending a link.
Modeling helped us to clarify another subtlety: handling prepared but uncommitted transactions in the storage engine. If the transaction protocol ignores a prepare conflict, T2's read at the t-start snapshot might see a value that appears valid at its timestamp, but is later overwritten by T1's commit at an earlier timestamp, violating snapshot semantics. That means a read must wait on prepared transactions to avoid this problem. This is an example of cross-layer interaction between the transaction protocol and the underlying WiredTiger storage we mentioned earlier.
Finally, some performance stats. Checking Snapshot Isolation and Read Committed with the TLA+ model on an EC2 `m6g.2xlarge` instance takes around 10 minutes for small instances of the problem. With just two transactions and two keys, the space is large but manageable. Bugs, if they exist, tend to show up even in small instances.
Model-based Verification
We invested early in modularizing the storage model (a discipline Will proposed) which paid off. With a clean storage API between the transaction layer and storage engine, we can generate test cases from TLA+ traces that exercise the real WiredTiger code, not just validate traces. This bridges the gap between model and implementation.
WiredTiger, being a single-node embedded store with a clean API, is easy to steer and test. We exploit this by generating unit tests directly from the model. Will built a Python tool that:
- Parses the state graph from TLC,
- Computes a minimal path cover,
- Translates each path into Python unit tests,
- Verifies that implementation values conform to the model.
This approach is powerful: our handwritten unit tests number in the thousands, but the generator produces over 87,000 tests in 30 minutes. Each test exercises the precise contract defined in the model, systematically linking high-level protocol behavior to the low-level storage layer. These tests bridge model and code, turning formal traces into executable validations.
Permissiveness
We use TLA+ to also evaluate the permissiveness of MongoDB’s transaction protocol—the degree of concurrency it allows under a given isolation level without violating correctness. Higher permissiveness translates to fewer unnecessary aborts and better throughput. Modeling lets us quantify how much concurrency is sacrificed for safety, and where the implementation might be overly conservative.
To do this, we compare our protocol's accepted behaviors to abstract commit tests from Crooks et al PODC'17 paper. By comparing the transaction protocol behavior to idealized isolation specs, we can locate overly strict choices and explore safe relaxations.
For example, for read committed, MongoDB's transaction protocol (computed over our finite model/configurations) accepts around 76% of the behaviors allowed by the isolation spec. One source of restriction for Read Committed is prepare conflicts, a mechanism that prevents certain races. Disabling it raises permissiveness to 79%. In one such case, a transaction reads the same key twice and sees different values: a non-repeatable read. Snapshot isolation forbids this; but read committed allows it. MongoDB blocks it, maybe unnecessarily. If relaxing this constraint improves performance without violating correctness, it may be worth reconsidering.
Future Work
Our modular TLA+ specification brings formal clarity to a complex, distributed transaction system. But work remains on the following fronts:
- Model catalog behavior to ensure correctness during chunk migrations.
- Extend multi-grain modeling to other protocols.
- Generate test cases directly from TLA+ to bridge spec and code.
- Analyze and optimize permissiveness to improve concurrency.
Deploying External Read Replica in MySQL InnoDB Cluster
PostgreSQL Event Triggers without superuser access
May 07, 2025
Concurrency Control and Recovery in Database Systems: Preface and Chapter 1
I'm catching up on Phil Eaton's book club and just finished the preface and Chapter 1 of Concurrency Control and Recovery in Database Systems by Bernstein, Hadzilacos, and Goodman.
This book came out in 1987: two years before the fall of Berlin wall, 5 years before Windows 3.1, and before the Gray/Reuters book on Transaction Processing.
I was first surprised about why "Recovery" was featured prominently in the book title, but then realized that in 1987 there was no solid solution for recovery. The ARIES paper on the write-ahead-log came out in 1992.
Once I realized that, the structure made sense: concurrency control (Chapters 1–5), recovery (Chapters 6–7), and a forward-looking chapter on replication (Chapter 8), where they candidly admit: "No database systems that we know of support general purpose access to replicated distributed data."
- The Problem
- Serializability Theory
- Two Phase Locking
- Non-Locking Schedulers
- Multiversion Concurrency Control
- Centralized Recovery
- Distributed Recovery
- Replicated Data
Chapter 1: The Problem
Chapter 1 motivates the twin problems of concurrency control and recovery. It defines correct transaction behavior from the user’s point of view and introduces an internal system model that will be used throughout the book.
"User's point of view" here means an operational one, focused on histories of reads/writesm rather than the state-based, client-centric view I am fond of. (See my write-up of Seeing is Believing, Crooks et al., PODC 2017.)
With 40 years of hindsight, the definitions in Chapter 1 reads as straightforward: transactions, commit/abort, recoverability, cascading aborts, lost updates, serializability. The chapter uses many scenarios to illustrate how transactions interfere and how their effects must sometimes be undone upon abort.
I was initially surprised by the level of detail around undoing writes, until I remembered that this is a single-version model. No MVCC yet. Chapter 5 eventually gets there, explaining its benefits and trade-offs. But in 1987, MVCC was too costly in terms of storage/CPU, and OCC was not considered due to high contention workloads of that era. Instead, transactions were implemented with 2PL on resource-constrained single computers. Bernstein himself contributed a lot for the popularization of both MVCC and OCC.
The chapter closes with a model of a database system with four components: transaction manager (TM), scheduler, recovery manager (RM), and cache manager (CM). Transactions issue operations to the TM, which forwards them to the scheduler for concurrency control. The scheduler ensures serializability (and often strictness) by delaying, executing, or rejecting operations. The RM guarantees atomicity and durability, and uses Fetch/Flush from the CM to read/write persistent state. The CM handles movement between volatile and stable storage.
Coordination among modules relies on handshaking: if a module wants two operations ordered (say, write before flush), it must wait for an acknowledgment before issuing the next operation. This is a minimalist model of layered interaction that earns points for clarity and separation of concerns, although real systems today bear little resemblance to it.
RocksDB 10.2 benchmarks: large server
This post has benchmark results for RocksDB 10.x, 9.x, 8.11, 7.10 and 6.29 on a large server.
\tl;dr
- There are several big improvements
- There are no new regressions
- For the block cache hyperclock does much better than LRU on CPU-bound tests
I used RocksDB versions 6.0.2, 6.29.5, 7.10.2, 8.11.4, 9.0.1, 9.1.2, 9.2.2, 9.3.2, 9.4.1, 9.5.2, 9.6.2, 9.7.4, 9.8.4, 9.9.3, 9.10.0, 9.11.2, 10.0.1, 10.1.3, 10.2.1. Everything was compiled with gcc 11.4.0.
For 8.x, 9.x and 10.x the benchmark was repeated using both the LRU block cache (older code) and hyperclock (newer code). That was done by setting the --cache_type argument:
- lru_cache was used for versions 7.6 and earlier
- hyper_clock_cache was used for versions 7.7 through 8.5
- auto_hyper_clock_cache was used for versions 8.5+
Hardware
The server is an ax162-s from Hetzner with an AMD EPYC 9454P processor, 48 cores, AMD SMT disabled and 128G RAM. The OS is Ubuntu 22.04. Storage is 2 NVMe devices with SW RAID 1 and ext4.
Benchmark
Overviews on how I use db_bench are here and here.
All of my tests here use a CPU-bound workload with a database that is cached by RocksDB and the benchmark is run for 36 threads.
Tests were repeated for 3 workload+configuration setups:
- byrx - database is cached by RocksDB
- iobuf - database is larger than RAM and RocksDB uses buffered IO
- iodir - database is larger than RAM and RocksDB uses O_DIRECT
- fillseq
- load RocksDB in key order with 1 thread
- revrangeww, fwdrangeww
- do reverse or forward range queries with a rate-limited writer. Report performance for the range queries
- readww
- do point queries with a rate-limited writer. Report performance for the point queries.
- overwrite
- overwrite (via Put) random keys using many threads
- overwrite
- ~1.2X faster in modern RocksDB
- revrangeww, fwdrangeww, readww
- slightly faster in modern RocksDB
- fillseq
- ~15% slower in modern RocksDB most likely from new code added for correctness checks
- there are approximately zero regressions. The changes are small and might be normal variance.
- readww
- almost 3X faster with hyperclock because it suffers the most from block cache contention
- revrangeww, fwdrangeww
- almost 2X faster with hyperclock
- fillseq
- no change with hyperclock because the workload uses only 1 thread
- overwrite
- no benefit from hyperclock because write stalls are the bottleneck
- fillseq
- ~1.6X faster since RocksDB 7.x
- readww
- ~6% faster in modern RocksDB
- overwrite
- suffered from issue 12038 in versions 8.6 through 9.8
- revrangeww, fwdrangeww
- ~5% slower since early 8.x
- overwrite
- suffered from issue 12038 in versions 8.6 through 9.8. The line would be similar to what I show above had the base case been prior to 8.5 or earlier
- fillseq
- ~7% faster in 10.2 relative to 8.11
- revrangeww, fwdrangeww, readww
- unchanged from 8.11 to 10.2
- readww
- ~8% faster with hyperclock. The benefit here is smaller than above for byrx because the workload here is less CPU-bound
- revrangeww, fwdrangeww, overwrite
- slightly faster with hyperclock
- fillseq
- no change with hyperclock because the workload uses only 1 thread
- fillseq
- ~1.6X faster since RocksDB 7.x (see results above for iobuf)
- overwrite
- ~1.2X faster in modern RocksDB
- revrangeww, fwdrangeww, readww
- unchanged from 6.29 to 10.2
- overwrite
- might have a small regression (~3%) from 8.11 to 10.2
- revrangeww, fwdrangeww, readww, fillseq
- unchanged from 8.11 to 10.2
- there are small regressions and/or small improvements and/or normal variance