Temporal Patterns#
Use global_iter() with math functions to make generated data change shape over the life of a workload. This lets you model real-world patterns like shifting popularity, price inflation, seasonal traffic, periodic spikes, and sensor calibration drift; all without external scripts.
Every pattern on this page uses the same building block: an expression that takes global_iter() as input and produces a value whose distribution or magnitude changes as the iteration counter grows.
The full config is at
_examples/temporal_patterns/crdb.yaml.
Globals Are User-Defined#
Names in the following globals: block are arbitrary and become variables available in any expression. The patterns below use names like total_iters and initial_skew, but you can name them whatever you like.
Patterns#
Zipf Skew Drift#
Product popularity starts nearly uniform and concentrates over time. The zipf() skew parameter is linearly interpolated from initial_skew to final_skew using global_iter().
Globals
globals:
total_iters: 10000
products: 100
initial_skew: 1.2 # nearly uniform
final_skew: 3.0 # heavily concentrated on the first productExpression
zipf(initial_skew + (final_skew - initial_skew) * global_iter() / total_iters, 1, products - 1)Config
- name: insert_view
type: exec
args:
- zipf(initial_skew + (final_skew - initial_skew) * global_iter() / total_iters, 1, products - 1)
- initial_skew + (final_skew - initial_skew) * global_iter() / total_iters
- global_iter()
query: |-
INSERT INTO product_views (product_id, skew_at_time, iteration)
VALUES ($1::INT, $2::FLOAT8, $3::INT8)Verification query
SELECT
bucket,
count(*) AS total,
count(*) FILTER (WHERE product_id = 0) AS top_product,
round((count(*) FILTER (WHERE product_id = 0))::FLOAT8 / count(*)::FLOAT8 * 100, 1) AS top_pct,
repeat('▒', (round((count(*) FILTER (WHERE product_id = 0))::FLOAT8 / count(*)::FLOAT8 * 40))::INT) AS histogram
FROM (
SELECT *, ntile(10) OVER (ORDER BY iteration) AS bucket
FROM product_views
)
GROUP BY bucket
ORDER BY bucket; bucket | total | top_product | top_pct | histogram
---------+-------+-------------+---------+-------------------------------------------
1 | 2644 | 1225 | 46.3 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
2 | 2643 | 1959 | 74.1 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
3 | 2643 | 2440 | 92.3 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
4 | 2643 | 2593 | 98.1 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
5 | 2643 | 2632 | 99.6 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
6 | 2643 | 2637 | 99.8 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
7 | 2643 | 2641 | 99.9 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
8 | 2643 | 2642 | 100 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
9 | 2643 | 2643 | 100 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
10 | 2643 | 2643 | 100 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒Tuning
initial_skew/final_skew- controls how much the distribution shifts. A smaller gap produces a subtler drift; a larger gap makes it dramatic. Values near 1.0 are nearly uniform, values above 3.0 concentrate almost all traffic on the lowest-ranked items.total_iters- the iteration count at whichfinal_skewis reached. After this point the skew parameter stays atfinal_skew. Set this to roughly match your expected total iterations (see Estimating total iterations).
Logarithmic Growth#
Prices rise steeply at first then plateau. Models inflation, adoption curves, or any quantity with diminishing returns.
Globals
globals:
base_price: 50.0
products: 100Expression
floor(base_price * (1.0 + log(1.0 + global_iter() / 1000.0)) * 100.0) / 100.0Config
- name: insert_price
type: exec
args:
- uniform(1, products)
- floor(base_price * (1.0 + log(1.0 + global_iter() / 1000.0)) * 100.0) / 100.0
- global_iter()
query: |-
INSERT INTO price_history (product_id, price, iteration)
VALUES ($1::INT, $2::FLOAT8, $3::INT8)Verification query
SELECT
bucket,
round(avg(price)::NUMERIC, 2) AS avg_price,
round(min(price)::NUMERIC, 2) AS min_price,
round(max(price)::NUMERIC, 2) AS max_price,
repeat('▒', (round(avg(price) / max(avg(price)) OVER () * 40))::INT) AS histogram
FROM (
SELECT *, ntile(10) OVER (ORDER BY iteration) AS bucket
FROM price_history
)
GROUP BY bucket
ORDER BY bucket; bucket | avg_price | min_price | max_price | histogram
---------+-----------+-----------+-----------+-------------------------------------------
1 | 105.75 | 50.09 | 139.66 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
2 | 156.93 | 139.67 | 170.72 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
3 | 185.96 | 170.73 | 200.89 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
4 | 211.99 | 200.90 | 221.88 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
5 | 229.27 | 221.89 | 236.09 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
6 | 242.19 | 236.10 | 247.77 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
7 | 252.52 | 247.78 | 256.94 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
8 | 260.88 | 256.95 | 264.72 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
9 | 268.11 | 264.72 | 271.35 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
10 | 274.40 | 271.35 | 277.37 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒Tuning
base_price- starting price before any growth.- The divisor inside
log(1.0 + global_iter() / 1000.0)controls how quickly the curve rises. A smaller divisor (e.g.100.0) makes it steeper; a larger divisor (e.g.10000.0) flattens it. floor(... * 100.0) / 100.0rounds to cents. Remove thefloor/division to keep full precision.
Sine Wave Seasonality#
Request counts oscillate around a slowly growing baseline. Models daily/weekly traffic cycles, seasonal patterns, or any periodic signal riding on a trend.
Globals
globals:
traffic_period: 50000
traffic_amplitude: 100.0
base_traffic: 100.0Expression
floor(abs(base_traffic + 0.5 * sqrt(global_iter()) + amplitude * sin(2.0 * pi * global_iter() / period)))Config
- name: insert_traffic
type: exec
args:
- set_rand(['GET /products', 'GET /orders', 'POST /checkout', 'GET /search'], [])
- floor(abs(base_traffic + 0.5 * sqrt(global_iter()) + traffic_amplitude * sin(2.0 * pi * global_iter() / traffic_period)))
- global_iter()
query: |-
INSERT INTO traffic_log (endpoint, request_count, iteration)
VALUES ($1, $2::INT, $3::INT8)Verification query
SELECT
bucket,
round(avg(request_count)::NUMERIC, 1) AS avg_requests,
min(request_count) AS min_requests,
max(request_count) AS max_requests,
repeat('▒', (round(avg(request_count) / max(avg(request_count)) OVER () * 40))::INT) AS histogram
FROM (
SELECT *, ntile(20) OVER (ORDER BY iteration) AS bucket
FROM traffic_log
)
GROUP BY bucket
ORDER BY bucket; bucket | avg_requests | min_requests | max_requests | histogram
---------+--------------+--------------+--------------+-------------------------------------------
1 | 149.5 | 101 | 186 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
2 | 212.8 | 186 | 236 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
3 | 248.8 | 236 | 257 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
4 | 253.9 | 247 | 257 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
5 | 232.8 | 215 | 247 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
6 | 192.7 | 170 | 215 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
7 | 147.7 | 127 | 170 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
8 | 111.6 | 100 | 127 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒
9 | 96.9 | 96 | 100 | ▒▒▒▒▒▒▒▒▒▒▒▒
10 | 107.2 | 98 | 121 | ▒▒▒▒▒▒▒▒▒▒▒▒▒
11 | 142.9 | 121 | 167 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
12 | 199.3 | 167 | 229 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
13 | 256.6 | 229 | 282 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
14 | 301.4 | 283 | 316 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
15 | 322.7 | 316 | 325 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
16 | 315.4 | 303 | 324 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
17 | 282.0 | 258 | 302 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
18 | 233.1 | 209 | 258 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
19 | 186.0 | 167 | 209 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
20 | 154.7 | 148 | 167 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒Tuning
traffic_period- iterations per full sine cycle. This is the most important parameter. If each bucket in your verification query averages over a full cycle or more, the sine wave averages to zero and you only see the baseline trend. See Matching period to your run.traffic_amplitude- peak deviation from the baseline. Must be large enough relative to the baseline to be visible. Ifbase_trafficis 100 and amplitude is 10, the wave is only a 10% wobble.0.5 * sqrt(global_iter())- the growth coefficient on the baseline trend. A larger coefficient makes the upward trend dominate the wave; a smaller one lets the oscillation stand out.abs(...)- prevents negative request counts when the trough of the sine wave dips below zero.
Periodic Spikes#
Severity surges at regular intervals then drops to near zero. Models periodic maintenance windows, batch job impacts, or recurring failure modes.
Expression
pow(cos(pi * mod(global_iter(), interval) / interval), 2.0) * 10.0cos² produces a smooth pulse: maximum (10.0) when mod(iter, interval) = 0, minimum (0.0) at the midpoint of each interval.
Globals
globals:
spike_interval: 25000Config
- name: insert_error
type: exec
args:
- uniform(500, 599)
- pow(cos(pi * mod(global_iter(), spike_interval) / spike_interval), 2.0) * 10.0
- global_iter()
query: |-
INSERT INTO error_events (error_code, severity, iteration)
VALUES ($1::INT, $2::FLOAT8, $3::INT8)Verification query
SELECT
bucket,
round(avg(severity)::NUMERIC, 2) AS avg_severity,
round(max(severity)::NUMERIC, 2) AS max_severity,
repeat('▒', (round(avg(severity) / max(avg(severity)) OVER () * 40))::INT) AS histogram
FROM (
SELECT *, ntile(20) OVER (ORDER BY iteration) AS bucket
FROM error_events
)
GROUP BY bucket
ORDER BY bucket; bucket | avg_severity | max_severity | histogram
---------+--------------+--------------+-------------------------------------------
1 | 8.06 | 10.00 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
2 | 1.43 | 4.67 | ▒▒▒▒▒▒
3 | 2.85 | 6.56 | ▒▒▒▒▒▒▒▒▒▒▒▒
4 | 9.05 | 10.00 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
5 | 6.34 | 9.58 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
6 | 0.59 | 2.20 | ▒▒▒
7 | 4.52 | 8.16 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
8 | 9.38 | 10.00 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
9 | 4.34 | 8.12 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
10 | 0.52 | 1.97 | ▒▒
11 | 5.91 | 9.36 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
12 | 9.21 | 10.00 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
13 | 3.37 | 7.09 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒
14 | 1.09 | 3.91 | ▒▒▒▒▒
15 | 7.41 | 9.92 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
16 | 8.50 | 10.00 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
17 | 2.03 | 5.37 | ▒▒▒▒▒▒▒▒▒
18 | 1.92 | 5.11 | ▒▒▒▒▒▒▒▒
19 | 8.37 | 10.00 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
20 | 7.42 | 9.96 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒Tuning
spike_interval- iterations between spike peaks. Same rule astraffic_period: if the interval is shorter than the iteration width of a single bucket, each bucket averages over multiple full cycles and flattens to ~5.0. See Matching period to your run.- The
* 10.0multiplier at the end sets the peak severity. Change it to scale the spike magnitude.
Bounded Drift (Arctangent Saturation)#
Sensor readings drift from a true value and asymptotically approach a maximum offset. Models calibration drift, battery degradation, or any quantity that grows quickly then saturates.
Expression (upward drift)
100.0 + (2.0 * atan(sqrt(global_iter()) / 100.0) / pi) * 15.0 + noiseExpression (downward drift)
100.0 - (2.0 * atan(sqrt(global_iter()) / 100.0) / pi) * 15.0 + noise2 * atan(x) / pi maps any positive input to the range (0, 1). Multiplying by 15 bounds the drift to 0–15 units. The sqrt inside atan gives fast initial movement that slows as it approaches the asymptote. Flip + to - to model decay (e.g. battery degradation) instead of growth.
Config
- name: insert_sensor
type: exec
args:
- uniform(1, 50)
- 100.0 + (2.0 * atan(sqrt(global_iter()) / 100.0) / pi) * 15.0 + norm_f(0, 0.5, -2, 2, 2)
- 2.0 * atan(sqrt(global_iter()) / 100.0) / pi * 15.0
- global_iter()
query: |-
INSERT INTO sensor_calibration (sensor_id, reading, drift_offset, iteration)
VALUES ($1::INT, $2::FLOAT8, $3::FLOAT8, $4::INT8)Verification query
SELECT
bucket,
round(avg(drift_offset)::NUMERIC, 3) AS avg_drift,
round(avg(reading)::NUMERIC, 2) AS avg_reading,
repeat('▒', (round(avg(drift_offset) / max(avg(drift_offset)) OVER () * 40))::INT) AS histogram
FROM (
SELECT *, ntile(10) OVER (ORDER BY iteration) AS bucket
FROM sensor_calibration
)
GROUP BY bucket
ORDER BY bucket;Upward drift - readings climb from 100 toward ~115:
bucket | avg_drift | avg_reading | histogram
---------+-----------+-------------+-------------------------------------------
1 | 6.068 | 106.06 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
2 | 9.114 | 109.12 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
3 | 10.209 | 110.22 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
4 | 10.853 | 110.86 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
5 | 11.292 | 111.30 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
6 | 11.621 | 111.62 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
7 | 11.866 | 111.87 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
8 | 12.068 | 112.05 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
9 | 12.235 | 112.21 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
10 | 12.376 | 112.37 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒Downward drift - readings decay from 100 toward ~85:
bucket | avg_drift | avg_reading | histogram
---------+-----------+-------------+-------------------------------------------
1 | -12.376 | 87.63 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
2 | -12.235 | 87.79 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
3 | -12.068 | 87.95 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
4 | -11.866 | 88.13 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
5 | -11.621 | 88.38 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
6 | -11.292 | 88.70 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
7 | -10.853 | 89.14 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
8 | -10.209 | 89.78 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
9 | -9.114 | 90.88 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
10 | -6.068 | 93.94 | ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒Tuning
- The
* 15.0multiplier sets the maximum drift (asymptote). Change it to control how far the reading can wander. - The
/ 100.0insideatan(sqrt(iter) / 100.0)controls how quickly saturation is reached. A smaller divisor reaches the asymptote sooner. norm_f(0, 0.5, -2, 2, 2)adds Gaussian noise to individual readings. Increase the stddev (second arg) for noisier data.
Estimating Total Iterations#
The actual number of global_iter() increments depends on workers, duration, query complexity, and hardware. This matters because periodic parameters (traffic_period, spike_interval) must be tuned relative to the actual iteration count - not the configured total_iters global.
Estimate total iterations from three values:
total_iterations = workers * duration_seconds / avg_latency_seconds| Variable | Meaning |
|---|---|
workers | Number of concurrent workers (-w flag) |
duration_seconds | Run duration in seconds (-d flag) |
avg_latency_seconds | Average time for one iteration (query execution + overhead) |
Example: 10 workers, 60s duration, 5ms average latency:
10 * 60 / 0.005 = 120,000 iterationsThis assumes each worker spends all its time executing queries. In practice, connection overhead and GC pauses reduce throughput slightly, so treat the result as an upper bound.
Matching Period to Your Run#
The verification queries use ntile(N) to divide rows into N equal-sized buckets by iteration order. For a periodic pattern to be visible in the histogram, each bucket must cover less than one full cycle. If a bucket spans one or more complete cycles, the periodic component averages out and disappears.
The rule: each bucket’s iteration width should be less than the period.
bucket_width = total_iterations / number_of_bucketsFor the pattern to be clearly visible:
period > bucket_widthFor 2 visible cycles across all buckets (a good default):
period = total_iterations / 2Example: with ~100,000 actual iterations and 20 buckets:
- Each bucket spans ~5,000 iterations
traffic_period: 50000-> 2 full cycles, 10 buckets per cycle - clear wavetraffic_period: 5000-> 20 full cycles, 1 cycle per bucket - wave averages out, histogram looks monotonictraffic_period: 500-> 200 cycles per bucket - completely flat
The same logic applies to spike_interval. If your histogram looks flat when you expect oscillation, increase the period.
Combining Patterns#
All five patterns run concurrently using run_weights to control the mix:
run_weights:
insert_view: 25
insert_price: 20
insert_traffic: 25
insert_error: 15
insert_sensor: 15Adjust weights to emphasise specific patterns or to control relative table sizes. The weights don’t affect the drift expressions themselves - global_iter() increments globally regardless of which statement executes.