@@ -18,10 +18,10 @@ package backup
1818
1919import (
2020 "context"
21- "encoding/hex"
2221 "fmt"
2322 "maps"
2423 "runtime"
24+ "slices"
2525 "time"
2626
2727 "github.com/c2h5oh/datasize"
@@ -58,7 +58,7 @@ func OpenPair(from, to string, label kv.Label, targetPageSize datasize.ByteSize,
5858 return src , dst
5959}
6060
61- func Kv2kv (ctx context.Context , src kv.RoDB , dst kv.RwDB , tables []string , readAheadThreads int , logger log.Logger ) error {
61+ func Kv2kv (ctx context.Context , src kv.RoDB , dst kv.RwDB , tables []string , logger log.Logger ) error {
6262 srcTx , err1 := src .BeginRo (ctx )
6363 if err1 != nil {
6464 return err1
@@ -79,76 +79,122 @@ func Kv2kv(ctx context.Context, src kv.RoDB, dst kv.RwDB, tables []string, readA
7979 }
8080 }
8181
82- for name , b := range tablesMap {
83- if b .IsDeprecated {
82+ var copiedTables int
83+ var copiedRows uint64
84+ for _ , name := range slices .Sorted (maps .Keys (tablesMap )) { // deterministic order for reproducible benchmarks
85+ if tablesMap [name ].IsDeprecated {
8486 continue
8587 }
86- if err := backupTable (ctx , srcTx , dst , name , logEvery , logger ); err != nil {
88+ rows , err := backupTable (ctx , src , srcTx , dst , name , logEvery , logger )
89+ if err != nil {
8790 return err
8891 }
92+ if rows > 0 {
93+ copiedTables ++
94+ copiedRows += rows
95+ }
8996 }
90- logger .Info ("done" )
97+ logger .Info ("done" , "tablesWithData" , copiedTables , "rows" , common . PrettyCounter ( copiedRows ) )
9198 return nil
9299}
93100
94- func backupTable (ctx context.Context , srcTx kv.Tx , dst kv.RwDB , table string , logEvery * time.Ticker , logger log.Logger ) error {
95- var total uint64
101+ // warmupWhile warms `table` into RAM in the background while `fn` runs, and
102+ // cancels the warmup as soon as `fn` returns — bounds warmup to one table at a
103+ // time and keeps fn's reads off disk on chaindata >> RAM.
104+ func warmupWhile (ctx context.Context , db kv.RoDB , table string , fn func () error ) error {
105+ warmupCtx , cancel := context .WithCancel (ctx )
106+ done := make (chan struct {})
107+ go func () {
108+ defer close (done )
109+ db .WarmupTable (warmupCtx , table )
110+ }()
111+ defer func () {
112+ cancel ()
113+ <- done
114+ }()
115+ return fn ()
116+ }
117+
118+ func backupTable (ctx context.Context , src kv.RoDB , srcTx kv.Tx , dst kv.RwDB , table string , logEvery * time.Ticker , logger log.Logger ) (uint64 , error ) {
119+ t := time .Now ()
96120 srcC , err := srcTx .Cursor (table )
97121 if err != nil {
98- return err
122+ return 0 , err
99123 }
100124 defer srcC .Close ()
101- total , _ = srcTx .Count (table )
125+ total , _ := srcTx .Count (table )
126+ size , _ := srcTx .BucketSize (table )
127+ if total > 0 {
128+ logger .Info ("[mdbx_to_mdbx] copying" , "table" , table , "rows" , common .PrettyCounter (total ), "size" , common .ByteCount (size ))
129+ }
130+
131+ // Parallel read-ahead: keep a bounded band of pages warm just ahead of the
132+ // copy cursor (cold page faults are slow; one reader can't saturate nvme).
133+ // No-op unless WARMUP_TABLE_WORKERS is set.
134+ var ra * kv.ReadAheader
135+ if workers := int (dbg .WarmupTableWorkers ); workers > 0 && total > 0 {
136+ ra = kv .NewReadAheader (ctx , src , table , nil , workers )
137+ }
138+ defer ra .Close ()
102139
103140 if err := dst .Update (ctx , func (tx kv.RwTx ) error {
104141 return tx .ClearTable (table )
105142 }); err != nil {
106- return err
143+ return 0 , err
107144 }
108145 dstTx , err := dst .BeginRw (ctx )
109146 if err != nil {
110- return err
147+ return 0 , err
111148 }
112149 defer dstTx .Rollback ()
113150
114151 c , err := dstTx .RwCursor (table )
115152 if err != nil {
116- return err
153+ return 0 , err
117154 }
118155 defer c .Close ()
119156 casted , isDupsort := c .(kv.RwCursorDupSort )
120157 i := uint64 (0 )
121158
122159 for k , v , err := srcC .First (); k != nil ; k , v , err = srcC .Next () {
123160 if err != nil {
124- return err
161+ return 0 , err
162+ }
163+ if i % 1024 == 0 {
164+ ra .SetPos (k )
125165 }
126166
127167 if isDupsort {
128168 if err = casted .AppendDup (k , v ); err != nil {
129- return err
169+ return 0 , err
130170 }
131171 } else {
132172 if err = c .Append (k , v ); err != nil {
133- return err
173+ return 0 , err
134174 }
135175 }
136176
137177 i ++
138178 if i % 100_000 == 0 {
139179 select {
140180 case <- ctx .Done ():
141- return ctx .Err ()
181+ return 0 , ctx .Err ()
142182 case <- logEvery .C :
143183 var m runtime.MemStats
144184 dbg .ReadMemStats (& m )
145185 logger .Info ("Progress" , "table" , table , "progress" ,
146- fmt .Sprintf ("%s/%s" , common .PrettyCounter (i ), common .PrettyCounter (total )), "key" , hex .EncodeToString (k ),
186+ fmt .Sprintf ("%s/%s" , common .PrettyCounter (i ), common .PrettyCounter (total )),
187+ "size" , common .ByteCount (size ), "keys/s" , uint64 (float64 (i )/ time .Since (t ).Seconds ()),
147188 "alloc" , common .ByteCount (m .Alloc ), "sys" , common .ByteCount (m .Sys ))
148189 default :
149190 }
150191 }
151192 }
193+
194+ // TODO: Unwind doesn't need to decrement the auto-increment sequence — it's
195+ // not exposed to users and not part of consensus, so we could switch to
196+ // mdbx's native Sequence.
197+
152198 // migrate bucket sequences to native mdbx implementation
153199 //currentID, err := srcTx.Sequence(name, 0)
154200 //if err != nil {
@@ -159,17 +205,17 @@ func backupTable(ctx context.Context, srcTx kv.Tx, dst kv.RwDB, table string, lo
159205 // return err
160206 //}
161207 if err2 := dstTx .Commit (); err2 != nil {
162- return err2
208+ return 0 , err2
163209 }
164- return nil
210+ return i , nil
165211}
166212
167- const ReadAheadThreads = 2048
168-
169- func ClearTables (ctx context.Context , tx kv.RwTx , tables ... string ) error {
213+ func ClearTables (ctx context.Context , db kv.RoDB , tx kv.RwTx , tables ... string ) error {
170214 for _ , tbl := range tables {
171215 log .Info ("Clear" , "table" , tbl )
172- if err := tx .ClearTable (tbl ); err != nil {
216+ if err := warmupWhile (ctx , db , tbl , func () error {
217+ return tx .ClearTable (tbl )
218+ }); err != nil {
173219 return err
174220 }
175221 }
0 commit comments