Skip to content

statedb: Restore ability to use Changes with WriteTxn#142

Merged
joamaki merged 1 commit intomainfrom
pr/joamaki/allow-changes-writetxn
Feb 9, 2026
Merged

statedb: Restore ability to use Changes with WriteTxn#142
joamaki merged 1 commit intomainfrom
pr/joamaki/allow-changes-writetxn

Conversation

@joamaki
Copy link
Contributor

@joamaki joamaki commented Feb 9, 2026

The index refactoring in cd27022 removed the ability to use ChangeIterator.Next with a WriteTxn targeting the table being observed. This was done to avoid having to hold onto the old instance of the tableEntry, but it broke a useful pattern for Changes(): the ability for a component A to write into a table and component B to be able to observe and "augment" the objects created by A.

Restore this ability to by keeping a pointer to the old root in the write transaction.

Before:

BenchmarkDB_WriteTxn_CommitOnly_100Tables-8      1428603               838.6 ns/op          1112 B/op          5 allocs/op
BenchmarkDB_WriteTxn_CommitOnly_1Table-8         2391542               503.3 ns/op           224 B/op          5 allocs/op
BenchmarkDB_NewWriteTxn-8                        2607277               458.1 ns/op           200 B/op          4 allocs/op
BenchmarkDB_WriteTxnCommit100-8                  1455978               823.8 ns/op          1096 B/op          5 allocs/op

After:

BenchmarkDB_WriteTxn_CommitOnly_100Tables-8      1239177               962.4 ns/op          1112 B/op          5 allocs/op
BenchmarkDB_WriteTxn_CommitOnly_1Table-8         2332510               515.2 ns/op           224 B/op          5 allocs/op
BenchmarkDB_NewWriteTxn-8                        2566347               468.2 ns/op           200 B/op          4 allocs/op
BenchmarkDB_WriteTxnCommit100-8                  1452818               892.0 ns/op          1096 B/op          5 allocs/op

No practical difference since we keep a pool for writeTxnHandle and thus don't really allocate more memory even though the writeTxnHandle is larger now. The impact this may have is to workloads that have a huge table and churn through all objects and now WriteTxn holds onto both the old root and the new root being prepared and thus do not allow GC to collect old objects. This however seems unlikely to be an issue since we do hold onto the old root via [DB] and only way for constructing lots of potentially garbage objects is to have two tables churning and have the two WriteTxn's hold onto "garbage" of the other table that is no longer reachable via [DB.ReadTxn].

The index refactoring in cd27022 removed
the ability to use ChangeIterator.Next with a WriteTxn targeting the table
being observed. This was done to avoid having to hold onto the old instance
of the tableEntry, but it broke a useful pattern for Changes(): the ability
for a component A to write into a table and component B to be able to observe
and "augment" the objects created by A.

Restore this ability to by keeping a pointer to the old root in the write
transaction.

Before:
BenchmarkDB_WriteTxn_CommitOnly_100Tables-8      1428603               838.6 ns/op          1112 B/op          5 allocs/op
BenchmarkDB_WriteTxn_CommitOnly_1Table-8         2391542               503.3 ns/op           224 B/op          5 allocs/op
BenchmarkDB_NewWriteTxn-8                        2607277               458.1 ns/op           200 B/op          4 allocs/op
BenchmarkDB_WriteTxnCommit100-8                  1455978               823.8 ns/op          1096 B/op          5 allocs/op

After:
BenchmarkDB_WriteTxn_CommitOnly_100Tables-8      1239177               962.4 ns/op          1112 B/op          5 allocs/op
BenchmarkDB_WriteTxn_CommitOnly_1Table-8         2332510               515.2 ns/op           224 B/op          5 allocs/op
BenchmarkDB_NewWriteTxn-8                        2566347               468.2 ns/op           200 B/op          4 allocs/op
BenchmarkDB_WriteTxnCommit100-8                  1452818               892.0 ns/op          1096 B/op          5 allocs/op

No practical difference since we keep a pool for writeTxnHandle and thus don't really allocate more memory even though
the writeTxnHandle is larger now. The impact this may have is to workloads that have a huge table and churn through
all objects and now WriteTxn holds onto both the old root and the new root being prepared and thus do not allow GC
to collect old objects. This however seems unlikely to be an issue since we do hold onto the old root via [DB]
and only way for constructing lots of potentially garbage objects is to have two tables churning and have the
two WriteTxn's hold onto "garbage" of the other table that is no longer reachable via [DB.ReadTxn].

Signed-off-by: Jussi Maki <jussi.maki@isovalent.com>
@joamaki joamaki requested a review from giorio94 February 9, 2026 10:52
@joamaki joamaki requested a review from a team as a code owner February 9, 2026 10:52
@joamaki joamaki requested review from dylandreimerink and removed request for a team February 9, 2026 10:52
@github-actions
Copy link

github-actions bot commented Feb 9, 2026

$ make
go build ./...
go: downloading go.yaml.in/yaml/v3 v3.0.3
go: downloading github.com/cilium/hive v0.0.0-20250731144630-28e7a35ed227
go: downloading golang.org/x/time v0.5.0
go: downloading github.com/spf13/cobra v1.8.0
go: downloading github.com/spf13/pflag v1.0.5
go: downloading github.com/cilium/stream v0.0.0-20240209152734-a0792b51812d
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/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/spf13/cast v1.6.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.8.4
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	414.374s	coverage: 78.5% of statements
ok  	github.com/cilium/statedb/index	0.005s	coverage: 28.7% of statements
ok  	github.com/cilium/statedb/internal	0.021s	coverage: 42.9% of statements
ok  	github.com/cilium/statedb/lpm	4.798s	coverage: 77.6% of statements
ok  	github.com/cilium/statedb/part	59.799s	coverage: 87.6% of statements
ok  	github.com/cilium/statedb/reconciler	0.248s	coverage: 92.9% 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	39.830s
ok  	github.com/cilium/statedb/index	1.014s
ok  	github.com/cilium/statedb/internal	1.025s
ok  	github.com/cilium/statedb/lpm	2.900s
ok  	github.com/cilium/statedb/part	35.484s
ok  	github.com/cilium/statedb/reconciler	1.375s
?   	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: AMD EPYC 7763 64-Core Processor                
BenchmarkDB_WriteTxn_1-4                      	  697321	      1680 ns/op	    595397 objects/sec	    1000 B/op	      16 allocs/op
BenchmarkDB_WriteTxn_10-4                     	 1718469	       697.0 ns/op	   1434806 objects/sec	     520 B/op	       8 allocs/op
BenchmarkDB_WriteTxn_100-4                    	 2141253	       602.0 ns/op	   1661239 objects/sec	     490 B/op	       7 allocs/op
BenchmarkDB_WriteTxn_1000-4                   	 1929946	       621.7 ns/op	   1608401 objects/sec	     447 B/op	       7 allocs/op
BenchmarkDB_WriteTxn_100_SecondaryIndex-4     	  811506	      1292 ns/op	    774217 objects/sec	    1007 B/op	      20 allocs/op
BenchmarkDB_WriteTxn_CommitOnly_100Tables-4   	 1000000	      1207 ns/op	    1112 B/op	       5 allocs/op
BenchmarkDB_WriteTxn_CommitOnly_1Table-4      	 1620358	       738.7 ns/op	     224 B/op	       5 allocs/op
BenchmarkDB_NewWriteTxn-4                     	 1773871	       675.0 ns/op	     200 B/op	       4 allocs/op
BenchmarkDB_WriteTxnCommit100-4               	 1000000	      1148 ns/op	    1096 B/op	       5 allocs/op
BenchmarkDB_NewReadTxn-4                      	549960415	         2.180 ns/op	       0 B/op	       0 allocs/op
BenchmarkDB_Modify-4                          	    1702	    678502 ns/op	   1473836 objects/sec	  471647 B/op	    8072 allocs/op
BenchmarkDB_GetInsert-4                       	    1515	    756031 ns/op	   1322697 objects/sec	  455644 B/op	    8072 allocs/op
BenchmarkDB_RandomInsert-4                    	    1830	    637910 ns/op	   1567620 objects/sec	  447631 B/op	    7072 allocs/op
BenchmarkDB_RandomReplace-4                   	     480	   2486511 ns/op	    402170 objects/sec	 1924771 B/op	   29102 allocs/op
BenchmarkDB_SequentialInsert-4                	    1922	    619436 ns/op	   1614373 objects/sec	  447630 B/op	    7072 allocs/op
BenchmarkDB_SequentialInsert_Prefix-4         	     487	   2467649 ns/op	    405244 objects/sec	 3564265 B/op	   45542 allocs/op
BenchmarkDB_Changes_Baseline-4                	    1590	    753179 ns/op	   1327705 objects/sec	  507772 B/op	    9163 allocs/op
BenchmarkDB_Changes-4                         	     904	   1279041 ns/op	    781836 objects/sec	  708929 B/op	   12314 allocs/op
BenchmarkDB_RandomLookup-4                    	   22446	     53426 ns/op	  18717645 objects/sec	       0 B/op	       0 allocs/op
BenchmarkDB_SequentialLookup-4                	   26742	     44807 ns/op	  22318167 objects/sec	       0 B/op	       0 allocs/op
BenchmarkDB_Prefix_SecondaryIndex-4           	    7195	    163216 ns/op	   6126839 objects/sec	  124920 B/op	    1025 allocs/op
BenchmarkDB_FullIteration_All-4               	     942	   1262207 ns/op	  79226314 objects/sec	     104 B/op	       4 allocs/op
BenchmarkDB_FullIteration_Prefix-4            	     891	   1304187 ns/op	  76676135 objects/sec	     136 B/op	       5 allocs/op
BenchmarkDB_FullIteration_Get-4               	     214	   5519962 ns/op	  18116067 objects/sec	       0 B/op	       0 allocs/op
BenchmarkDB_FullIteration_Get_Secondary-4     	     117	  10206907 ns/op	   9797287 objects/sec	       0 B/op	       0 allocs/op
BenchmarkDB_FullIteration_ReadTxnGet-4        	     216	   5550702 ns/op	  18015740 objects/sec	       0 B/op	       0 allocs/op
BenchmarkDB_PropagationDelay-4                	  609922	      1744 ns/op	        14.00 50th_µs	        19.00 90th_µs	        48.00 99th_µs	    1120 B/op	      19 allocs/op
BenchmarkDB_WriteTxn_100_LPMIndex-4           	  520508	      2326 ns/op	    429911 objects/sec	    1778 B/op	      37 allocs/op
BenchmarkDB_WriteTxn_1_LPMIndex-4             	  130932	     14351 ns/op	     69682 objects/sec	   15753 B/op	      81 allocs/op
BenchmarkDB_LPMIndex_Get-4                    	     390	   3011507 ns/op	   3320597 objects/sec	       0 B/op	       0 allocs/op
BenchmarkWatchSet_4-4                         	 2137464	       556.5 ns/op	     296 B/op	       4 allocs/op
BenchmarkWatchSet_16-4                        	  667856	      1575 ns/op	    1096 B/op	       5 allocs/op
BenchmarkWatchSet_128-4                       	   87555	     13669 ns/op	    8904 B/op	       5 allocs/op
BenchmarkWatchSet_1024-4                      	    8610	    136072 ns/op	   73743 B/op	       5 allocs/op
PASS
ok  	github.com/cilium/statedb	43.437s
PASS
ok  	github.com/cilium/statedb/index	0.004s
goos: linux
goarch: amd64
pkg: github.com/cilium/statedb/internal
cpu: AMD EPYC 7763 64-Core Processor                
Benchmark_SortableMutex-4   	 6185431	       193.7 ns/op	       0 B/op	       0 allocs/op
PASS
ok  	github.com/cilium/statedb/internal	1.202s
goos: linux
goarch: amd64
pkg: github.com/cilium/statedb/lpm
cpu: AMD EPYC 7763 64-Core Processor                
Benchmark_txn_insert/batchSize=1-4         	    1902	    637856 ns/op	   1567753 objects/sec	  838410 B/op	   13975 allocs/op
Benchmark_txn_insert/batchSize=10-4        	    2941	    393274 ns/op	   2542757 objects/sec	  385194 B/op	    6668 allocs/op
Benchmark_txn_insert/batchSize=100-4       	    3211	    367996 ns/op	   2717420 objects/sec	  345613 B/op	    6027 allocs/op
Benchmark_txn_delete/batchSize=1-4         	    1568	    762981 ns/op	   1310649 objects/sec	 1286469 B/op	   13976 allocs/op
Benchmark_txn_delete/batchSize=10-4        	    3148	    378571 ns/op	   2641515 objects/sec	  372418 B/op	    5769 allocs/op
Benchmark_txn_delete/batchSize=100-4       	    3420	    339691 ns/op	   2943850 objects/sec	  286753 B/op	    5038 allocs/op
Benchmark_LPM_Lookup-4                     	    7891	    156377 ns/op	   6394795 objects/sec	       0 B/op	       0 allocs/op
Benchmark_LPM_All-4                        	  134142	      9003 ns/op	 111070307 objects/sec	      32 B/op	       1 allocs/op
Benchmark_LPM_Prefix-4                     	  133177	      9125 ns/op	 109586556 objects/sec	      32 B/op	       1 allocs/op
Benchmark_LPM_LowerBound-4                 	  242322	      4861 ns/op	 102869945 objects/sec	     288 B/op	       2 allocs/op
PASS
ok  	github.com/cilium/statedb/lpm	11.949s
goos: linux
goarch: amd64
pkg: github.com/cilium/statedb/part
cpu: AMD EPYC 7763 64-Core Processor                
Benchmark_Uint64Map_Random-4                  	    1611	    729782 ns/op	   1370272 items/sec	 2528463 B/op	    6046 allocs/op
Benchmark_Uint64Map_Sequential-4              	    1934	    615342 ns/op	   1625113 items/sec	 2216723 B/op	    5754 allocs/op
Benchmark_Uint64Map_Sequential_Insert-4       	    2149	    554802 ns/op	   1802445 items/sec	 2208720 B/op	    4753 allocs/op
Benchmark_Uint64Map_Sequential_Txn_Insert-4   	   10000	    102102 ns/op	   9794111 items/sec	   86352 B/op	    2028 allocs/op
Benchmark_Uint64Map_Random_Insert-4           	    1784	    670602 ns/op	   1491197 items/sec	 2519878 B/op	    5044 allocs/op
Benchmark_Uint64Map_Random_Txn_Insert-4       	    6884	    166032 ns/op	   6022927 items/sec	  119574 B/op	    2418 allocs/op
Benchmark_Insert_RootOnlyWatch-4              	   10000	    112956 ns/op	   8853037 objects/sec	   71504 B/op	    2033 allocs/op
Benchmark_Insert-4                            	    7022	    168673 ns/op	   5928640 objects/sec	  186937 B/op	    3060 allocs/op
Benchmark_Modify-4                            	   10000	    103236 ns/op	   9686547 objects/sec	   58224 B/op	    1007 allocs/op
Benchmark_GetInsert-4                         	    8698	    132986 ns/op	   7519615 objects/sec	   58224 B/op	    1007 allocs/op
Benchmark_Replace-4                           	31841386	        37.65 ns/op	  26559186 objects/sec	       0 B/op	       0 allocs/op
Benchmark_Replace_RootOnlyWatch-4             	31259274	        37.90 ns/op	  26384973 objects/sec	       0 B/op	       0 allocs/op
Benchmark_txn_1-4                             	 5730718	       207.9 ns/op	   4809479 objects/sec	     168 B/op	       3 allocs/op
Benchmark_txn_10-4                            	10043313	       119.4 ns/op	   8377051 objects/sec	      86 B/op	       2 allocs/op
Benchmark_txn_100-4                           	11847519	       100.9 ns/op	   9907527 objects/sec	      80 B/op	       2 allocs/op
Benchmark_txn_1000-4                          	 9995095	       117.8 ns/op	   8487876 objects/sec	      65 B/op	       2 allocs/op
Benchmark_txn_delete_1-4                      	 4982335	       238.2 ns/op	   4198382 objects/sec	     664 B/op	       4 allocs/op
Benchmark_txn_delete_10-4                     	10843818	       110.4 ns/op	   9054911 objects/sec	     106 B/op	       1 allocs/op
Benchmark_txn_delete_100-4                    	11637084	       101.7 ns/op	   9831484 objects/sec	      47 B/op	       1 allocs/op
Benchmark_txn_delete_1000-4                   	14109571	        84.44 ns/op	  11842860 objects/sec	      24 B/op	       1 allocs/op
Benchmark_Get-4                               	   45535	     26375 ns/op	  37914102 objects/sec	       0 B/op	       0 allocs/op
Benchmark_All-4                               	  119691	     10457 ns/op	  95630647 objects/sec	       0 B/op	       0 allocs/op
Benchmark_Iterator_All-4                      	  137739	      9967 ns/op	 100333455 objects/sec	       0 B/op	       0 allocs/op
Benchmark_Iterator_Next-4                     	  157797	      7463 ns/op	 133990569 objects/sec	     896 B/op	       1 allocs/op
Benchmark_Hashmap_Insert-4                    	   14956	     80297 ns/op	  12453809 objects/sec	   74264 B/op	      20 allocs/op
Benchmark_Hashmap_Get_Uint64-4                	  135264	      8862 ns/op	 112843592 objects/sec	       0 B/op	       0 allocs/op
Benchmark_Hashmap_Get_Bytes-4                 	  108512	     11009 ns/op	  90834359 objects/sec	       0 B/op	       0 allocs/op
Benchmark_Delete_Random-4                     	      81	  15001250 ns/op	   6666111 objects/sec	 2111842 B/op	  102364 allocs/op
Benchmark_find16-4                            	226294816	         5.299 ns/op	       0 B/op	       0 allocs/op
Benchmark_findIndex16-4                       	100000000	        10.24 ns/op	       0 B/op	       0 allocs/op
Benchmark_find4-4                             	424163169	         2.826 ns/op	       0 B/op	       0 allocs/op
Benchmark_findIndex4-4                        	349626944	         3.427 ns/op	       0 B/op	       0 allocs/op
PASS
ok  	github.com/cilium/statedb/part	39.130s
PASS
ok  	github.com/cilium/statedb/reconciler	0.004s
?   	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 2.08 seconds (batch size 1000)
Throughput 479971.45 objects per second
817MB total allocated, 6015178 in-use objects, 338MB bytes in use

@joamaki joamaki merged commit b594c3b into main Feb 9, 2026
1 check passed
@joamaki joamaki deleted the pr/joamaki/allow-changes-writetxn branch February 9, 2026 11:10
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.

2 participants