@@ -18,7 +18,9 @@ use graph::{
18
18
use itertools:: Itertools ;
19
19
20
20
use crate :: {
21
- catalog, deployment,
21
+ catalog,
22
+ copy:: BATCH_STATEMENT_TIMEOUT ,
23
+ deployment,
22
24
relational:: { Table , VID_COLUMN } ,
23
25
vid_batcher:: { VidBatcher , VidRange } ,
24
26
} ;
@@ -105,16 +107,15 @@ impl TablePair {
105
107
tracker. start_copy_final ( conn, & self . src , range) ?;
106
108
107
109
while !batcher. finished ( ) {
108
- let ( _, rows) = batcher. step ( |start, end| {
109
- conn. transaction ( |conn| {
110
- // Page through all rows in `src` in batches of `batch_size`
111
- // and copy the ones that are visible to queries at block
112
- // heights between `earliest_block` and `final_block`, but
113
- // whose block_range does not extend past `final_block`
114
- // since they could still be reverted while we copy.
115
- // The conditions on `block_range` are expressed redundantly
116
- // to make more indexes useable
117
- sql_query ( format ! (
110
+ let rows = batch_with_timeout ( conn, & mut batcher, |conn, start, end| {
111
+ // Page through all rows in `src` in batches of `batch_size`
112
+ // and copy the ones that are visible to queries at block
113
+ // heights between `earliest_block` and `final_block`, but
114
+ // whose block_range does not extend past `final_block`
115
+ // since they could still be reverted while we copy.
116
+ // The conditions on `block_range` are expressed redundantly
117
+ // to make more indexes useable
118
+ sql_query ( format ! (
118
119
"/* controller=prune,phase=final,start_vid={start},batch_size={batch_size} */ \
119
120
insert into {dst}({column_list}) \
120
121
select {column_list} from {src} \
@@ -128,13 +129,12 @@ impl TablePair {
128
129
dst = self . dst. qualified_name,
129
130
batch_size = end - start + 1 ,
130
131
) )
131
- . bind :: < Integer , _ > ( earliest_block)
132
- . bind :: < Integer , _ > ( final_block)
133
- . bind :: < BigInt , _ > ( start)
134
- . bind :: < BigInt , _ > ( end)
135
- . execute ( conn)
136
- . map_err ( StoreError :: from)
137
- } )
132
+ . bind :: < Integer , _ > ( earliest_block)
133
+ . bind :: < Integer , _ > ( final_block)
134
+ . bind :: < BigInt , _ > ( start)
135
+ . bind :: < BigInt , _ > ( end)
136
+ . execute ( conn)
137
+ . map_err ( StoreError :: from)
138
138
} ) ?;
139
139
let rows = rows. unwrap_or ( 0 ) ;
140
140
tracker. finish_batch ( conn, & self . src , rows as i64 , & batcher) ?;
@@ -168,14 +168,13 @@ impl TablePair {
168
168
tracker. start_copy_nonfinal ( conn, & self . src , range) ?;
169
169
170
170
while !batcher. finished ( ) {
171
- let ( _ , rows) = batcher . step ( | start, end| {
171
+ let rows = batch_with_timeout ( conn , & mut batcher , |conn , start, end| {
172
172
// Page through all the rows in `src` in batches of
173
173
// `batch_size` that are visible to queries at block heights
174
174
// starting right after `final_block`. The conditions on
175
175
// `block_range` are expressed redundantly to make more
176
176
// indexes useable
177
- conn. transaction ( |conn| {
178
- sql_query ( format ! (
177
+ sql_query ( format ! (
179
178
"/* controller=prune,phase=nonfinal,start_vid={start},batch_size={batch_size} */ \
180
179
insert into {dst}({column_list}) \
181
180
select {column_list} from {src} \
@@ -192,7 +191,6 @@ impl TablePair {
192
191
. bind :: < BigInt , _ > ( end)
193
192
. execute ( conn)
194
193
. map_err ( StoreError :: from)
195
- } )
196
194
} ) ?;
197
195
let rows = rows. unwrap_or ( 0 ) ;
198
196
@@ -460,7 +458,8 @@ impl Layout {
460
458
461
459
tracker. start_delete ( conn, table, range, & batcher) ?;
462
460
while !batcher. finished ( ) {
463
- let ( _, rows) = batcher. step ( |start, end| { sql_query ( format ! (
461
+ let rows = batch_with_timeout ( conn, & mut batcher, |conn, start, end| {
462
+ sql_query ( format ! (
464
463
"/* controller=prune,phase=delete,start_vid={start},batch_size={batch_size} */ \
465
464
delete from {qname} \
466
465
where coalesce(upper(block_range), 2147483647) <= $1 \
@@ -471,7 +470,8 @@ impl Layout {
471
470
. bind :: < Integer , _ > ( req. earliest_block )
472
471
. bind :: < BigInt , _ > ( start)
473
472
. bind :: < BigInt , _ > ( end)
474
- . execute ( conn) . map_err ( StoreError :: from) } ) ?;
473
+ . execute ( conn) . map_err ( StoreError :: from)
474
+ } ) ?;
475
475
let rows = rows. unwrap_or ( 0 ) ;
476
476
477
477
tracker. finish_batch ( conn, table, -( rows as i64 ) , & batcher) ?;
@@ -501,6 +501,42 @@ impl Layout {
501
501
}
502
502
}
503
503
504
+ /// Perform a step with the `batcher`. If that step takes longer than
505
+ /// `BATCH_STATEMENT_TIMEOUT`, kill the query and reset the batch size of
506
+ /// the batcher to 1 and perform a step with that size which we assume takes
507
+ /// less than `BATCH_STATEMENT_TIMEOUT`.
508
+ ///
509
+ /// Doing this serves as a safeguard against very bad batch size estimations
510
+ /// so that batches never take longer than `BATCH_SIZE_TIMEOUT`
511
+ fn batch_with_timeout < F , T > (
512
+ conn : & mut PgConnection ,
513
+ batcher : & mut VidBatcher ,
514
+ query : F ,
515
+ ) -> Result < Option < T > , StoreError >
516
+ where
517
+ F : Fn ( & mut PgConnection , i64 , i64 ) -> Result < T , StoreError > ,
518
+ {
519
+ let res = batcher
520
+ . step ( |start, end| {
521
+ conn. transaction ( |conn| {
522
+ if let Some ( timeout) = BATCH_STATEMENT_TIMEOUT . as_ref ( ) {
523
+ conn. batch_execute ( timeout) ?;
524
+ }
525
+ query ( conn, start, end)
526
+ } )
527
+ } )
528
+ . map ( |( _, res) | res) ;
529
+
530
+ if !matches ! ( res, Err ( StoreError :: StatementTimeout ) ) {
531
+ return res;
532
+ }
533
+
534
+ batcher. set_batch_size ( 1 ) ;
535
+ batcher
536
+ . step ( |start, end| conn. transaction ( |conn| query ( conn, start, end) ) )
537
+ . map ( |( _, res) | res)
538
+ }
539
+
504
540
mod status {
505
541
use std:: sync:: Arc ;
506
542
0 commit comments