Statement Ratio CLI Flag
July 31, 2026 · View on GitHub
The statement ratio feature provides fine-grained control over the distribution of different statement types that Gemini executes during a test run. This allows for precise workload modeling and targeted performance testing scenarios.
Overview
This feature controls the ratio of statements that Gemini will execute during a run using percentage-based configuration. All values must be between 0 and 1, and when adding up within each category, they must sum to 1.0 with a tolerance of 0.001 (due to floating-point precision).
How It Works
The statement ratio system uses cumulative distribution functions (CDFs) to efficiently select statement types based on the configured ratios:
- Configuration: Ratios are specified via JSON (inline or file)
- Validation: Gemini validates that all ratio groups sum to 1.0
- CDF Building: Internal CDFs are built for efficient random selection
- Runtime Selection: During execution, random numbers are used to select statement types according to the configured distribution
CLI Flag
--statement-ratios
This flag accepts statement ratio configuration in JSON format. You can provide the JSON in two ways:
- Inline JSON: Provide the JSON configuration directly as a command line argument
- File Path: Provide a path to a JSON file containing the configuration
Examples:
# Inline JSON (use single quotes to avoid shell interpretation)
gemini --statement-ratios='{"mutation":{"insert":0.7,"update":0.2,"delete":0.1}}' \
--oracle-cluster=192.168.1.10
# JSON file path
gemini --statement-ratios=/path/to/ratios.json \
--oracle-cluster=192.168.1.10
# Complex inline configuration (escaped for shell)
gemini --statement-ratios='{
"mutation": {
"insert": 0.5,
"update": 0.3,
"delete": 0.2,
"insert_subtypes": {
"regular_insert": 0.9,
"json_insert": 0.1
}
}
}' --oracle-cluster=192.168.1.10
JSON Configuration Structure
The configuration uses a hierarchical JSON structure with two main categories:
{
"mutation": {
"insert": 0.5,
"update": 0.3,
"delete": 0.2,
"insert_subtypes": {
"regular_insert": 0.9,
"json_insert": 0.1
},
"delete_subtypes": {
"whole_partition": 0.4,
"single_row": 0.3,
"clustering_subset": 0.2,
"multiple_partitions": 0.1
}
},
"validation": {
"select_subtypes": {
"single_partition": 0.6,
"multiple_partition": 0.3,
"clustering_range": 0.05,
"multiple_partition_clustering_range": 0.04,
"single_index": 0.01
}
}
}
Note: The values in the above example are not the default values, but demonstrate the JSON structure.
Configuration Breakdown
Mutation Ratios (mutation)
Controls the distribution of data modification operations:
insert: Ratio of INSERT statements (0.0-1.0)update: Ratio of UPDATE statements (0.0-1.0)delete: Ratio of DELETE statements (0.0-1.0)
Note: These three values must sum to 1.0
Renormalization when a type is filtered
Some phases exclude a mutation type entirely — most importantly warmup and
any no-delete worker, which filter delete so the warmup phase populates data
without removing it.
When a type is filtered, its probability mass is redistributed proportionally across the remaining types, preserving their configured ratio to one another. It is not reassigned to any single type.
With insert: 0.2, update: 0.3, delete: 0.5, a worker that filters delete
generates:
| Type | Configured | Effective (delete filtered) |
|---|---|---|
insert | 0.2 | 0.2 / (0.2 + 0.3) = 0.4 |
update | 0.3 | 0.3 / (0.2 + 0.3) = 0.6 |
A type configured to 0.0 is never generated, even when it is the only type
left after filtering. Setting insert: 0.0 means no INSERTs — including during
warmup.
⚠️ Behaviour change. Earlier versions dumped the filtered mass onto
insertinstead of redistributing it, which skewed the insert:update proportion during warmup and could generate INSERTs even withinsert: 0.0. Correcting this shifts the effective warmup mix for every configuration — with the defaults, from ≈0.75/0.25 to ≈0.737/0.263 insert:update. Expect a corresponding shift in run baselines.
Configurations with no valid candidate
Because the three ratios only have to sum to 1.0 — there is no per-type minimum —
it is possible to configure a workload where a filtering phase has nothing left
to generate. For example insert: 0.0, update: 0.0, delete: 1.0 is accepted at
startup, but a warmup worker filters delete and is left with only zero-weight
types.
Rather than silently substituting a statement type the phase excluded, gemini
terminates the mutation worker with a clear error and stops the run. The
condition is permanent — the ratios and the phase's filter are both fixed for
the life of the run — so no retry could recover. If you hit this, either give
insert or update a non-zero ratio, or do not run a warmup / no-delete phase
with a delete-only configuration.
Insert Subtypes (insert_subtypes)
Fine-grained control over INSERT statement types:
regular_insert: Standard INSERT statementsjson_insert: INSERT statements with JSON data
Note: These values must sum to 1.0
Delete Subtypes (delete_subtypes)
Fine-grained control over DELETE statement types:
whole_partition: DELETE entire partitions (usesReplaceNext()to recycle the partition)single_row: DELETE a specific row identified by all partition + clustering keys (uses tracked rows from validation)cluster: DELETE a range of rows sharing a clustering key prefix (uses tracked rows from validation)multiple_partitions: DELETE across multiple partitions using IN clause
The single_row and cluster subtypes consume rows from the row tracker (populated during validation). When no tracked rows are available, they fall back to random clustering key values. See Targeted Deletions for details.
Note: These values must sum to 1.0
Validation Ratios (validation)
Controls the distribution of data validation operations:
Since validation currently only supports SELECT statements, only select_subtypes is available:
single_partition: SELECT from a single partitionmultiple_partition: SELECT from multiple partitionsclustering_range: SELECT with clustering range conditionsmultiple_partition_clustering_range: SELECT with clustering ranges across partitionssingle_index: SELECT using indexes (falls back tosingle_partitionif no indexes exist)
Note: These values must sum to 1.0
Default Configuration
If no ratios are specified, Gemini uses this balanced default configuration:
{
"mutation": {
"insert": 0.70,
"update": 0.25,
"delete": 0.05,
"insert_subtypes": {
"regular_insert": 0.9,
"json_insert": 0.1
},
"delete_subtypes": {
"whole_partition": 0.3,
"single_row": 0.3,
"clustering_subset": 0.3,
"multiple_partitions": 0.1
}
},
"validation": {
"select_subtypes": {
"single_partition": 0.6,
"multiple_partition": 0.3,
"clustering_range": 0.05,
"multiple_partition_clustering_range": 0.04,
"single_index": 0.01
}
}
}
Partial Configuration
Gemini supports partial configurations with intelligent defaults. When you provide incomplete subtype configurations, Gemini will automatically calculate the missing values to ensure all ratios sum to 1.0.
Automatic Subtype Completion
When specifying subtypes partially, Gemini uses these rules:
- Insert Subtypes: If only one subtype is specified, the other is automatically calculated as
1.0 - specified_value - Delete Subtypes: If some subtypes are specified, the remaining values are distributed equally among unspecified subtypes
- Select Subtypes: If some subtypes are specified, the remaining values are distributed equally among unspecified subtypes
Examples of Partial Configuration
Only main mutation ratios:
{
"mutation": {
"insert": 0.8,
"update": 0.15,
"delete": 0.05
}
}
Result: All subtypes use default values
Partial insert subtypes:
{
"mutation": {
"insert": 0.7,
"update": 0.2,
"delete": 0.1,
"insert_subtypes": {
"regular_insert": 0.8
}
}
}
Result: json_insert automatically set to 0.2 (1.0 - 0.8)
Partial delete subtypes:
{
"mutation": {
"delete_subtypes": {
"whole_partition": 0.5,
"single_row": 0.3
}
}
}
Result: single_column and multiple_partitions each set to 0.1 ((1.0 - 0.8) / 2)
Empty subtype objects:
{
"mutation": {
"insert_subtypes": {}
}
}
Result: Default values are preserved
File Path Support
The --statement-ratios flag supports JSON file paths for complex configurations:
# Create a ratios configuration file
cat > custom-ratios.json << EOF
{
"mutation": {
"insert": 0.6,
"update": 0.3,
"delete": 0.1,
"insert_subtypes": {
"regular_insert": 0.8,
"json_insert": 0.2
},
"delete_subtypes": {
"whole_partition": 0.5,
"single_row": 0.2,
"clustering_subset": 0.2,
"multiple_partitions": 0.1
}
},
"validation": {
"select_subtypes": {
"single_partition": 0.7,
"multiple_partition": 0.2,
"clustering_range": 0.05,
"multiple_partition_clustering_range": 0.04,
"single_index": 0.01
}
}
}
EOF
# Use the file
gemini --statement-ratios=custom-ratios.json --oracle-cluster=192.168.1.10
Validation and Error Handling
Gemini performs strict validation on ratio configurations:
- Range Check: All values must be between 0.0 and 1.0
- Sum Validation: Each ratio group must sum to 1.0 (±0.001 tolerance)
- Structure Validation: JSON structure must match expected format
- File Validation: If a file path is provided, the file must exist and be readable
If validation fails, Gemini will display a clear error message and exit.
Use Cases
High-Insert Workload Testing
{
"mutation": {
"insert": 0.9,
"update": 0.08,
"delete": 0.02
}
}
Balanced CRUD Operations
{
"mutation": {
"insert": 0.4,
"update": 0.4,
"delete": 0.2
}
}
JSON-Heavy Workload
{
"mutation": {
"insert": 0.8,
"update": 0.15,
"delete": 0.05,
"insert_subtypes": {
"regular_insert": 0.3,
"json_insert": 0.7
}
}
}
Missing Features
- Update Subtypes: Currently, Gemini only supports regular UPDATE statements. Planned features include updates for multiple rows and multiple partitions.
Performance Impact
There is no performance impact from using statement ratios. The feature only controls which types of statements are executed - the overhead of random number generation for statement selection already exists in Gemini's core operation.