@@ -58,7 +58,7 @@ static ArchiveHandle *_allocAH(const char *FileSpec, const ArchiveFormat fmt,
58
58
SetupWorkerPtrType setupWorkerPtr );
59
59
static void _getObjectDescription (PQExpBuffer buf , TocEntry * te ,
60
60
ArchiveHandle * AH );
61
- static void _printTocEntry (ArchiveHandle * AH , TocEntry * te , bool isData , bool acl_pass );
61
+ static void _printTocEntry (ArchiveHandle * AH , TocEntry * te , bool isData );
62
62
static char * replace_line_endings (const char * str );
63
63
static void _doSetFixedOutputState (ArchiveHandle * AH );
64
64
static void _doSetSessionAuth (ArchiveHandle * AH , const char * user );
@@ -71,6 +71,7 @@ static void _selectTablespace(ArchiveHandle *AH, const char *tablespace);
71
71
static void processEncodingEntry (ArchiveHandle * AH , TocEntry * te );
72
72
static void processStdStringsEntry (ArchiveHandle * AH , TocEntry * te );
73
73
static teReqs _tocEntryRequired (TocEntry * te , teSection curSection , RestoreOptions * ropt );
74
+ static RestorePass _tocEntryRestorePass (TocEntry * te );
74
75
static bool _tocEntryIsACL (TocEntry * te );
75
76
static void _disableTriggersIfNecessary (ArchiveHandle * AH , TocEntry * te );
76
77
static void _enableTriggersIfNecessary (ArchiveHandle * AH , TocEntry * te );
@@ -86,13 +87,18 @@ static OutputContext SaveOutput(ArchiveHandle *AH);
86
87
static void RestoreOutput (ArchiveHandle * AH , OutputContext savedContext );
87
88
88
89
static int restore_toc_entry (ArchiveHandle * AH , TocEntry * te , bool is_parallel );
89
- static void restore_toc_entries_prefork (ArchiveHandle * AH );
90
- static void restore_toc_entries_parallel (ArchiveHandle * AH , ParallelState * pstate ,
90
+ static void restore_toc_entries_prefork (ArchiveHandle * AH ,
91
+ TocEntry * pending_list );
92
+ static void restore_toc_entries_parallel (ArchiveHandle * AH ,
93
+ ParallelState * pstate ,
94
+ TocEntry * pending_list );
95
+ static void restore_toc_entries_postfork (ArchiveHandle * AH ,
91
96
TocEntry * pending_list );
92
- static void restore_toc_entries_postfork (ArchiveHandle * AH , TocEntry * pending_list );
93
97
static void par_list_header_init (TocEntry * l );
94
98
static void par_list_append (TocEntry * l , TocEntry * te );
95
99
static void par_list_remove (TocEntry * te );
100
+ static void move_to_ready_list (TocEntry * pending_list , TocEntry * ready_list ,
101
+ RestorePass pass );
96
102
static TocEntry * get_next_work_item (ArchiveHandle * AH ,
97
103
TocEntry * ready_list ,
98
104
ParallelState * pstate );
@@ -625,20 +631,18 @@ RestoreArchive(Archive *AHX)
625
631
AH -> currSchema = NULL ;
626
632
}
627
633
628
- /*
629
- * In serial mode, we now process each non-ACL TOC entry.
630
- *
631
- * In parallel mode, turn control over to the parallel-restore logic.
632
- */
633
634
if (parallel_mode )
634
635
{
636
+ /*
637
+ * In parallel mode, turn control over to the parallel-restore logic.
638
+ */
635
639
ParallelState * pstate ;
636
640
TocEntry pending_list ;
637
641
638
642
par_list_header_init (& pending_list );
639
643
640
644
/* This runs PRE_DATA items and then disconnects from the database */
641
- restore_toc_entries_prefork (AH );
645
+ restore_toc_entries_prefork (AH , & pending_list );
642
646
Assert (AH -> connection == NULL );
643
647
644
648
/* ParallelBackupStart() will actually fork the processes */
@@ -652,28 +656,51 @@ RestoreArchive(Archive *AHX)
652
656
}
653
657
else
654
658
{
659
+ /*
660
+ * In serial mode, process everything in three phases: normal items,
661
+ * then ACLs, then matview refresh items. We might be able to skip
662
+ * one or both extra phases in some cases, eg data-only restores.
663
+ */
664
+ bool haveACL = false;
665
+ bool haveRefresh = false;
666
+
655
667
for (te = AH -> toc -> next ; te != AH -> toc ; te = te -> next )
656
- (void ) restore_toc_entry (AH , te , false);
657
- }
668
+ {
669
+ if ((te -> reqs & (REQ_SCHEMA | REQ_DATA )) == 0 )
670
+ continue ; /* ignore if not to be dumped at all */
658
671
659
- /*
660
- * Scan TOC again to output ownership commands and ACLs
661
- */
662
- for (te = AH -> toc -> next ; te != AH -> toc ; te = te -> next )
663
- {
664
- AH -> currentTE = te ;
672
+ switch (_tocEntryRestorePass (te ))
673
+ {
674
+ case RESTORE_PASS_MAIN :
675
+ (void ) restore_toc_entry (AH , te , false);
676
+ break ;
677
+ case RESTORE_PASS_ACL :
678
+ haveACL = true;
679
+ break ;
680
+ case RESTORE_PASS_REFRESH :
681
+ haveRefresh = true;
682
+ break ;
683
+ }
684
+ }
665
685
666
- /* Both schema and data objects might now have ownership/ACLs */
667
- if ((te -> reqs & (REQ_SCHEMA | REQ_DATA )) != 0 )
686
+ if (haveACL )
668
687
{
669
- /* Show namespace if available */
670
- if (te -> namespace )
671
- ahlog (AH , 1 , "setting owner and privileges for %s \"%s.%s\"\n" ,
672
- te -> desc , te -> namespace , te -> tag );
673
- else
674
- ahlog (AH , 1 , "setting owner and privileges for %s \"%s\"\n" ,
675
- te -> desc , te -> tag );
676
- _printTocEntry (AH , te , false, true);
688
+ for (te = AH -> toc -> next ; te != AH -> toc ; te = te -> next )
689
+ {
690
+ if ((te -> reqs & (REQ_SCHEMA | REQ_DATA )) != 0 &&
691
+ _tocEntryRestorePass (te ) == RESTORE_PASS_ACL )
692
+ (void ) restore_toc_entry (AH , te , false);
693
+ }
694
+ }
695
+
696
+ if (haveRefresh )
697
+ {
698
+ for (te = AH -> toc -> next ; te != AH -> toc ; te = te -> next )
699
+ {
700
+ if ((te -> reqs & (REQ_SCHEMA | REQ_DATA )) != 0 &&
701
+ _tocEntryRestorePass (te ) == RESTORE_PASS_REFRESH )
702
+ (void ) restore_toc_entry (AH , te , false);
703
+ }
677
704
}
678
705
}
679
706
@@ -720,10 +747,7 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te, bool is_parallel)
720
747
AH -> currentTE = te ;
721
748
722
749
/* Work out what, if anything, we want from this entry */
723
- if (_tocEntryIsACL (te ))
724
- reqs = 0 ; /* ACLs are never restored here */
725
- else
726
- reqs = te -> reqs ;
750
+ reqs = te -> reqs ;
727
751
728
752
/*
729
753
* Ignore DATABASE entry unless we should create it. We must check this
@@ -744,17 +768,19 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te, bool is_parallel)
744
768
745
769
defnDumped = false;
746
770
747
- if ((reqs & REQ_SCHEMA ) != 0 ) /* We want the schema */
771
+ /*
772
+ * If it has a schema component that we want, then process that
773
+ */
774
+ if ((reqs & REQ_SCHEMA ) != 0 )
748
775
{
749
- /* Show namespace if available */
776
+ /* Show namespace in log message if available */
750
777
if (te -> namespace )
751
778
ahlog (AH , 1 , "creating %s \"%s.%s\"\n" ,
752
779
te -> desc , te -> namespace , te -> tag );
753
780
else
754
781
ahlog (AH , 1 , "creating %s \"%s\"\n" , te -> desc , te -> tag );
755
782
756
-
757
- _printTocEntry (AH , te , false, false);
783
+ _printTocEntry (AH , te , false);
758
784
defnDumped = true;
759
785
760
786
if (strcmp (te -> desc , "TABLE" ) == 0 )
@@ -810,7 +836,7 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te, bool is_parallel)
810
836
}
811
837
812
838
/*
813
- * If we have a data component, then process it
839
+ * If it has a data component that we want , then process that
814
840
*/
815
841
if ((reqs & REQ_DATA ) != 0 )
816
842
{
@@ -826,7 +852,7 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te, bool is_parallel)
826
852
*/
827
853
if (AH -> PrintTocDataPtr != NULL )
828
854
{
829
- _printTocEntry (AH , te , true, false );
855
+ _printTocEntry (AH , te , true);
830
856
831
857
if (strcmp (te -> desc , "BLOBS" ) == 0 ||
832
858
strcmp (te -> desc , "BLOB COMMENTS" ) == 0 )
@@ -914,7 +940,7 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te, bool is_parallel)
914
940
{
915
941
/* If we haven't already dumped the defn part, do so now */
916
942
ahlog (AH , 1 , "executing %s %s\n" , te -> desc , te -> tag );
917
- _printTocEntry (AH , te , false, false );
943
+ _printTocEntry (AH , te , false);
918
944
}
919
945
}
920
946
@@ -2943,8 +2969,30 @@ _tocEntryRequired(TocEntry *te, teSection curSection, RestoreOptions *ropt)
2943
2969
return res ;
2944
2970
}
2945
2971
2972
+ /*
2973
+ * Identify which pass we should restore this TOC entry in.
2974
+ *
2975
+ * See notes with the RestorePass typedef in pg_backup_archiver.h.
2976
+ */
2977
+ static RestorePass
2978
+ _tocEntryRestorePass (TocEntry * te )
2979
+ {
2980
+ /* "ACL LANGUAGE" was a crock emitted only in PG 7.4 */
2981
+ if (strcmp (te -> desc , "ACL" ) == 0 ||
2982
+ strcmp (te -> desc , "ACL LANGUAGE" ) == 0 ||
2983
+ strcmp (te -> desc , "DEFAULT ACL" ) == 0 )
2984
+ return RESTORE_PASS_ACL ;
2985
+ if (strcmp (te -> desc , "MATERIALIZED VIEW DATA" ) == 0 )
2986
+ return RESTORE_PASS_REFRESH ;
2987
+ return RESTORE_PASS_MAIN ;
2988
+ }
2989
+
2946
2990
/*
2947
2991
* Identify TOC entries that are ACLs.
2992
+ *
2993
+ * Note: it seems worth duplicating some code here to avoid a hard-wired
2994
+ * assumption that these are exactly the same entries that we restore during
2995
+ * the RESTORE_PASS_ACL phase.
2948
2996
*/
2949
2997
static bool
2950
2998
_tocEntryIsACL (TocEntry * te )
@@ -3364,23 +3412,18 @@ _getObjectDescription(PQExpBuffer buf, TocEntry *te, ArchiveHandle *AH)
3364
3412
type );
3365
3413
}
3366
3414
3415
+ /*
3416
+ * Emit the SQL commands to create the object represented by a TOC entry
3417
+ *
3418
+ * This now also includes issuing an ALTER OWNER command to restore the
3419
+ * object's ownership, if wanted. But note that the object's permissions
3420
+ * will remain at default, until the matching ACL TOC entry is restored.
3421
+ */
3367
3422
static void
3368
- _printTocEntry (ArchiveHandle * AH , TocEntry * te , bool isData , bool acl_pass )
3423
+ _printTocEntry (ArchiveHandle * AH , TocEntry * te , bool isData )
3369
3424
{
3370
3425
RestoreOptions * ropt = AH -> public .ropt ;
3371
3426
3372
- /* ACLs are dumped only during acl pass */
3373
- if (acl_pass )
3374
- {
3375
- if (!_tocEntryIsACL (te ))
3376
- return ;
3377
- }
3378
- else
3379
- {
3380
- if (_tocEntryIsACL (te ))
3381
- return ;
3382
- }
3383
-
3384
3427
/*
3385
3428
* Avoid dumping the public schema, as it will already be created ...
3386
3429
* unless we are using --clean mode (and *not* --create mode), in which
@@ -3567,7 +3610,7 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, bool isData, bool acl_pass)
3567
3610
* If it's an ACL entry, it might contain SET SESSION AUTHORIZATION
3568
3611
* commands, so we can no longer assume we know the current auth setting.
3569
3612
*/
3570
- if (acl_pass )
3613
+ if (_tocEntryIsACL ( te ) )
3571
3614
{
3572
3615
if (AH -> currUser )
3573
3616
free (AH -> currUser );
@@ -3597,6 +3640,9 @@ replace_line_endings(const char *str)
3597
3640
return result ;
3598
3641
}
3599
3642
3643
+ /*
3644
+ * Write the file header for a custom-format archive
3645
+ */
3600
3646
void
3601
3647
WriteHead (ArchiveHandle * AH )
3602
3648
{
@@ -3772,16 +3818,14 @@ dumpTimestamp(ArchiveHandle *AH, const char *msg, time_t tim)
3772
3818
/*
3773
3819
* Main engine for parallel restore.
3774
3820
*
3775
- * Work is done in three phases.
3776
- * First we process all SECTION_PRE_DATA tocEntries, in a single connection,
3777
- * just as for a standard restore. Second we process the remaining non-ACL
3778
- * steps in parallel worker children (threads on Windows, processes on Unix),
3779
- * each of which connects separately to the database. Finally we process all
3780
- * the ACL entries in a single connection (that happens back in
3781
- * RestoreArchive).
3821
+ * Parallel restore is done in three phases. In this first phase,
3822
+ * we'll process all SECTION_PRE_DATA TOC entries that are allowed to be
3823
+ * processed in the RESTORE_PASS_MAIN pass. (In practice, that's all
3824
+ * PRE_DATA items other than ACLs.) Entries we can't process now are
3825
+ * added to the pending_list for later phases to deal with.
3782
3826
*/
3783
3827
static void
3784
- restore_toc_entries_prefork (ArchiveHandle * AH )
3828
+ restore_toc_entries_prefork (ArchiveHandle * AH , TocEntry * pending_list )
3785
3829
{
3786
3830
bool skipped_some ;
3787
3831
TocEntry * next_work_item ;
@@ -3799,23 +3843,31 @@ restore_toc_entries_prefork(ArchiveHandle *AH)
3799
3843
* about showing all the dependencies of SECTION_PRE_DATA items, so we do
3800
3844
* not risk trying to process them out-of-order.
3801
3845
*
3846
+ * Stuff that we can't do immediately gets added to the pending_list.
3847
+ * Note: we don't yet filter out entries that aren't going to be restored.
3848
+ * They might participate in dependency chains connecting entries that
3849
+ * should be restored, so we treat them as live until we actually process
3850
+ * them.
3851
+ *
3802
3852
* Note: as of 9.2, it should be guaranteed that all PRE_DATA items appear
3803
3853
* before DATA items, and all DATA items before POST_DATA items. That is
3804
3854
* not certain to be true in older archives, though, so this loop is coded
3805
3855
* to not assume it.
3806
3856
*/
3857
+ AH -> restorePass = RESTORE_PASS_MAIN ;
3807
3858
skipped_some = false;
3808
3859
for (next_work_item = AH -> toc -> next ; next_work_item != AH -> toc ; next_work_item = next_work_item -> next )
3809
3860
{
3810
- /* NB: process-or-continue logic must be the inverse of loop below */
3861
+ bool do_now = true;
3862
+
3811
3863
if (next_work_item -> section != SECTION_PRE_DATA )
3812
3864
{
3813
3865
/* DATA and POST_DATA items are just ignored for now */
3814
3866
if (next_work_item -> section == SECTION_DATA ||
3815
3867
next_work_item -> section == SECTION_POST_DATA )
3816
3868
{
3869
+ do_now = false;
3817
3870
skipped_some = true;
3818
- continue ;
3819
3871
}
3820
3872
else
3821
3873
{
@@ -3826,18 +3878,35 @@ restore_toc_entries_prefork(ArchiveHandle *AH)
3826
3878
* comment's dependencies are satisfied, so skip it for now.
3827
3879
*/
3828
3880
if (skipped_some )
3829
- continue ;
3881
+ do_now = false ;
3830
3882
}
3831
3883
}
3832
3884
3833
- ahlog (AH , 1 , "processing item %d %s %s\n" ,
3834
- next_work_item -> dumpId ,
3835
- next_work_item -> desc , next_work_item -> tag );
3885
+ /*
3886
+ * Also skip items that need to be forced into later passes. We need
3887
+ * not set skipped_some in this case, since by assumption no main-pass
3888
+ * items could depend on these.
3889
+ */
3890
+ if (_tocEntryRestorePass (next_work_item ) != RESTORE_PASS_MAIN )
3891
+ do_now = false;
3892
+
3893
+ if (do_now )
3894
+ {
3895
+ /* OK, restore the item and update its dependencies */
3896
+ ahlog (AH , 1 , "processing item %d %s %s\n" ,
3897
+ next_work_item -> dumpId ,
3898
+ next_work_item -> desc , next_work_item -> tag );
3836
3899
3837
- (void ) restore_toc_entry (AH , next_work_item , false);
3900
+ (void ) restore_toc_entry (AH , next_work_item , false);
3838
3901
3839
- /* there should be no touch of ready_list here, so pass NULL */
3840
- reduce_dependencies (AH , next_work_item , NULL );
3902
+ /* there should be no touch of ready_list here, so pass NULL */
3903
+ reduce_dependencies (AH , next_work_item , NULL );
3904
+ }
3905
+ else
3906
+ {
3907
+ /* Nope, so add it to pending_list */
3908
+ par_list_append (pending_list , next_work_item );
3909
+ }
3841
3910
}
3842
3911
3843
3912
/*
@@ -3863,104 +3932,95 @@ restore_toc_entries_prefork(ArchiveHandle *AH)
3863
3932
/*
3864
3933
* Main engine for parallel restore.
3865
3934
*
3866
- * Work is done in three phases.
3867
- * First we process all SECTION_PRE_DATA tocEntries, in a single connection,
3868
- * just as for a standard restore. This is done in restore_toc_entries_prefork().
3869
- * Second we process the remaining non-ACL steps in parallel worker children
3870
- * (threads on Windows, processes on Unix), these fork off and set up their
3871
- * connections before we call restore_toc_entries_parallel_forked.
3872
- * Finally we process all the ACL entries in a single connection (that happens
3873
- * back in RestoreArchive).
3935
+ * Parallel restore is done in three phases. In this second phase,
3936
+ * we process entries by dispatching them to parallel worker children
3937
+ * (processes on Unix, threads on Windows), each of which connects
3938
+ * separately to the database. Inter-entry dependencies are respected,
3939
+ * and so is the RestorePass multi-pass structure. When we can no longer
3940
+ * make any entries ready to process, we exit. Normally, there will be
3941
+ * nothing left to do; but if there is, the third phase will mop up.
3874
3942
*/
3875
3943
static void
3876
3944
restore_toc_entries_parallel (ArchiveHandle * AH , ParallelState * pstate ,
3877
3945
TocEntry * pending_list )
3878
3946
{
3879
- bool skipped_some ;
3880
3947
TocEntry ready_list ;
3881
3948
TocEntry * next_work_item ;
3882
3949
3883
3950
ahlog (AH , 2 , "entering restore_toc_entries_parallel\n" );
3884
3951
3885
3952
/*
3886
- * Initialize the lists of ready items, the list for pending items has
3887
- * already been initialized in the caller. After this setup, the pending
3888
- * list is everything that needs to be done but is blocked by one or more
3889
- * dependencies, while the ready list contains items that have no
3890
- * remaining dependencies. Note: we don't yet filter out entries that
3891
- * aren't going to be restored. They might participate in dependency
3892
- * chains connecting entries that should be restored, so we treat them as
3893
- * live until we actually process them.
3953
+ * The pending_list contains all items that we need to restore. Move all
3954
+ * items that are available to process immediately into the ready_list.
3955
+ * After this setup, the pending list is everything that needs to be done
3956
+ * but is blocked by one or more dependencies, while the ready list
3957
+ * contains items that have no remaining dependencies and are OK to
3958
+ * process in the current restore pass.
3894
3959
*/
3895
3960
par_list_header_init (& ready_list );
3896
- skipped_some = false;
3897
- for (next_work_item = AH -> toc -> next ; next_work_item != AH -> toc ; next_work_item = next_work_item -> next )
3898
- {
3899
- /* NB: process-or-continue logic must be the inverse of loop above */
3900
- if (next_work_item -> section == SECTION_PRE_DATA )
3901
- {
3902
- /* All PRE_DATA items were dealt with above */
3903
- continue ;
3904
- }
3905
- if (next_work_item -> section == SECTION_DATA ||
3906
- next_work_item -> section == SECTION_POST_DATA )
3907
- {
3908
- /* set this flag at same point that previous loop did */
3909
- skipped_some = true;
3910
- }
3911
- else
3912
- {
3913
- /* SECTION_NONE items must be processed if previous loop didn't */
3914
- if (!skipped_some )
3915
- continue ;
3916
- }
3917
-
3918
- if (next_work_item -> depCount > 0 )
3919
- par_list_append (pending_list , next_work_item );
3920
- else
3921
- par_list_append (& ready_list , next_work_item );
3922
- }
3961
+ AH -> restorePass = RESTORE_PASS_MAIN ;
3962
+ move_to_ready_list (pending_list , & ready_list , AH -> restorePass );
3923
3963
3924
3964
/*
3925
3965
* main parent loop
3926
3966
*
3927
3967
* Keep going until there is no worker still running AND there is no work
3928
- * left to be done.
3968
+ * left to be done. Note invariant: at top of loop, there should always
3969
+ * be at least one worker available to dispatch a job to.
3929
3970
*/
3930
-
3931
3971
ahlog (AH , 1 , "entering main parallel loop\n" );
3932
3972
3933
- while ((next_work_item = get_next_work_item (AH , & ready_list , pstate )) != NULL ||
3934
- !IsEveryWorkerIdle (pstate ))
3973
+ for (;;)
3935
3974
{
3975
+ /* Look for an item ready to be dispatched to a worker */
3976
+ next_work_item = get_next_work_item (AH , & ready_list , pstate );
3936
3977
if (next_work_item != NULL )
3937
3978
{
3938
3979
/* If not to be restored, don't waste time launching a worker */
3939
- if ((next_work_item -> reqs & (REQ_SCHEMA | REQ_DATA )) == 0 ||
3940
- _tocEntryIsACL (next_work_item ))
3980
+ if ((next_work_item -> reqs & (REQ_SCHEMA | REQ_DATA )) == 0 )
3941
3981
{
3942
3982
ahlog (AH , 1 , "skipping item %d %s %s\n" ,
3943
3983
next_work_item -> dumpId ,
3944
3984
next_work_item -> desc , next_work_item -> tag );
3945
-
3985
+ /* Drop it from ready_list, and update its dependencies */
3946
3986
par_list_remove (next_work_item );
3947
3987
reduce_dependencies (AH , next_work_item , & ready_list );
3948
-
3988
+ /* Loop around to see if anything else can be dispatched */
3949
3989
continue ;
3950
3990
}
3951
3991
3952
3992
ahlog (AH , 1 , "launching item %d %s %s\n" ,
3953
3993
next_work_item -> dumpId ,
3954
3994
next_work_item -> desc , next_work_item -> tag );
3955
3995
3996
+ /* Remove it from ready_list, and dispatch to some worker */
3956
3997
par_list_remove (next_work_item );
3957
3998
3958
3999
DispatchJobForTocEntry (AH , pstate , next_work_item , ACT_RESTORE ,
3959
4000
mark_restore_job_done , & ready_list );
3960
4001
}
4002
+ else if (IsEveryWorkerIdle (pstate ))
4003
+ {
4004
+ /*
4005
+ * Nothing is ready and no worker is running, so we're done with
4006
+ * the current pass or maybe with the whole process.
4007
+ */
4008
+ if (AH -> restorePass == RESTORE_PASS_LAST )
4009
+ break ; /* No more parallel processing is possible */
4010
+
4011
+ /* Advance to next restore pass */
4012
+ AH -> restorePass ++ ;
4013
+ /* That probably allows some stuff to be made ready */
4014
+ move_to_ready_list (pending_list , & ready_list , AH -> restorePass );
4015
+ /* Loop around to see if anything's now ready */
4016
+ continue ;
4017
+ }
3961
4018
else
3962
4019
{
3963
- /* at least one child is working and we have nothing ready. */
4020
+ /*
4021
+ * We have nothing ready, but at least one child is working, so
4022
+ * wait for some subjob to finish.
4023
+ */
3964
4024
}
3965
4025
3966
4026
/*
@@ -3980,9 +4040,21 @@ restore_toc_entries_parallel(ArchiveHandle *AH, ParallelState *pstate,
3980
4040
next_work_item ? WFW_ONE_IDLE : WFW_GOT_STATUS );
3981
4041
}
3982
4042
4043
+ /* There should now be nothing in ready_list. */
4044
+ Assert (ready_list .par_next == & ready_list );
4045
+
3983
4046
ahlog (AH , 1 , "finished main parallel loop\n" );
3984
4047
}
3985
4048
4049
+ /*
4050
+ * Main engine for parallel restore.
4051
+ *
4052
+ * Parallel restore is done in three phases. In this third phase,
4053
+ * we mop up any remaining TOC entries by processing them serially.
4054
+ * This phase normally should have nothing to do, but if we've somehow
4055
+ * gotten stuck due to circular dependencies or some such, this provides
4056
+ * at least some chance of completing the restore successfully.
4057
+ */
3986
4058
static void
3987
4059
restore_toc_entries_postfork (ArchiveHandle * AH , TocEntry * pending_list )
3988
4060
{
@@ -4002,18 +4074,17 @@ restore_toc_entries_postfork(ArchiveHandle *AH, TocEntry *pending_list)
4002
4074
_doSetFixedOutputState (AH );
4003
4075
4004
4076
/*
4005
- * Make sure there is no non-ACL work left due to, say, circular
4006
- * dependencies, or some other pathological condition. If so, do it in the
4007
- * single parent connection.
4077
+ * Make sure there is no work left due to, say, circular dependencies, or
4078
+ * some other pathological condition. If so, do it in the single parent
4079
+ * connection. We don't sweat about RestorePass ordering; it's likely we
4080
+ * already violated that.
4008
4081
*/
4009
4082
for (te = pending_list -> par_next ; te != pending_list ; te = te -> par_next )
4010
4083
{
4011
4084
ahlog (AH , 1 , "processing missed item %d %s %s\n" ,
4012
4085
te -> dumpId , te -> desc , te -> tag );
4013
4086
(void ) restore_toc_entry (AH , te , false);
4014
4087
}
4015
-
4016
- /* The ACLs will be handled back in RestoreArchive. */
4017
4088
}
4018
4089
4019
4090
/*
@@ -4072,6 +4143,36 @@ par_list_remove(TocEntry *te)
4072
4143
}
4073
4144
4074
4145
4146
+ /*
4147
+ * Move all immediately-ready items from pending_list to ready_list.
4148
+ *
4149
+ * Items are considered ready if they have no remaining dependencies and
4150
+ * they belong in the current restore pass. (See also reduce_dependencies,
4151
+ * which applies the same logic one-at-a-time.)
4152
+ */
4153
+ static void
4154
+ move_to_ready_list (TocEntry * pending_list , TocEntry * ready_list ,
4155
+ RestorePass pass )
4156
+ {
4157
+ TocEntry * te ;
4158
+ TocEntry * next_te ;
4159
+
4160
+ for (te = pending_list -> par_next ; te != pending_list ; te = next_te )
4161
+ {
4162
+ /* must save list link before possibly moving te to other list */
4163
+ next_te = te -> par_next ;
4164
+
4165
+ if (te -> depCount == 0 &&
4166
+ _tocEntryRestorePass (te ) == pass )
4167
+ {
4168
+ /* Remove it from pending_list ... */
4169
+ par_list_remove (te );
4170
+ /* ... and add to ready_list */
4171
+ par_list_append (ready_list , te );
4172
+ }
4173
+ }
4174
+ }
4175
+
4075
4176
/*
4076
4177
* Find the next work item (if any) that is capable of being run now.
4077
4178
*
@@ -4457,8 +4558,17 @@ reduce_dependencies(ArchiveHandle *AH, TocEntry *te, TocEntry *ready_list)
4457
4558
{
4458
4559
TocEntry * otherte = AH -> tocsByDumpId [te -> revDeps [i ]];
4459
4560
4561
+ Assert (otherte -> depCount > 0 );
4460
4562
otherte -> depCount -- ;
4461
- if (otherte -> depCount == 0 && otherte -> par_prev != NULL )
4563
+
4564
+ /*
4565
+ * It's ready if it has no remaining dependencies and it belongs in
4566
+ * the current restore pass. However, don't move it if it has not yet
4567
+ * been put into the pending list.
4568
+ */
4569
+ if (otherte -> depCount == 0 &&
4570
+ _tocEntryRestorePass (otherte ) == AH -> restorePass &&
4571
+ otherte -> par_prev != NULL )
4462
4572
{
4463
4573
/* It must be in the pending list, so remove it ... */
4464
4574
par_list_remove (otherte );
0 commit comments