Skip to content

statedb: Minor WriteTxn optimizations - #205

Open
joamaki wants to merge 6 commits into
mainfrom
pr/joamaki/minor-writetxn-optimizations
Open

joamaki wants to merge 6 commits into
mainfrom
pr/joamaki/minor-writetxn-optimizations

Conversation

@joamaki

@joamaki joamaki commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor
  • statedb.New now defaults to NopMetrics instead of ExpVarMetrics
  • Avoid expvar.Int allocation in ExpVarMetrics
  • Use the slice in the txn handle as the new root to avoid allocating a new one (WriteTxn_1 allocs 13 -> 12)
  • As we're starting to have tens of tables in Cilium keep track of locked tables instead of iterating over all tables when committing (CommitOnly_100 695ns/op -> 542ns/op (-22%))
  • Reduce the number of calls to time.Now in write transactions (WriteTxn_1 742ns/op -> 703ns/op (-5%))
  • Speed up "tableIndex" lookup by avoiding going via TableMeta interface

Benchstat highlights:

┌─────────────────────────────────────────┬────────┬────────┬───────────────┬─────────┐
│                Benchmark                │  main  │  head  │    Δ time     │ allocs  │
├─────────────────────────────────────────┼────────┼────────┼───────────────┼─────────┤
│ DB_WriteTxn_CommitOnly_100Tables        │ 749.8n │ 507.0n │ −32.4%        │ 4 → 3   │
├─────────────────────────────────────────┼────────┼────────┼───────────────┼─────────┤
│ DB_WriteTxnCommit100                    │ 738.7n │ 502.3n │ −32.0%        │ 4 → 3   │
├─────────────────────────────────────────┼────────┼────────┼───────────────┼─────────┤
│ DB_WriteTxn_CommitOnly_1Table           │ 425.8n │ 359.3n │ −15.6%        │ 4 → 3   │
├─────────────────────────────────────────┼────────┼────────┼───────────────┼─────────┤
│ DB_PropagationDelay                     │ 863.6n │ 755.5n │ −12.5%        │ 15 → 13 │
├─────────────────────────────────────────┼────────┼────────┼───────────────┼─────────┤
│ DB_NewWriteTxn                          │ 374.4n │ 330.2n │ −11.8%        │ 3 → 3   │
├─────────────────────────────────────────┼────────┼────────┼───────────────┼─────────┤
│ DB_WriteTxn_1                           │ 760.5n │ 689.8n │ −9.3%         │ 13 → 12 │
├─────────────────────────────────────────┼────────┼────────┼───────────────┼─────────┤
│ DB_WriteTxn_1_LPMIndex                  │ 8.293µ │ 7.685µ │ −7.3%         │ 80 → 68 │
├─────────────────────────────────────────┼────────┼────────┼───────────────┼─────────┤
│ DB_LPMIndex_Get                         │ 2.500m │ 2.379m │ −4.8%         │ 0 → 0   │
├─────────────────────────────────────────┼────────┼────────┼───────────────┼─────────┤
│ DB_WriteTxn_10                          │ 332.4n │ 319.8n │ −3.8%         │ 6 → 5   │
├─────────────────────────────────────────┼────────┼────────┼───────────────┼─────────┤
│ DB_RandomLookup                         │ 30.70µ │ 29.77µ │ −3.0%         │ 0 → 0   │
├─────────────────────────────────────────┼────────┼────────┼───────────────┼─────────┤
│ DB_Changes                              │ 705.9µ │ 685.2µ │ −2.9%         │ ≈       │
├─────────────────────────────────────────┼────────┼────────┼───────────────┼─────────┤
│ DB_WriteTxn_1000                        │ 309.1n │ 302.0n │ −2.3%         │ =       │
├─────────────────────────────────────────┼────────┼────────┼───────────────┼─────────┤
│ DB_RandomReplace                        │ 600.8µ │ 592.4µ │ −1.4%         │ ≈       │
├─────────────────────────────────────────┼────────┼────────┼───────────────┼─────────┤
│ DB_WriteTxn_100_SecondaryIndex          │ 459.8n │ 453.6n │ −1.3%         │ =       │
└─────────────────────────────────────────┴────────┴────────┴───────────────┴─────────┘

AIL:3

@joamaki
joamaki requested a review from bimmlerd September 25, 2026 10:19
@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
$ make
go build ./...
go: downloading go.yaml.in/yaml/v3 v3.0.4
go: downloading github.com/cilium/hive v1.0.4
go: downloading golang.org/x/time v0.15.0
go: downloading github.com/spf13/cobra v1.10.2
go: downloading github.com/spf13/pflag v1.0.10
go: downloading github.com/cilium/stream v0.0.1
go: downloading github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de
go: downloading github.com/spf13/viper v1.18.2
go: downloading go.uber.org/dig v1.17.1
go: downloading golang.org/x/term v0.16.0
go: downloading github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
go: downloading github.com/mitchellh/mapstructure v1.5.0
go: downloading golang.org/x/sys v0.17.0
go: downloading golang.org/x/tools v0.17.0
go: downloading github.com/spf13/cast v1.6.0
go: downloading github.com/fsnotify/fsnotify v1.7.0
go: downloading github.com/sagikazarmark/slog-shim v0.1.0
go: downloading github.com/spf13/afero v1.11.0
go: downloading github.com/subosito/gotenv v1.6.0
go: downloading github.com/hashicorp/hcl v1.0.0
go: downloading gopkg.in/ini.v1 v1.67.0
go: downloading github.com/magiconair/properties v1.8.7
go: downloading github.com/pelletier/go-toml/v2 v2.1.0
go: downloading gopkg.in/yaml.v3 v3.0.1
go: downloading golang.org/x/text v0.14.0
STATEDB_VALIDATE=1 go test ./... -cover -vet=all -test.count 1
go: downloading github.com/stretchr/testify v1.11.1
go: downloading go.uber.org/goleak v1.3.0
go: downloading golang.org/x/exp v0.0.0-20240119083558-1b970713d09a
go: downloading github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2
ok  	github.com/cilium/statedb	322.257s	coverage: 79.6% of statements
ok  	github.com/cilium/statedb/index	0.006s	coverage: 48.6% of statements
ok  	github.com/cilium/statedb/internal	0.029s	coverage: 54.1% of statements
ok  	github.com/cilium/statedb/lpm	4.124s	coverage: 76.1% of statements
ok  	github.com/cilium/statedb/part	99.633s	coverage: 86.6% of statements
ok  	github.com/cilium/statedb/reconciler	0.296s	coverage: 93.3% of statements
	github.com/cilium/statedb/reconciler/benchmark		coverage: 0.0% of statements
	github.com/cilium/statedb/reconciler/example		coverage: 0.0% of statements
go test -race ./... -test.count 1
ok  	github.com/cilium/statedb	41.450s
ok  	github.com/cilium/statedb/index	1.015s
ok  	github.com/cilium/statedb/internal	1.034s
ok  	github.com/cilium/statedb/lpm	2.680s
ok  	github.com/cilium/statedb/part	34.282s
ok  	github.com/cilium/statedb/reconciler	1.345s
?   	github.com/cilium/statedb/reconciler/benchmark	[no test files]
?   	github.com/cilium/statedb/reconciler/example	[no test files]
go test ./... -bench . -benchmem -test.run xxx
goos: linux
goarch: amd64
pkg: github.com/cilium/statedb
cpu: INTEL(R) XEON(R) PLATINUM 8573C
BenchmarkDB_WriteTxn_1-4                      	  976614	      1080 ns/op	    926178 objects/sec	     632 B/op	      12 allocs/op
BenchmarkDB_WriteTxn_10-4                     	 2483883	       484.2 ns/op	   2065399 objects/sec	     349 B/op	       5 allocs/op
BenchmarkDB_WriteTxn_100-4                    	 2922711	       438.1 ns/op	   2282329 objects/sec	     317 B/op	       5 allocs/op
BenchmarkDB_WriteTxn_1000-4                   	 2677845	       451.0 ns/op	   2217416 objects/sec	     318 B/op	       5 allocs/op
BenchmarkDB_WriteTxn_100_SecondaryIndex-4     	 1784185	       690.7 ns/op	   1447718 objects/sec	     419 B/op	       7 allocs/op
BenchmarkDB_WriteTxn_CommitOnly_100Tables-4   	 1599876	       747.3 ns/op	    1088 B/op	       3 allocs/op
BenchmarkDB_WriteTxn_CommitOnly_1Table-4      	 2167628	       547.1 ns/op	     200 B/op	       3 allocs/op
BenchmarkDB_NewWriteTxn-4                     	 2389606	       502.4 ns/op	     200 B/op	       3 allocs/op
BenchmarkDB_WriteTxnCommit100-4               	 1602181	       747.6 ns/op	    1072 B/op	       3 allocs/op
BenchmarkDB_NewReadTxn-4                      	834369504	         1.403 ns/op	       0 B/op	       0 allocs/op
BenchmarkDB_Modify-4                          	    2274	    507361 ns/op	   1970985 objects/sec	  342487 B/op	    6072 allocs/op
BenchmarkDB_GetInsert-4                       	    2149	    555647 ns/op	   1799702 objects/sec	  326480 B/op	    6072 allocs/op
BenchmarkDB_RandomInsert-4                    	    2509	    477152 ns/op	   2095770 objects/sec	  318476 B/op	    5072 allocs/op
BenchmarkDB_RandomReplace-4                   	    1336	    915174 ns/op	   1092688 objects/sec	  432297 B/op	    8088 allocs/op
BenchmarkDB_SequentialInsert-4                	    2708	    455298 ns/op	   2196364 objects/sec	  318476 B/op	    5072 allocs/op
BenchmarkDB_SequentialInsert_Prefix-4         	     573	   2093968 ns/op	    477562 objects/sec	 2794461 B/op	   41797 allocs/op
BenchmarkDB_Changes_Baseline-4                	    2168	    535792 ns/op	   1866396 objects/sec	  388223 B/op	    6162 allocs/op
BenchmarkDB_Changes-4                         	    1162	   1014168 ns/op	    986030 objects/sec	  604075 B/op	    9329 allocs/op
BenchmarkDB_RandomLookup-4                    	   31168	     38460 ns/op	  26001302 objects/sec	       0 B/op	       0 allocs/op
BenchmarkDB_SequentialLookup-4                	   43137	     27882 ns/op	  35865647 objects/sec	       0 B/op	       0 allocs/op
BenchmarkDB_Prefix_SecondaryIndex-4           	    8300	    126190 ns/op	   7924567 objects/sec	  124952 B/op	    1026 allocs/op
BenchmarkDB_FullIteration_All-4               	    1348	    861610 ns/op	 116061746 objects/sec	     104 B/op	       4 allocs/op
BenchmarkDB_FullIteration_Prefix-4            	    1320	    890139 ns/op	 112342059 objects/sec	     136 B/op	       5 allocs/op
BenchmarkDB_FullIteration_Get-4               	     378	   3158649 ns/op	  31659109 objects/sec	       0 B/op	       0 allocs/op
BenchmarkDB_FullIteration_Get_Secondary-4     	     198	   5981041 ns/op	  16719498 objects/sec	       0 B/op	       0 allocs/op
BenchmarkDB_FullIteration_ReadTxnGet-4        	     368	   3229527 ns/op	  30964290 objects/sec	       0 B/op	       0 allocs/op
BenchmarkDB_PropagationDelay-4                	 1039843	      1145 ns/op	         9.000 50th_µs	        12.00 90th_µs	        33.00 99th_µs	     754 B/op	      13 allocs/op
BenchmarkDB_WriteTxn_1_DefaultMetrics-4       	 1000000	      1127 ns/op	     632 B/op	      12 allocs/op
BenchmarkDB_WriteTxn_1_ExpVarMetrics-4        	  770346	      1466 ns/op	     656 B/op	      15 allocs/op
BenchmarkDB_WriteTxn_100_LPMIndex-4           	  539209	      2018 ns/op	    495558 objects/sec	    1571 B/op	      35 allocs/op
BenchmarkDB_WriteTxn_1_LPMIndex-4             	  160638	     12774 ns/op	     78287 objects/sec	   13792 B/op	      65 allocs/op
BenchmarkDB_LPMIndex_Get-4                    	     379	   3126946 ns/op	   3198009 objects/sec	       0 B/op	       0 allocs/op
BenchmarkWatchSet_4-4                         	 2511273	       471.1 ns/op	     296 B/op	       4 allocs/op
BenchmarkWatchSet_16-4                        	  658549	      1613 ns/op	    1096 B/op	       5 allocs/op
BenchmarkWatchSet_128-4                       	   82316	     14671 ns/op	    8904 B/op	       5 allocs/op
BenchmarkWatchSet_1024-4                      	    8046	    147614 ns/op	   73744 B/op	       5 allocs/op
PASS
ok  	github.com/cilium/statedb	47.093s
PASS
ok  	github.com/cilium/statedb/index	0.004s
goos: linux
goarch: amd64
pkg: github.com/cilium/statedb/internal
cpu: INTEL(R) XEON(R) PLATINUM 8573C
Benchmark_SortableMutex-4   	 6908955	       174.3 ns/op	       0 B/op	       0 allocs/op
PASS
ok  	github.com/cilium/statedb/internal	1.209s
goos: linux
goarch: amd64
pkg: github.com/cilium/statedb/lpm
cpu: INTEL(R) XEON(R) PLATINUM 8573C
Benchmark_txn_insert/batchSize=1-4         	    2275	    523832 ns/op	   1909010 objects/sec	  822419 B/op	   13975 allocs/op
Benchmark_txn_insert/batchSize=10-4        	    3811	    313656 ns/op	   3188210 objects/sec	  369199 B/op	    6668 allocs/op
Benchmark_txn_insert/batchSize=100-4       	    4156	    292846 ns/op	   3414762 objects/sec	  329617 B/op	    6027 allocs/op
Benchmark_txn_delete/batchSize=1-4         	    1622	    724859 ns/op	   1379579 objects/sec	 1270476 B/op	   13976 allocs/op
Benchmark_txn_delete/batchSize=10-4        	    3290	    365693 ns/op	   2734533 objects/sec	  356419 B/op	    5769 allocs/op
Benchmark_txn_delete/batchSize=100-4       	    3796	    315747 ns/op	   3167096 objects/sec	  270754 B/op	    5038 allocs/op
Benchmark_LPM_Lookup-4                     	    9471	    123961 ns/op	   8067085 objects/sec	       0 B/op	       0 allocs/op
Benchmark_LPM_All-4                        	  138512	      8725 ns/op	 114617165 objects/sec	      32 B/op	       1 allocs/op
Benchmark_LPM_Prefix-4                     	  141208	      8465 ns/op	 118137166 objects/sec	      32 B/op	       1 allocs/op
Benchmark_LPM_LowerBound-4                 	  265959	      4534 ns/op	 110278673 objects/sec	     288 B/op	       2 allocs/op
PASS
ok  	github.com/cilium/statedb/lpm	11.978s
goos: linux
goarch: amd64
pkg: github.com/cilium/statedb/part
cpu: INTEL(R) XEON(R) PLATINUM 8573C
Benchmark_Set_Singleton_Create-4              	30203962	        38.66 ns/op	      24 B/op	       1 allocs/op
Benchmark_Set_Singleton_Has-4                 	100000000	        10.46 ns/op	       0 B/op	       0 allocs/op
Benchmark_StringMap_Txn_Insert-4              	    8991	    126323 ns/op	   7916235 items/sec	   98250 B/op	    1306 allocs/op
Benchmark_Uint64Map_Random-4                  	    1971	    658051 ns/op	   1519639 items/sec	 1322070 B/op	    6033 allocs/op
Benchmark_Uint64Map_Sequential-4              	    1954	    617074 ns/op	   1620550 items/sec	 1703089 B/op	    5753 allocs/op
Benchmark_Uint64Map_Sequential_Insert-4       	    2121	    566664 ns/op	   1764714 items/sec	 1695086 B/op	    4752 allocs/op
Benchmark_Uint64Map_Sequential_Txn_Insert-4   	   10000	    107001 ns/op	   9345670 items/sec	   90464 B/op	    2031 allocs/op
Benchmark_Uint64Map_Random_Insert-4           	    2164	    559756 ns/op	   1786493 items/sec	 1313755 B/op	    5036 allocs/op
Benchmark_Uint64Map_Random_Txn_Insert-4       	    6808	    172078 ns/op	   5811326 items/sec	  116561 B/op	    2399 allocs/op
Benchmark_Insert_RootOnlyWatch-4              	   10000	    104979 ns/op	   9525705 objects/sec	   75552 B/op	    2036 allocs/op
Benchmark_Insert-4                            	   10000	    103598 ns/op	   9652674 objects/sec	   76096 B/op	    2047 allocs/op
Benchmark_WatchReplace-4                      	   12584	     95455 ns/op	  10476112 objects/sec	   57305 B/op	    1006 allocs/op
Benchmark_Modify-4                            	   13417	     89559 ns/op	  11165864 objects/sec	   58056 B/op	    1007 allocs/op
Benchmark_GetInsert-4                         	   10000	    110800 ns/op	   9025272 objects/sec	   58056 B/op	    1007 allocs/op
Benchmark_Replace-4                           	32069787	        36.81 ns/op	  27164467 objects/sec	       0 B/op	       0 allocs/op
Benchmark_Replace_RootOnlyWatch-4             	32986080	        37.65 ns/op	  26560093 objects/sec	       0 B/op	       0 allocs/op
Benchmark_txn_1-4                             	 8927662	       132.2 ns/op	   7564217 objects/sec	      64 B/op	       3 allocs/op
Benchmark_txn_10-4                            	11233168	       105.3 ns/op	   9497598 objects/sec	      76 B/op	       2 allocs/op
Benchmark_txn_100-4                           	12805341	        95.82 ns/op	  10435860 objects/sec	      67 B/op	       2 allocs/op
Benchmark_txn_1000-4                          	11036932	       108.8 ns/op	   9190331 objects/sec	      65 B/op	       2 allocs/op
Benchmark_txn_delete_1-4                      	 5167924	       235.1 ns/op	   4252736 objects/sec	     632 B/op	       3 allocs/op
Benchmark_txn_delete_10-4                     	11920584	        99.13 ns/op	  10087692 objects/sec	     103 B/op	       1 allocs/op
Benchmark_txn_delete_100-4                    	15713445	        75.25 ns/op	  13288936 objects/sec	      35 B/op	       1 allocs/op
Benchmark_txn_delete_1000-4                   	16356343	        72.63 ns/op	  13769030 objects/sec	      28 B/op	       1 allocs/op
Benchmark_Get-4                               	   67993	     18195 ns/op	  54960558 objects/sec	       0 B/op	       0 allocs/op
Benchmark_GetWatch-4                          	   55663	     21815 ns/op	  45840168 objects/sec	       0 B/op	       0 allocs/op
Benchmark_All-4                               	  194055	      6133 ns/op	 163055442 objects/sec	       0 B/op	       0 allocs/op
Benchmark_Iterator_All-4                      	  189721	      6310 ns/op	 158470694 objects/sec	       0 B/op	       0 allocs/op
Benchmark_Iterator_Next-4                     	  193593	      6115 ns/op	 163525166 objects/sec	     896 B/op	       1 allocs/op
Benchmark_Hashmap_Insert-4                    	   21267	     57010 ns/op	  17540701 objects/sec	   74264 B/op	      20 allocs/op
Benchmark_Hashmap_Get_Uint64-4                	  200360	      6013 ns/op	 166297480 objects/sec	       0 B/op	       0 allocs/op
Benchmark_Hashmap_Get_Bytes-4                 	  169119	      7089 ns/op	 141055939 objects/sec	       0 B/op	       0 allocs/op
Benchmark_Delete_Random-4                     	      75	  15471534 ns/op	   6463483 objects/sec	 2539393 B/op	  102756 allocs/op
Benchmark_find16-4                            	238989865	         4.946 ns/op	       0 B/op	       0 allocs/op
Benchmark_findIndex16-4                       	87272276	        13.36 ns/op	       0 B/op	       0 allocs/op
Benchmark_find64-4                            	339700149	         3.532 ns/op	       0 B/op	       0 allocs/op
Benchmark_findIndex64_hit-4                   	339119684	         3.579 ns/op	       0 B/op	       0 allocs/op
Benchmark_findIndex64_miss-4                  	327884152	         3.695 ns/op	       0 B/op	       0 allocs/op
Benchmark_find4-4                             	536139598	         2.250 ns/op	       0 B/op	       0 allocs/op
Benchmark_findIndex4-4                        	320759037	         3.641 ns/op	       0 B/op	       0 allocs/op
BenchmarkSmallWriteTxn/updates_1-4            	  406306	      2661 ns/op	    3529 B/op	       4 allocs/op
BenchmarkSmallWriteTxn/updates_2-4            	  301194	      3729 ns/op	    4742 B/op	       6 allocs/op
BenchmarkSmallWriteTxn/updates_4-4            	  197018	      5858 ns/op	    7155 B/op	      11 allocs/op
BenchmarkSmallWriteTxn/updates_8-4            	  118827	     10111 ns/op	   11927 B/op	      20 allocs/op
BenchmarkSmallWriteTxn/updates_16-4           	   62299	     18746 ns/op	   21290 B/op	      38 allocs/op
BenchmarkAtomicWatchPointerFirstChannel-4     	21329996	        57.60 ns/op	     120 B/op	       2 allocs/op
BenchmarkAtomicWatchPointerChannel-4          	469570008	         2.532 ns/op	       0 B/op	       0 allocs/op
PASS
ok  	github.com/cilium/statedb/part	57.076s
PASS
ok  	github.com/cilium/statedb/reconciler	0.005s
?   	github.com/cilium/statedb/reconciler/benchmark	[no test files]
?   	github.com/cilium/statedb/reconciler/example	[no test files]
go run ./reconciler/benchmark -quiet
1000000 objects reconciled in 1.51 seconds (batch size 1000)
Throughput 660196.45 objects per second
506MB total allocated, 5010194 in-use objects, 231MB bytes in use

When no metrics implementation was given New() used an unpublished
ExpVarMetrics. As it's not published and not otherwise accessible,
nothing could observe these metrics, yet every write transaction paid
for building the metric keys and allocating the expvar values. This
also applied when using the hive Cell without providing Metrics.

Default to NopMetrics instead.

                             │   old    │                new                 │
                             │  sec/op  │   sec/op     vs base               │
DB_WriteTxn_1_DefaultMetrics 1149.5n ± 4%  761.2n ± 6%  -33.78% (p=0.002 n=6)
                             │  B/op    │    B/op     vs base                │
DB_WriteTxn_1_DefaultMetrics   896.0 ± 0%  656.0 ± 0%  -26.79% (p=0.002 n=6)
                             │ allocs/op │ allocs/op   vs base               │
DB_WriteTxn_1_DefaultMetrics   25.00 ± 0%  13.00 ± 0%  -48.00% (p=0.002 n=6)

AIL:3
Signed-off-by: Jussi Maki <jussi@isovalent.com>
The gauge metrics of ExpVarMetrics allocated a new expvar.Int and
replaced the map entry on every update. The object count, graveyard
object count and revision gauges are updated for each modified table on
every commit. Update the existing value in place if it exists.

                              │     old      │                new                 │
                              │    sec/op    │   sec/op     vs base               │
DB_WriteTxn_1_ExpVarMetrics     1153.0n ± 1%   960.3n ± 0%  -16.71% (p=0.002 n=6)

                              │    old     │                new                │
                              │    B/op    │    B/op     vs base               │
DB_WriteTxn_1_ExpVarMetrics     896.0 ± 0%   680.0 ± 0%  -24.11% (p=0.002 n=6)

                              │    old     │                new                │
                              │ allocs/op  │ allocs/op   vs base               │
DB_WriteTxn_1_ExpVarMetrics     25.00 ± 0%   16.00 ± 0%  -36.00% (p=0.002 n=6)

AIL:3
Signed-off-by: Jussi Maki <jussi@isovalent.com>
Commit() stored a pointer to a local slice header as the new root,
which forced a heap allocation on every commit. The write transaction
handle already holds a readTxn for the ReadTxn returned from Commit(),
so store the root there and publish a pointer to it instead.

                                │     old     │                new                │
                                │   sec/op    │   sec/op     vs base              │
DB_WriteTxn_1                     778.5n ± 1%   758.5n ± 2%  -2.58% (p=0.002 n=6)
DB_WriteTxn_CommitOnly_1Table     436.2n ± 2%   414.9n ± 1%  -4.89% (p=0.002 n=6)
geomean                           582.8n        561.0n       -3.74%

                │     old     │                new                │
                │ objects/sec │ objects/sec  vs base              │
DB_WriteTxn_1     1.284M ± 1%   1.318M ± 2%  +2.65% (p=0.002 n=6)

                                │    old     │                new                │
                                │    B/op    │    B/op     vs base               │
DB_WriteTxn_1                     656.0 ± 0%   632.0 ± 0%   -3.66% (p=0.002 n=6)
DB_WriteTxn_CommitOnly_1Table     224.0 ± 0%   200.0 ± 0%  -10.71% (p=0.002 n=6)
geomean                           383.3        355.5        -7.25%

                                │    old     │                new                │
                                │ allocs/op  │ allocs/op   vs base               │
DB_WriteTxn_1                     13.00 ± 0%   12.00 ± 0%   -7.69% (p=0.002 n=6)
DB_WriteTxn_CommitOnly_1Table     4.000 ± 0%   3.000 ± 0%  -25.00% (p=0.002 n=6)
geomean                           7.211        6.000       -16.79%

AIL:3
Signed-off-by: Jussi Maki <jussi@isovalent.com>
Commit() iterated over all tables in the database twice, once to commit
the indexes of the locked tables and once to refresh the unlocked table
entries from the current root, and Abort() once to find the locked
tables. With many tables this dominated the cost of small write
transactions.

Keep track of the locked table entries in the transaction and iterate
over them instead. Refresh the unlocked entries only if the root has
changed since the transaction was created by copying the current root
and putting the locked entries back into it.

                                   │     old     │                new                 │
                                   │   sec/op    │   sec/op     vs base               │
DB_WriteTxn_CommitOnly_100Tables     695.2n ± 1%   542.7n ± 1%  -21.94% (p=0.000 n=8)
DB_WriteTxn_CommitOnly_1Table        399.1n ± 1%   396.8n ± 0%        ~ (p=0.069 n=8)
DB_WriteTxnCommit100                 686.3n ± 1%   535.7n ± 1%  -21.95% (p=0.000 n=8)
geomean                              575.3n        486.8n       -15.39%

AIL:3
Signed-off-by: Jussi Maki <jussi@isovalent.com>
A single table write transaction called time.Now() six times: twice
around locking the tables, twice when locking each table, once per
table when releasing it and once for the transaction duration.

Have SortableMutexes.Lock() return the start and acquire times and
derive the per-mutex acquire durations from consecutive timestamps. On
commit and abort take a single timestamp for releasing the tables and
for the transaction duration metric.

                                │     old      │                 new                  │
                                │    sec/op    │    sec/op     vs base                │
DB_WriteTxn_1                     742.0n ± 20%   703.9n ± 11%   -5.14% (p=0.023 n=10)
DB_WriteTxn_CommitOnly_1Table     431.9n ±  8%   369.6n ±  8%  -14.44% (p=0.002 n=10)
DB_NewWriteTxn                    384.4n ± 11%   338.9n ± 35%        ~ (p=0.075 n=10)
geomean                           497.6n         445.1n        -10.55%

AIL:3
Signed-off-by: Jussi Maki <jussi@isovalent.com>
modify() and delete() already look up the table entry and check that it
is locked, but then used mustIndexWriteTxn() for each index which looked
up the table entry again through the TableMeta interface and re-checked
the lock. Get the index transactions directly from the table entry.

                                 │     old     │                new                 │
                                 │   sec/op    │   sec/op     vs base               │
DB_WriteTxn_100                    294.7n ± 4%   287.2n ± 3%       ~ (p=0.148 n=10)
DB_WriteTxn_100_SecondaryIndex     470.0n ± 2%   457.8n ± 2%  -2.60% (p=0.004 n=10)
DB_Modify                          350.2µ ± 5%   341.4µ ± 6%       ~ (p=0.218 n=10)
DB_RandomReplace                   601.3µ ± 5%   602.7µ ± 5%       ~ (p=0.529 n=10)
geomean                            13.07µ        12.82µ       -1.86%

AIL:3
Signed-off-by: Jussi Maki <jussi@isovalent.com>
@joamaki
joamaki force-pushed the pr/joamaki/minor-writetxn-optimizations branch from df64b71 to c7554ca Compare September 30, 2026 07:15
@joamaki
joamaki marked this pull request as ready for review September 30, 2026 07:17
@joamaki
joamaki requested a review from a team as a code owner September 30, 2026 07:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant