File: clone0snapshot.h

package info (click to toggle)
mysql-8.0 8.0.44-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 1,272,892 kB
  • sloc: cpp: 4,685,345; ansic: 412,712; pascal: 108,395; java: 83,641; perl: 30,221; cs: 27,067; sql: 26,594; python: 21,816; sh: 17,285; yacc: 17,169; php: 11,522; xml: 7,388; javascript: 7,083; makefile: 1,793; lex: 1,075; awk: 670; asm: 520; objc: 183; ruby: 97; lisp: 86
file content (1075 lines) | stat: -rw-r--r-- 38,288 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
/*****************************************************************************

Copyright (c) 2017, 2025, Oracle and/or its affiliates.

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License, version 2.0, as published by the
Free Software Foundation.

This program is designed to work with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation.  The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have either included with
the program or referenced in the documentation.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
for more details.

You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA

*****************************************************************************/

/** @file include/clone0snapshot.h
 Database Physical Snapshot

 *******************************************************/

#ifndef CLONE_SNAPSHOT_INCLUDE
#define CLONE_SNAPSHOT_INCLUDE

#include "univ.i"

#include "arch0log.h"
#include "arch0page.h"
#include "clone0api.h"
#include "clone0desc.h"
#include "clone0monitor.h"
#include "fil0fil.h"
#include "sql/handler.h"

#include <map>
#include <vector>

struct Clone_file_ctx {
  /** File state:
  [CREATED] -------------> [DROPPING] --> [DROPPED] --> [DROPPED_HANDLED]
      |                        ^
      |                        |
       ----> [RENAMING] -> [RENAMED]
                 |             |
                  <------------
  */
  enum class State {
    /* Invalid state. */
    NONE,
    /* File is being dropped. */
    DROPPING,
    /* File is being renamed. */
    RENAMING,
    /* Newly created file or pre-existing before clone. */
    CREATED,
    /* File is renamed during clone. */
    RENAMED,
    /* File is deleted during clone. */
    DROPPED,
    /* File is deleted and chunk information is handled. */
    DROPPED_HANDLED
  };

  /** File extension to use with name. */
  enum class Extension {
    /* No extension. */
    NONE,
    /* Replace extension - clone file to be replaced during recovery. */
    REPLACE,
    /* DDL extension - temporary extension used during rename. */
    DDL
  };

  /** Initialize file state.
  @param[in]    extn    file name extension */
  void init(Extension extn) {
    m_state.store(State::CREATED);
    m_extension = extn;

    m_pin.store(0);
    m_modified_ddl = false;
    m_waiting = 0;

    m_next_state = CLONE_SNAPSHOT_NONE;

    m_meta.init();
  }

  /** Get file name with extension.
  @param[out]   name    file name. */
  void get_file_name(std::string &name) const;

  /** Mark file added by DDL.
  @param[in]    next_state      next snapshot state */
  void set_ddl(Snapshot_State next_state) {
    m_modified_ddl = true;
    m_next_state = next_state;
  }

  /** @return true iff added or modified by ddl in previous state.
  @param[in]    state   current snapshot state */
  bool by_ddl(Snapshot_State state) const {
    return m_modified_ddl && (state <= m_next_state);
  }

  /** Start waiting for DDL */
  void begin_wait() { ++m_waiting; }

  /** Finish waiting for DDL */
  void end_wait() {
    ut_a(m_waiting > 0);
    --m_waiting;
  }

  /** @return true, iff there are waiting clone tasks. */
  bool is_waiting() const { return (m_waiting > 0); }

  /** Pin the file. */
  void pin() { ++m_pin; }

  /** Unpin the file. */
  void unpin() {
    ut_a(m_pin > 0);
    --m_pin;
  }

  /** @return true, iff clone tasks are using the file. */
  bool is_pinned() const { return (m_pin.load() > 0); }

  /** @return true, iff DDL is modifying file. */
  bool modifying() const {
    State state = m_state.load();
    return (state == State::RENAMING || state == State::DROPPING);
  }

  /** @return true, iff DDL is deleting file. */
  bool deleting() const {
    State state = m_state.load();
    return (state == State::DROPPING);
  }

  /** @return true, iff file is already deleted. */
  bool deleted() const {
    State state = m_state.load();
    return (state == State::DROPPED || state == State::DROPPED_HANDLED);
  }

  /** @return true, iff file is already renamed. */
  bool renamed() const {
    State state = m_state.load();
    return (state == State::RENAMED);
  }

  /** @return file metadata. */
  Clone_File_Meta *get_file_meta() { return &m_meta; }

  /** @return file metadata for read. */
  const Clone_File_Meta *get_file_meta_read() const { return &m_meta; }

  /** File metadata state. Modified by DDL commands. Protected by snapshot
  mutex. Atomic operation helps clone to skip mutex when no ddl. */
  std::atomic<State> m_state;

  /** File name extension. */
  Extension m_extension;

 private:
  /** Pin count incremented and decremented by clone tasks to synchronize with
  concurrent DDL. Protected by snapshot mutex. */
  std::atomic<uint32_t> m_pin;

  /** Waiting count incremented and decremented by clone tasks while waiting
  DDL file operation in progress. Protected by snapshot mutex. */
  uint32_t m_waiting;

  /** true, if file created or modified after clone is started. */
  bool m_modified_ddl{false};

  /** Next state when ddl last modified file. */
  Snapshot_State m_next_state{CLONE_SNAPSHOT_DONE};

  /** File metadata. */
  Clone_File_Meta m_meta;
};

/** Vector type for storing clone files */
using Clone_File_Vec = std::vector<Clone_file_ctx *>;

/** Map type for mapping space ID to clone file index */
using Clone_File_Map = std::map<space_id_t, uint>;

/** Page identified by space and page number */
struct Clone_Page {
  /** Tablespace ID */
  uint32_t m_space_id;

  /** Page number within tablespace */
  uint32_t m_page_no;
};

/** Comparator for storing sorted page ID. */
struct Less_Clone_Page {
  /** Less than operator for page ID.
  @param[in]    page1   first page
  @param[in]    page2   second page
  @return true, if page1 is less than page2 */
  inline bool operator()(const Clone_Page &page1,
                         const Clone_Page &page2) const {
    if (page1.m_space_id < page2.m_space_id) {
      return (true);
    }

    if (page1.m_space_id == page2.m_space_id &&
        page1.m_page_no < page2.m_page_no) {
      return (true);
    }
    return (false);
  }
};

/** Vector type for storing clone page IDs */
using Clone_Page_Vec = std::vector<Clone_Page>;

/** Set for storing unique page IDs. */
using Clone_Page_Set = std::set<Clone_Page, Less_Clone_Page>;

/** Clone handle type */
enum Clone_Handle_Type {
  /** Clone Handle for COPY */
  CLONE_HDL_COPY = 1,

  /** Clone Handle for APPLY */
  CLONE_HDL_APPLY
};

/** Default chunk size in power of 2 in unit of pages.
Chunks are reserved by each thread for multi-threaded clone. For 16k page
size, chunk size is 64M. */
const uint SNAPSHOT_DEF_CHUNK_SIZE_POW2 = 12;

/** Default block size in power of 2 in unit of pages.
Data transfer callback is invoked once for each block. This is also
the maximum size of data that would be re-send if clone is stopped
and resumed. For 16k page size, block size is 1M. */
const uint SNAPSHOT_DEF_BLOCK_SIZE_POW2 = 6;

/** Maximum block size in power of 2 in unit of pages.
For 16k page size, maximum block size is 64M. */
const uint SNAPSHOT_MAX_BLOCK_SIZE_POW2 = 12;

/** Dynamic database snapshot: Holds metadata and handle to data */
class Clone_Snapshot {
 public:
  /** RAII style guard for begin & end  of snapshot state transition. */
  class State_transit {
   public:
    /** Constructor to begin state transition.
    @param[in,out]      snapshot        Clone Snapshot
    @param[in]          new_state       State to transit */
    explicit State_transit(Clone_Snapshot *snapshot, Snapshot_State new_state);

    /** Destructor to end state transition. */
    ~State_transit();

    /** @return error code */
    int get_error() const { return m_error; }

    /** Disable copy construction */
    State_transit(State_transit const &) = delete;

    /** Disable assignment */
    State_transit &operator=(State_transit const &) = delete;

   private:
    /** Clone Snapshot */
    Clone_Snapshot *m_snapshot;

    /** Saved error while beginning transition. */
    int m_error;
  };

  /** Construct snapshot
  @param[in]    hdl_type        copy, apply
  @param[in]    clone_type      clone type
  @param[in]    arr_idx         index in global array
  @param[in]    snap_id         unique snapshot ID */
  Clone_Snapshot(Clone_Handle_Type hdl_type, Ha_clone_type clone_type,
                 uint arr_idx, uint64_t snap_id);

  /** Release contexts and free heap */
  ~Clone_Snapshot();

  /** DDL notification before the operation.
  @param[in]    type            type of DDL notification
  @param[in]    space           space ID for the ddl operation
  @param[in]    no_wait         return with error if needs to wait
  @param[in]    check_intr      check for interrupt during wait
  @param[out]   error           mysql error code
  @return true iff clone state change is blocked. */
  bool begin_ddl_state(Clone_notify::Type type, space_id_t space, bool no_wait,
                       bool check_intr, int &error);

  /** DDL notification after the operation.
  @param[in]    type    type of DDL notification
  @param[in]    space   space ID for the ddl operation */
  void end_ddl_state(Clone_notify::Type type, space_id_t space);

  /** Wait for concurrent DDL file operation and pin file.
  @param[in,out]        file_ctx        file context
  @param[out]           handle_delete   if caller needs to handle deleted state
  @return mysql error code. */
  int pin_file(Clone_file_ctx *file_ctx, bool &handle_delete);

  /** Unpin a file.
  @param[in,out]        file_ctx        file context */
  void unpin_file(Clone_file_ctx *file_ctx) { file_ctx->unpin(); }

  /** Check if DDL needs to block clone operation.
  @param[in]    file_ctx        file context
  @return true iff clone operation needs to be blocked. */
  bool blocks_clone(const Clone_file_ctx *file_ctx);

  /** @return estimated bytes on disk */
  uint64_t get_disk_estimate() const { return (m_data_bytes_disk); }

  /** Get unique snapshot identifier
  @return snapshot ID */
  uint64_t get_id() { return (m_snapshot_id); }

  /** Get snapshot index in global array
  @return array index */
  uint get_index() { return (m_snapshot_arr_idx); }

  /** Get performance schema accounting object used to monitor stage
  progress.
  @return PFS stage object */
  Clone_Monitor &get_clone_monitor() { return (m_monitor); }

  /** Get snapshot heap used for allocation during clone.
  @return heap */
  mem_heap_t *lock_heap() {
    mutex_enter(&m_snapshot_mutex);
    return (m_snapshot_heap);
  }

  /* Release snapshot heap */
  void release_heap(mem_heap_t *&heap) {
    heap = nullptr;
    mutex_exit(&m_snapshot_mutex);
  }

  /** Get snapshot state
  @return state */
  Snapshot_State get_state() { return (m_snapshot_state); }

  /** Get the redo file size for the snapshot
  @return redo file size */
  uint64_t get_redo_file_size() { return (m_redo_file_size); }

  /** Get total number of chunks for current state
  @return number of data chunks */
  uint get_num_chunks() { return (m_num_current_chunks); }

  /** Get maximum file length seen till now
  @return file name length */
  size_t get_max_file_name_length() { return (m_max_file_name_len); }

  /** Get maximum buffer size required for clone
  @return maximum dynamic buffer */
  uint get_dyn_buffer_length() {
    uint ret_len = 0;

    if (is_copy() && m_snapshot_type != HA_CLONE_BLOCKING) {
      ret_len = static_cast<uint>(2 * UNIV_PAGE_SIZE);
    }

    return (ret_len);
  }

  using File_Cbk_Func = std::function<int(Clone_file_ctx *)>;

  /** Iterate through all files in current state
  @param[in]    func    callback function
  @return error code */
  int iterate_files(File_Cbk_Func &&func);

  /** Iterate through all data files
  @param[in]    func    callback function
  @return error code */
  int iterate_data_files(File_Cbk_Func &&func);

  /** Iterate through all redo files
  @param[in]    func    callback function
  @return error code */
  int iterate_redo_files(File_Cbk_Func &&func);

  /** Fill state descriptor from snapshot
  @param[in]  do_estimate   estimate data bytes to transfer
  @param[out] state_desc    snapshot state descriptor */
  void get_state_info(bool do_estimate, Clone_Desc_State *state_desc);

  /** Set state information during apply
  @param[in]    state_desc      snapshot state descriptor */
  void set_state_info(Clone_Desc_State *state_desc);

  /** Get next state based on snapshot type
  @return next state */
  Snapshot_State get_next_state();

  /** Try to attach to snapshot
  @param[in]    hdl_type        copy, apply
  @param[in]    pfs_monitor     enable PFS monitoring
  @return true if successfully attached */
  bool attach(Clone_Handle_Type hdl_type, bool pfs_monitor);

  /** Detach from snapshot. */
  void detach();

  /** Set current snapshot aborted state. Used in error cases before exiting
  clone to make sure any DDL notifier exits waiting. */
  void set_abort();

  /** @return true, iff clone has aborted. */
  bool is_aborted() const;

  /** Start transition to new state
  @param[in]    state_desc      descriptor for next state
  @param[in]    new_state       state to move for apply
  @param[in]    temp_buffer     buffer used for collecting page IDs
  @param[in]    temp_buffer_len buffer length
  @param[in]    cbk             alter callback for long wait
  @return error code */
  int change_state(Clone_Desc_State *state_desc, Snapshot_State new_state,
                   byte *temp_buffer, uint temp_buffer_len,
                   Clone_Alert_Func cbk);

  /** Add file metadata entry at destination
  @param[in]    file_meta       file metadata from donor
  @param[in]    data_dir        destination data directory
  @param[in]    desc_create     create if doesn't exist
  @param[out]   desc_exists     descriptor already exists
  @param[out]   file_ctx        if there, set to current file context
  @return error code */
  int get_file_from_desc(const Clone_File_Meta *file_meta, const char *data_dir,
                         bool desc_create, bool &desc_exists,
                         Clone_file_ctx *&file_ctx);

  /** Rename an existing file descriptor.
  @param[in]    file_meta       renamed file metadata from donor
  @param[in]    data_dir        destination data directory
  @param[out]   file_ctx        if there, set to current file context
  @return error code */
  int rename_desc(const Clone_File_Meta *file_meta, const char *data_dir,
                  Clone_file_ctx *&file_ctx);

  /** Fix files renamed with ddl extension. The file name is checked against
  existing file and added to appropriate status file.
  @param[in]            data_dir        destination data directory
  @param[in,out]        file_ctx        Set to correct extension
  @return error code */
  int fix_ddl_extension(const char *data_dir, Clone_file_ctx *file_ctx);

  /** Add file descriptor to file list
  @param[in,out]        file_ctx        current file context
  @param[in]            ddl_create      added by DDL concurrently
  @return true, if it is the last file. */
  bool add_file_from_desc(Clone_file_ctx *&file_ctx, bool ddl_create);

  /** Extract file information from node and add to snapshot
  @param[in]    node    file node
  @param[in]    by_ddl  node is added concurrently by DDL
  @return error code */
  dberr_t add_node(fil_node_t *node, bool by_ddl);

  /** Add page ID to to the set of pages in snapshot
  @param[in]    space_id        page tablespace
  @param[in]    page_num        page number within tablespace
  @return error code */
  int add_page(uint32_t space_id, uint32_t page_num);

  /** Add redo file to snapshot
  @param[in]    file_name       file name
  @param[in]    file_size       file size in bytes
  @param[in]    file_offset     start offset
  @return error code. */
  int add_redo_file(char *file_name, uint64_t file_size, uint64_t file_offset);

  /** Get file metadata by index for current state
  @param[in]    index   file index
  @return file metadata entry */
  Clone_File_Meta *get_file_by_index(uint index);

  /** Get clone file context by index for current state
  @param[in]    index   file index
  @return file context */
  Clone_file_ctx *get_file_ctx_by_index(uint index);

  /** Get clone file context by chunk and block number.
  @param[in]    chunk_num       chunk number
  @param[in]    block_num       block number
  @param[in]    hint_index      hint file index number to start search.
  @return file context */
  Clone_file_ctx *get_file_ctx(uint32_t chunk_num, uint32_t block_num,
                               uint32_t hint_index);

  /** Get next block of data to transfer
  @param[in]    chunk_num       current chunk
  @param[in,out]        block_num       current/next block
  @param[in,out]        file_ctx        current/next block file context
  @param[out]   data_offset     block offset in file
  @param[out]   data_buf        data buffer or NULL if transfer from file
  @param[out]   data_size       size of data in bytes
  @param[out]   file_size       updated file size if extended
  @return error code */
  int get_next_block(uint chunk_num, uint &block_num,
                     const Clone_file_ctx *&file_ctx, uint64_t &data_offset,
                     byte *&data_buf, uint32_t &data_size, uint64_t &file_size);

  /** Update snapshot block size based on caller's buffer size
  @param[in]    buff_size       buffer size for clone transfer */
  void update_block_size(uint buff_size);

  /** @return chunk size in bytes. */
  inline uint32_t get_chunk_size() const {
    return chunk_size() * UNIV_PAGE_SIZE;
  }

  /** @return number of blocks per chunk for different states. */
  uint32_t get_blocks_per_chunk() const;

  /** Check if copy snapshot
  @return true if snapshot is for copy */
  bool is_copy() const { return (m_snapshot_handle_type == CLONE_HDL_COPY); }

  /** Update file size when file is extended during page copy
  @param[in]    file_index      current file index
  @param[in]    file_size       new file size */
  void update_file_size(uint32_t file_index, uint64_t file_size);

  /** Encrypt tablespace key in header page with master key.
  @param[in]            page_size       page size descriptor
  @param[in,out]        page_data       page data to update
  @return true, if successful. */
  bool encrypt_key_in_header(const page_size_t &page_size, byte *page_data);

  /** Encrypt tablespace key in header page with master key.
  @param[in,out]        log_header      page data to update
  @param[in]            header_len      length of log header
  @return true, if successful. */
  bool encrypt_key_in_log_header(byte *log_header, uint32_t header_len);

  /** Decrypt tablespace key in header page with master key.
  @param[in]            file_meta       clone file metadata
  @param[in]            page_size       page size descriptor
  @param[in,out]        page_data       page data to update */
  void decrypt_key_in_header(const Clone_File_Meta *file_meta,
                             const page_size_t &page_size, byte *&page_data);

  /** @return maximum blocks to transfer with file pinned. */
  uint32_t get_max_blocks_pin() const;

  /** Skip all blocks belonging to currently deleted file context.
  @param[in]            chunk_num       current chunk
  @param[in,out]        block_num       current, next block */
  void skip_deleted_blocks(uint32_t chunk_num, uint32_t &block_num);

 private:
  /** Allow DDL file operation after 64 pages. */
  const static uint32_t S_MAX_PAGES_PIN = 64;

  /** Allow DDL file operation after every block (1M data by default) */
  const static uint32_t S_MAX_BLOCKS_PIN = 1;

  /** File name allocation size base. */
  const static size_t S_FILE_NAME_BASE_LEN = 256;

  /** Various wait types related to snapshot state. */
  enum class Wait_type {
    /* DDL- limited wait if clone is waiting for another DDL. */
    STATE_TRANSIT_WAIT,
    /* DDL- Wait till snapshot state transition is over. */
    STATE_TRANSIT,
    /* DDL- Wait till PAGE COPY state is over. */
    STATE_END_PAGE_COPY,
    /* Clone - Wait till there are no blockers for state transition. */
    STATE_BLOCKER,
    /*DDL - Wait till the waiting clone threads are active. This are
    clone threads from last DDL and useful to prevent starvation. */
    DATA_FILE_WAIT,
    /* DDL - Wait till all threads have closed active data files. */
    DATA_FILE_CLOSE,
    /* Clone - Wait till DDL file operation is complete. */
    DDL_FILE_OPERATION
  };

#ifdef UNIV_DEBUG
  /** Debug sync Wait during state transition. */
  void debug_wait_state_transit();
#endif /* UNIV_DEBUG */

  /** Update deleted state of a file if not yet done.
  @param[in,out]        file_ctx        file context
  @return true, if updated state */
  bool update_deleted_state(Clone_file_ctx *file_ctx);

  /** Get clone data file context by chunk number.
  @param[in]    chunk_num       chunk number
  @param[in]    hint_index      hint file index number to start search.
  @return file context */
  Clone_file_ctx *get_data_file_ctx(uint32_t chunk_num, uint32_t hint_index);

  /** Get clone page file context by chunk number and block number.
  @param[in]    chunk_num       chunk number
  @param[in]    block_num       block number
  @return file context */
  Clone_file_ctx *get_page_file_ctx(uint32_t chunk_num, uint32_t block_num);

  /** Get clone redo file context by chunk number.
  @param[in]    chunk_num       chunk number
  @param[in]    hint_index      hint file index number to start search.
  @return file context */
  Clone_file_ctx *get_redo_file_ctx(uint32_t chunk_num, uint32_t hint_index);

  /** Get wait information string based on wait type.
  @param[in]    wait_type       wait type
  @return wait information string. */
  const char *wait_string(Wait_type wait_type) const;

  /** Wait for various operations based on type.
  @param[in]    type            wait type
  @param[in]    ctx             file context when relevant
  @param[in]    no_wait         return with error if needs to wait
  @param[in]    check_intr      check for interrupt during wait
  @return mysql error code. */
  int wait(Wait_type type, const Clone_file_ctx *ctx, bool no_wait,
           bool check_intr);

  /** During wait get relevant message string for logging.
  @param[in]    wait_type       wait type
  @param[out]   info            notification to log while waiting
  @param[out]   error           error message to log on timeout */
  void get_wait_mesg(Wait_type wait_type, std::string &info,
                     std::string &error);

  /** Block clone state transition. Clone must wait.
  @param[in]    type            type of DDL notification
  @param[in]    space           space ID for the ddl operation
  @param[in]    no_wait         return with error if needs to wait
  @param[in]    check_intr      check for interrupt during wait`
  @param[out]   error           mysql error code
  @return true iff clone state change is blocked. */
  bool block_state_change(Clone_notify::Type type, space_id_t space,
                          bool no_wait, bool check_intr, int &error);

  /** Unblock clone state transition. */
  void unblock_state_change();

  /** Get next file state while being modified by ddl.
  @param[in]    type    ddl notification type
  @param[in]    begin   true, if DDL begin notification
                        false, if DDL end notification
  @return target file state. */
  Clone_file_ctx::State get_target_file_state(Clone_notify::Type type,
                                              bool begin);

  /** Handle files for DDL begin notification.
  @param[in]    type            type of DDL notification
  @param[in]    space           space ID for the ddl operation
  @param[in]    no_wait         return with error if needs to wait
  @param[in]    check_intr      check for interrupt during wait
  @return mysql error code */
  int begin_ddl_file(Clone_notify::Type type, space_id_t space, bool no_wait,
                     bool check_intr);

  /** Handle files for DDL end notification.
  @param[in]    type    type of DDL notification
  @param[in]    space   space ID for the ddl operation */
  void end_ddl_file(Clone_notify::Type type, space_id_t space);

  /** Synchronize snapshot with binary log and GTID.
  @param[in]    cbk     alert callback for long wait
  @return error code. */
  int synchronize_binlog_gtid(Clone_Alert_Func cbk);

  /** Make sure that the trx sys page binary log position correctly reflects
  all transactions committed to innodb. It updates binary log position
  in transaction sys page, if required. The caller must ensure that any new
  transaction is committed in order of binary log.
  @return error code. */
  int update_binlog_position();

  /** Wait for already prepared binlog transactions to end.
  @return error code. */
  int wait_for_binlog_prepared_trx();

  /** Wait for a transaction to end.
  @param[in]    thd     current THD
  @param[in]    trx_id  transaction to wait for
  @return error code. */
  int wait_trx_end(THD *thd, trx_id_t trx_id);

  /** Begin state transition before waiting for DDL. */
  void begin_transit_ddl_wait() {
    mutex_own(&m_snapshot_mutex);
    /* Update number of clones to transit to new state. Set this prior to
    waiting for DDLs blocking state transfer. This would help a new DDL to
    find if clone is blocked by other DDL before state transition. */
    m_num_clones_transit = m_num_clones;
  }

  /** Begin state transition.
  @param[in]    new_state       state to transit to */
  void begin_transit(Snapshot_State new_state) {
    mutex_own(&m_snapshot_mutex);
    m_snapshot_next_state = new_state;
    /* Move to next state. This is ok as the snapshot
    mutex is not released till transition is ended, This
    could change later when we ideally should release
    the snapshot mutex during transition. */
    m_snapshot_state = m_snapshot_next_state;
  }

  /** End state transition. */
  void end_transit() {
    mutex_own(&m_snapshot_mutex);
    m_num_clones_transit = 0;
    m_snapshot_next_state = CLONE_SNAPSHOT_NONE;
  }

  /** Check if state transition is in progress
  @return true during state transition */
  bool in_transit_state() const {
    mutex_own(&m_snapshot_mutex);
    return (m_snapshot_next_state != CLONE_SNAPSHOT_NONE);
  }

  /** @return true, if waiting before starting transition. Generally the
  case when some DDL blocks state transition. */
  bool in_transit_wait() const {
    mutex_own(&m_snapshot_mutex);
    return (!in_transit_state() && m_num_clones_transit != 0);
  }

  /** Start redo archiving.
  @return error code */
  int init_redo_archiving();

  /** Initialize snapshot state for file copy
  @param[in]    new_state       state to move for apply
  @return error code */
  int init_file_copy(Snapshot_State new_state);

  /** Initialize disk byte estimate. */
  void init_disk_estimate() {
    /* Initial size is set to the redo file size on disk. */
    IB_mutex_guard latch{&(log_sys->limits_mutex), UT_LOCATION_HERE};
    m_data_bytes_disk = log_sys->m_capacity.current_physical_capacity();
  }

  /** Initialize snapshot state for page copy
  @param[in]    new_state       state to move for apply
  @param[in]    page_buffer     temporary buffer to copy page IDs
  @param[in]    page_buffer_len buffer length
  @return error code */
  int init_page_copy(Snapshot_State new_state, byte *page_buffer,
                     uint page_buffer_len);

  /** Initialize snapshot state for redo copy
  @param[in]    new_state       state to move for apply
  @param[in]    cbk             alert callback for long wait
  @return error code */
  int init_redo_copy(Snapshot_State new_state, Clone_Alert_Func cbk);

  /** Initialize state while applying cloned data
  @param[in]    state_desc      snapshot state descriptor
  @return error code */
  int init_apply_state(Clone_Desc_State *state_desc);

  /** Extend and flush files after copying data
  @param[in]    flush_redo if true flush redo, otherwise data
  @return error code */
  int extend_and_flush_files(bool flush_redo);

  /** Create file descriptor and add to current file list
  @param[in]    data_dir        destination data directory
  @param[in]    file_meta       file metadata from donor
  @param[in]    is_ddl          if ddl temporary file
  @param[out]   file_ctx        file context
  @return error code */
  int create_desc(const char *data_dir, const Clone_File_Meta *file_meta,
                  bool is_ddl, Clone_file_ctx *&file_ctx);

  /** Get file context for current chunk
  @param[in]    file_vector     clone file vector
  @param[in]    chunk_num       current chunk number
  @param[in]    start_index     index for starting the search
  @return file context */
  Clone_file_ctx *get_file(Clone_File_Vec &file_vector, uint32_t chunk_num,
                           uint32_t start_index);

  /** Get next page from buffer pool
  @param[in]    chunk_num       current chunk
  @param[in,out]        block_num       current, next block
  @param[in,out]        file_ctx        current, next block file context
  @param[out]   data_offset     offset in file
  @param[out]   data_buf        page data
  @param[out]   data_size       page data size
  @param[out]   file_size       updated file size if extended
  @return error code */
  int get_next_page(uint chunk_num, uint &block_num,
                    const Clone_file_ctx *&file_ctx, uint64_t &data_offset,
                    byte *&data_buf, uint32_t &data_size, uint64_t &file_size);

  /** Get page from buffer pool and make ready for write
  @param[in]    page_id         page ID chunk
  @param[in]    page_size       page size descriptor
  @param[in]    file_ctx        clone file context
  @param[out]   page_data       data page
  @param[out]   data_size       page size in bytes
  @return error code */
  int get_page_for_write(const page_id_t &page_id, const page_size_t &page_size,
                         const Clone_file_ctx *file_ctx, byte *&page_data,
                         uint &data_size);

  /* Make page ready for flush by updating LSN anc checksum
  @param[in]            page_size       page size descriptor
  @param[in]            page_lsn        LSN to update the page with
  @param[in,out]        page_data       data page */
  void page_update_for_flush(const page_size_t &page_size, lsn_t page_lsn,
                             byte *&page_data);

  /** Build file metadata entry
  @param[in]    file_name       name of the file
  @param[in]    file_size       file size in bytes
  @param[in]    file_offset     start offset
  @param[in]    num_chunks      total number of chunks in the file
  @return file context */
  Clone_file_ctx *build_file(const char *file_name, uint64_t file_size,
                             uint64_t file_offset, uint &num_chunks);

  /** Allocate and set clone file name.
  @param[in,out]        file_meta       file metadata
  @param[in]            file_name       file name
  @return true iff successful. */
  bool build_file_name(Clone_File_Meta *file_meta, const char *file_name);

  /** Add buffer pool dump file to the file list
  @return error code */
  int add_buf_pool_file();

  /** Add file to snapshot
  @param[in]    name            file name
  @param[in]    size_bytes      file size in bytes
  @param[in]    alloc_bytes     allocation size on disk for sparse file
  @param[in]    node            file node
  @param[in]    by_ddl          node is added concurrently by DDL
  @return error code. */
  int add_file(const char *name, uint64_t size_bytes, uint64_t alloc_bytes,
               fil_node_t *node, bool by_ddl);

  /** Check if file context has been changed by ddl.
  @param[in]    node            tablespace file node
  @param[out]   file_ctx        file context if exists
  @return true iff file is created or modified by DDL. */
  bool file_ctx_changed(const fil_node_t *node, Clone_file_ctx *&file_ctx);

  /** Get chunk size
  @return chunk size in pages */
  inline uint32_t chunk_size() const {
    auto size = static_cast<uint32_t>(ut_2_exp(m_chunk_size_pow2));
    return size;
  }

  /** Get block size for file copy
  @return block size in pages */
  uint32_t block_size() {
    ut_a(m_block_size_pow2 <= SNAPSHOT_MAX_BLOCK_SIZE_POW2);
    auto size = static_cast<uint32_t>(ut_2_exp(m_block_size_pow2));

    return size;
  }

  /** Get number of blocks per chunk for file copy
  @return blocks per chunk */
  inline uint32_t blocks_per_chunk() const {
    ut_a(m_block_size_pow2 <= m_chunk_size_pow2);
    return (1 << (m_chunk_size_pow2 - m_block_size_pow2));
  }

  /** Update system file name from configuration.
  @param[in]            replace         if replacing current data directory
  @param[in]            file_meta       file descriptor
  @param[in,out]        file_name       file name to update
  @return error code */
  int update_sys_file_name(bool replace, const Clone_File_Meta *file_meta,
                           std::string &file_name);

  /** Build file name along with path for cloned data files.
  @param[in]    data_dir    clone data directory
  @param[in]    file_desc   file descriptor
  @param[out]   file_path   built file path if returned 0
  @return error code (0 on success) */
  int build_file_path(const char *data_dir, const Clone_File_Meta *file_desc,
                      std::string &file_path);

  /** Build file context from file path.
  @param[in]    extn            file extension type
  @param[in]    file_meta       file descriptor
  @param[in]    file_path       data file along with path
  @param[out]   file_ctx        created file context
  @return error code */
  int build_file_ctx(Clone_file_ctx::Extension extn,
                     const Clone_File_Meta *file_meta,
                     const std::string &file_path, Clone_file_ctx *&file_ctx);

  /** Check for existing file and if clone extension is needed. This function
  has the side effect to add undo file indexes.
  @param[in]    replace         if data directory is replaced
  @param[in]    undo_file       if undo tablespace file
  @param[in]    redo_file       if redo file
  @param[in]    data_file_index index of file
  @param[in]    data_file       data file name
  @param[out]   extn            file extension needs to be used
  @return error code */
  int handle_existing_file(bool replace, bool undo_file, bool redo_file,
                           uint32_t data_file_index,
                           const std::string &data_file,
                           Clone_file_ctx::Extension &extn);

  /** @return number of data files to transfer. */
  inline size_t num_data_files() const { return m_data_file_vector.size(); }

  /** @return number of redo files to transfer. */
  inline size_t num_redo_files() const { return m_redo_file_vector.size(); }

 private:
  /** @name Snapshot type and ID */

  /** Snapshot handle type */
  Clone_Handle_Type m_snapshot_handle_type;

  /** Clone type */
  Ha_clone_type m_snapshot_type;

  /** Unique snapshot ID */
  uint64_t m_snapshot_id;

  /** Index in global snapshot array */
  uint m_snapshot_arr_idx;

  /** @name Snapshot State  */

  /** Mutex to handle access by concurrent clones */
  mutable ib_mutex_t m_snapshot_mutex;

  /** Number of blockers for state change. Usually DDLs for short duration. */
  uint32_t m_num_blockers;

  /** Set to true only if clone is aborted after error. */
  bool m_aborted;

  /** Number of clones attached to this snapshot */
  uint m_num_clones;

  /** Number of clones in in state transition */
  uint m_num_clones_transit;

  /** Current state */
  Snapshot_State m_snapshot_state;

  /** Next state to move to. Set only during state transfer. */
  Snapshot_State m_snapshot_next_state;

  /** @name Snapshot data block */

  /** Memory allocation heap */
  mem_heap_t *m_snapshot_heap;

  /** Chunk size in power of 2 */
  uint m_chunk_size_pow2;

  /** Block size in power of 2 */
  uint m_block_size_pow2;

  /** Number of chunks in current state */
  uint m_num_current_chunks;

  /** Maximum file name length observed till now. */
  size_t m_max_file_name_len;

  /** @name Snapshot file data */

  /** All data files for transfer */
  Clone_File_Vec m_data_file_vector;

  /** Map space ID to file vector index */
  Clone_File_Map m_data_file_map;

  /** Total number of data chunks */
  uint m_num_data_chunks;

  /** Number of bytes on disk. */
  uint64_t m_data_bytes_disk;

  /** Index into m_data_file_vector for all undo files. */
  std::vector<int> m_undo_file_indexes;

  /** @name Snapshot page data */

  /** Page archiver client */
  Page_Arch_Client_Ctx m_page_ctx;

  /** Set of unique page IDs */
  Clone_Page_Set m_page_set;

  /** Sorted page IDs to transfer */
  Clone_Page_Vec m_page_vector;

  /** Number of pages to transfer */
  uint m_num_pages;

  /** Number of duplicate pages found */
  uint m_num_duplicate_pages;

  /** @name Snapshot redo data */

  /** redo log archiver client */
  Log_Arch_Client_Ctx m_redo_ctx;

  /** All archived redo files to transfer */
  Clone_File_Vec m_redo_file_vector;

  /** Start offset in first redo file */
  uint64_t m_redo_start_offset;

  /** Redo header block */
  byte *m_redo_header;

  /** Redo header size */
  uint m_redo_header_size;

  /** Redo trailer block */
  byte *m_redo_trailer;

  /** Redo trailer size */
  uint m_redo_trailer_size;

  /** Redo trailer block offset */
  uint64_t m_redo_trailer_offset;

  /** Archived redo file size */
  uint64_t m_redo_file_size;

  /** Total number of redo data chunks */
  uint m_num_redo_chunks;

  /** Enable PFS monitoring */
  bool m_enable_pfs;

  /** Performance Schema accounting object to monitor stage progress */
  Clone_Monitor m_monitor;
};

#endif /* CLONE_SNAPSHOT_INCLUDE */