Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
a50376f
feat: add iavlx
aaronc Oct 17, 2025
a3f1cf2
feat!: adapt iavlx to store
aaronc Oct 20, 2025
1041204
fixes
aaronc Oct 20, 2025
2cbdf7b
build fixes
aaronc Oct 20, 2025
dc876a4
WIP on system tests
aaronc Oct 20, 2025
c3d9665
WIP on system tests
aaronc Oct 20, 2025
7f154ba
Merge branch 'main' of github.com:cosmos/cosmos-sdk into aaronc/iavlx…
aaronc Oct 20, 2025
7efef6f
Merge branch 'main' of github.com:cosmos/cosmos-sdk into aaronc/iavlx
aaronc Oct 20, 2025
26a09d8
Merge branch 'aaronc/iavlx' into aaronc/iavlx-store
aaronc Oct 20, 2025
29edd54
WIP on system tests
aaronc Oct 21, 2025
aa26a92
i think thats everything? i guess?
technicallyty Oct 21, 2025
80b98c6
fix bug with multi-store isolation
aaronc Oct 21, 2025
fbf7209
attempting to fix cachewrap write behavior
aaronc Oct 21, 2025
13aa9fa
fixing bugs and fixing working hash implementation
aaronc Oct 21, 2025
ca2d85a
fix struct alignment
technicallyty Oct 21, 2025
8b61055
revert unneeded makefile changes
aaronc Oct 21, 2025
47c029a
remove leafOffsetRemappings and add a comment to offsetCache
technicallyty Oct 21, 2025
d4d932e
branch persisted removals and method removals
technicallyty Oct 21, 2025
e85a1aa
fully remove leafOffsetRemapping code
technicallyty Oct 21, 2025
245982d
fix bug
aaronc Oct 21, 2025
8585d2b
fix
technicallyty Oct 21, 2025
eb32c2d
lint fix
aaronc Oct 21, 2025
09e0de9
fix resolve
technicallyty Oct 23, 2025
93aa22c
some comments
technicallyty Oct 23, 2025
98aec1c
add iavlx-options to server
aaronc Oct 23, 2025
179d2f3
fix data race
aaronc Oct 24, 2025
b255d89
perf: benchmarking with sim tests
aaronc Oct 24, 2025
357eb54
add fsync interval option
aaronc Oct 24, 2025
6918a0d
perf: optimize getConfigKey
aaronc Oct 27, 2025
2a4b678
perf: eliminate blocking queues
aaronc Oct 27, 2025
9586547
fix queue
aaronc Oct 27, 2025
20f0574
remove pond
aaronc Oct 27, 2025
4feb860
Merge remote-tracking branch 'origin/main' into aaronc/iavlx
technicallyty Oct 27, 2025
f5d3526
WIP on cachekv
aaronc Oct 27, 2025
e98ec2e
move to store/iavlx
aaronc Oct 27, 2025
5cc83ef
add cachekv for iavlx
aaronc Oct 27, 2025
582bee0
fix import issue
aaronc Oct 27, 2025
67a55a3
move wal write timing
aaronc Oct 28, 2025
b1fe43c
Merge branch 'technicallyty/iavlx/25474-relative-offsets' into aaronc…
aaronc Oct 28, 2025
1cf5e6c
feat: add tracing
aaronc Oct 29, 2025
a207f94
work on otel tracer impl
aaronc Oct 29, 2025
ee0bfb3
add basic baseapp tracing
aaronc Oct 29, 2025
d5f5ea4
latest WIP
aaronc Oct 30, 2025
47f83e8
add trace exporter setup
aaronc Oct 30, 2025
7fafce3
fixes
aaronc Oct 30, 2025
7bd8e21
feat(iavl): blockSTM updates to iavlx store impl (#25504)
technicallyty Oct 30, 2025
bdba035
simapp setup, make tracers wrap loggers
aaronc Oct 30, 2025
25e3135
add test setup
aaronc Oct 30, 2025
5c7e464
fix shutdown order
aaronc Oct 30, 2025
d71f7c1
block trace nesting
aaronc Oct 30, 2025
56b215a
update metrics config and instrumentation
aaronc Oct 30, 2025
2b0b4b1
Merge branch 'aaronc/tracing-metrics' into aaronc/iavlx-sim-bench
aaronc Oct 30, 2025
98b1d38
tidy
aaronc Oct 31, 2025
57a64e5
Merge branch 'aaronc/iavlx' into aaronc/iavlx-sim-bench
aaronc Oct 31, 2025
80b7dc1
fixes
aaronc Oct 31, 2025
f9ce55c
start adding otel metric config
aaronc Oct 31, 2025
3fff00f
migrate to pure otel setup
aaronc Oct 31, 2025
5077567
fixes
aaronc Oct 31, 2025
31536b6
add basic metrics
aaronc Oct 31, 2025
c922688
add telemetry shutdown hook
aaronc Oct 31, 2025
ed891cc
docs, cleanup
aaronc Oct 31, 2025
f685bd4
WIP on removing go-metrics
aaronc Oct 31, 2025
42da2f7
Merge branch 'main' of github.com:cosmos/cosmos-sdk into aaronc/traci…
aaronc Oct 31, 2025
962533a
Merge branch 'aaronc/tracing-metrics' into aaronc/iavlx-sim-bench
aaronc Oct 31, 2025
699f5d3
setup sim test flag
aaronc Oct 31, 2025
5df2460
integrate slog logging
aaronc Oct 31, 2025
1c84edb
update to use official env var
aaronc Oct 31, 2025
46e4bcb
add README.md
aaronc Nov 3, 2025
f0c3955
delete spaces
aaronc Nov 3, 2025
133f682
Merge branch 'aaronc/tracing-metrics' into aaronc/iavlx-sim-bench
technicallyty Nov 3, 2025
7dfb754
setup TestingMain
aaronc Nov 3, 2025
1ce344b
update suggested config in README.md
aaronc Nov 3, 2025
edbae92
add otel custom config options
aaronc Nov 3, 2025
0f8085a
add otel custom config options
aaronc Nov 3, 2025
03b6069
add more instrumentation
aaronc Nov 3, 2025
c4dbd07
remove pretty print
aaronc Nov 4, 2025
7ecd5c1
Merge branch 'aaronc/tracing-metrics' into aaronc/iavlx-sim-bench
aaronc Nov 4, 2025
4a94851
go mod tidy
aaronc Nov 4, 2025
3dde2cc
improve instrumentation
aaronc Nov 4, 2025
0041a8d
fix instrumentation nesting
aaronc Nov 4, 2025
d64d65c
named msg handler spans
aaronc Nov 4, 2025
8261955
add basic python timing analysis
aaronc Nov 4, 2025
cc2ddb4
remove all mmap writing and default to no fsync (to match iavl/v1 beh…
aaronc Nov 5, 2025
e88a416
fix bug when fsync enabled
aaronc Nov 5, 2025
fa918d8
optimize LeafPersistent.Value to reduce copying
aaronc Nov 5, 2025
aece48b
optimize []byte -> string lookup in map
aaronc Nov 5, 2025
3610389
import fix
aaronc Nov 5, 2025
521cea7
use larger bufio.Writer buffer by default
aaronc Nov 5, 2025
cc7dac8
use sync.Pool for sha256 hashers
aaronc Nov 6, 2025
a51b055
update analysis
aaronc Nov 6, 2025
92e9d80
feat(iavlx): reload database
aaronc Nov 15, 2025
473ed33
WIP on reopening
aaronc Nov 17, 2025
9cd5fbe
refactor version tracking
aaronc Nov 17, 2025
9078e38
fix reloading bugs
aaronc Nov 17, 2025
27a0b64
renames, make sure to call cancel
technicallyty Nov 17, 2025
81a5c7b
Merge remote-tracking branch 'origin/main' into aaronc/iavlx-sim-bench
technicallyty Nov 18, 2025
cb78dfc
Merge remote-tracking branch 'origin/main' into aaronc/iavlx-sim-bench
technicallyty Nov 19, 2025
6f89fda
bug fixes related to orphan tracking, graphviz updates from presentation
aaronc Nov 24, 2025
971f232
bug fixes related to symlinks from visualize branch
aaronc Nov 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,5 @@ debug_container.log
*.synctex.gz
/x/genutil/config/priv_validator_key.json
/x/genutil/data/priv_validator_state.json
/.envrc
/.env
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.13
8 changes: 6 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -492,13 +492,17 @@ localnet-debug: localnet-stop localnet-build-dlv localnet-build-nodes

.PHONY: localnet-start localnet-stop localnet-debug localnet-build-env localnet-build-dlv localnet-build-nodes

test-system: build-v53 build
build-system-test-current: build
mkdir -p ./tests/systemtests/binaries/
cp $(BUILDDIR)/simd ./tests/systemtests/binaries/

test-system: build-v53 build-system-test-current
mkdir -p ./tests/systemtests/binaries/
cp $(BUILDDIR)/simd ./tests/systemtests/binaries/
mkdir -p ./tests/systemtests/binaries/v0.53
mv $(BUILDDIR)/simdv53 ./tests/systemtests/binaries/v0.53/simd
$(MAKE) -C tests/systemtests test
.PHONY: test-system
.PHONY: test-system build-system-test-current

# build-v53 checks out the v0.53.x branch, builds the binary, and renames it to simdv53.
build-v53:
Expand Down
Empty file added analysis/__init__.py
Empty file.
1,934 changes: 1,934 additions & 0 deletions analysis/analysis.ipynb

Large diffs are not rendered by default.

124 changes: 124 additions & 0 deletions analysis/analysis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
"""Analysis functions for OpenTelemetry trace and metrics data."""

import duckdb
from datetime import datetime
from pydantic import BaseModel
import pandas as pd
import plotly.graph_objects as go
from typing import Dict

# Type alias for runs dictionary (run_name -> DuckDB connection)
Runs = Dict[str, duckdb.DuckDBPyConnection]


class BlockSummary(BaseModel):
"""Summary of block execution."""
start_time: datetime
end_time: datetime
total_duration_seconds: float
block_count: int


def block_summary(con: duckdb.DuckDBPyConnection) -> BlockSummary:
"""
Generate a high-level summary of block execution.

Args:
con: DuckDB connection with spans view loaded

Returns:
BlockSummary object with:
- start_time: Start time of first block
- end_time: End time of last block
- total_duration_seconds: Total duration from first block start to last block end
- block_count: Total number of blocks processed

Example:
>>> from analysis.read_otel import load_otel_data
>>> con = load_otel_data('/path/to/data')
>>> summary = block_summary(con)
>>> print(f"Processed {summary.block_count} blocks in {summary.total_duration_seconds:.2f}s")
"""
result = con.sql("""
SELECT
MIN(start_time) AS start_time,
MAX(end_time) AS end_time,
EXTRACT(EPOCH FROM (MAX(end_time) - MIN(start_time))) AS total_duration_seconds,
COUNT(*) AS block_count
FROM spans
WHERE span_name = 'Block' AND scope = 'cosmos-sdk/baseapp'
""").fetchone()

return BlockSummary(
start_time=result[0],
end_time=result[1],
total_duration_seconds=result[2],
block_count=result[3],
)


def block_durations(con: duckdb.DuckDBPyConnection, span_name: str = 'Block') -> pd.DataFrame:
"""
Get duration for each block.

Args:
con: DuckDB connection with spans view loaded

Returns:
DataFrame with columns:
- block_number: Sequential block number (1-indexed)
- duration_ms: Block duration in milliseconds

Example:
>>> from analysis.read_otel import load_otel_data
>>> con = load_otel_data('/path/to/data')
>>> df = block_durations(con)
>>> df.head()
"""
return con.sql("""
SELECT
ROW_NUMBER() OVER (ORDER BY start_time) AS block_number,
EXTRACT(EPOCH FROM duration) * 1000 AS duration_ms
FROM spans
WHERE span_name = ? AND scope = 'cosmos-sdk/baseapp'
ORDER BY start_time
""", params=[span_name]).df()


def plot_block_durations(runs: Runs, span_name: str = 'Block') -> go.Figure:
"""
Create a plotly line chart comparing block durations across runs.

Args:
runs: Dictionary mapping run name to DuckDB connection

Returns:
Plotly Figure object with block duration traces for each run

Example:
>>> from analysis.read_otel import load_otel_runs
>>> runs = load_otel_runs('/path/to/data')
>>> fig = plot_block_durations(runs)
>>> fig.show()
"""
fig = go.Figure()

for run_name, con in runs.items():
df = block_durations(con, span_name)
fig.add_trace(go.Scatter(
x=df['block_number'],
y=df['duration_ms'],
mode='lines',
name=run_name,
line=dict(width=2)
))

fig.update_layout(
title=f'{span_name} Duration Comparison',
xaxis_title='Block Number',
yaxis_title='Duration (ms)',
hovermode='x unified',
template='plotly_white'
)

return fig
128 changes: 128 additions & 0 deletions analysis/read_otel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
"""Load OpenTelemetry JSONL data into DuckDB views."""

import duckdb
from pathlib import Path
from typing import Dict


def load_otel_data(data_path: str | Path) -> duckdb.DuckDBPyConnection:
"""
Load OpenTelemetry data from a directory containing trace.jsonl, logs.jsonl, and metrics.jsonl.

Creates three views in the returned DuckDB connection:
- spans: flattened trace data
- logs: flattened log data
- metrics: flattened metrics data

Args:
data_path: Path to directory containing the JSONL files

Returns:
DuckDB connection with views created

Example:
>>> con = load_otel_data('/path/to/data')
>>> con.sql("SELECT span_name, count(*) FROM spans GROUP BY span_name").show()
"""
data_path = Path(data_path)
con = duckdb.connect(':memory:')

# Create spans view from trace.jsonl
trace_file = data_path / 'trace.jsonl'
if trace_file.exists():
con.execute(f"""
CREATE VIEW spans AS
SELECT
Name AS span_name,
SpanContext.TraceID AS trace_id,
SpanContext.SpanID AS span_id,
Parent.SpanID AS parent_span_id,
CAST(StartTime AS TIMESTAMPTZ) AS start_time,
CAST(EndTime AS TIMESTAMPTZ) AS end_time,
CAST(EndTime AS TIMESTAMPTZ) - CAST(StartTime AS TIMESTAMPTZ) AS duration,
InstrumentationScope.Name AS scope,
ChildSpanCount AS child_span_count,
Attributes,
Resource,
Status
FROM read_ndjson_auto('{trace_file}')
""")

# Create logs view from logs.jsonl
logs_file = data_path / 'logs.jsonl'
if logs_file.exists():
con.execute(f"""
CREATE VIEW logs AS
SELECT
CAST(Timestamp AS TIMESTAMPTZ) AS timestamp,
CAST(ObservedTimestamp AS TIMESTAMPTZ) AS observed_timestamp,
Severity AS severity,
SeverityText AS severity_text,
Body.Value AS body,
TraceID AS trace_id,
SpanID AS span_id,
Attributes,
Resource,
Scope
FROM read_ndjson_auto('{logs_file}')
""")

# Create metrics view from metrics.jsonl (if file exists and has data)
metrics_file = data_path / 'metrics.jsonl'
if metrics_file.exists() and metrics_file.stat().st_size > 0:
# Flatten the deeply nested metrics structure
# Structure: Resource → ScopeMetrics[] → Metrics[] → Data → DataPoints[]
con.execute(f"""
CREATE VIEW metrics AS
SELECT
scope_metric.Scope.Name AS scope_name,
scope_metric.Scope.Version AS scope_version,
metric.Name AS metric_name,
metric.Description AS metric_description,
metric.Unit AS unit,
metric.Data.Temporality AS temporality,
metric.Data.IsMonotonic AS is_monotonic,
CAST(dp.Time AS TIMESTAMPTZ) AS time,
CAST(dp.StartTime AS TIMESTAMPTZ) AS start_time,
dp.Value AS value,
dp.Count AS count,
dp.Sum AS sum,
dp.Min AS min,
dp.Max AS max,
dp.Attributes AS attributes,
raw.Resource AS resource
FROM read_ndjson_auto('{metrics_file}') AS raw
CROSS JOIN UNNEST(raw.ScopeMetrics) AS t(scope_metric)
CROSS JOIN UNNEST(scope_metric.Metrics) AS t2(metric)
CROSS JOIN UNNEST(metric.Data.DataPoints) AS t3(dp)
""")

return con


def load_otel_runs(runs_dir: str | Path) -> Dict[str, duckdb.DuckDBPyConnection]:
"""
Load OpenTelemetry data from multiple run directories.

Args:
runs_dir: Path to directory containing subdirectories with OTEL data
Each subdirectory should contain trace.jsonl, logs.jsonl, metrics.jsonl

Returns:
Dictionary mapping run name (subdirectory name) to DuckDB connection

Example:
>>> connections = load_otel_runs('/Users/arc/iavl-bench-data/sims')
>>> # Returns: {'iavlx': <connection>, 'iavl1': <connection>}
>>> connections['iavlx'].sql("SELECT count(*) FROM spans").show()
"""
runs_dir = Path(runs_dir)
connections = {}

for subdir in runs_dir.iterdir():
if subdir.is_dir():
# Check if it has trace.jsonl to confirm it's a valid run directory
if (subdir / 'trace.jsonl').exists():
connections[subdir.name] = load_otel_data(subdir)

return connections
Loading