@@ -74,11 +74,11 @@ type archivePushWatchHooks struct {
7474 pgStartupSync func (
7575 context.Context , * syncpkg.Engine , bool ,
7676 ) (bool , error )
77- newPGPusher func (* syncpkg.Engine ) * pgPusher
78- newDuckDBPusher func (* syncpkg.Engine ) * duckDBPusher
79- duckDBStartupSync func (
77+ duckDBStartupSync func (
8078 context.Context , * syncpkg.Engine , bool ,
8179 ) (bool , error )
80+ newPGPusher func (* syncpkg.Engine ) * pgPusher
81+ newDuckDBPusher func (* syncpkg.Engine ) * duckDBPusher
8282 newUnwatchedPoller func (context.Context , unwatchedPollSyncer ) unwatchedRootPoller
8383}
8484
@@ -138,6 +138,49 @@ func newArchivePushLoop(
138138 return loop , ticker .Stop
139139}
140140
141+ func archivePushWatchWatcherOptions (
142+ loop * pushLoop , poller unwatchedRootPoller ,
143+ ) syncpkg.WatcherOptions {
144+ return syncpkg.WatcherOptions {
145+ OnCoverageDegraded : func (roots []string ) error {
146+ // Degraded coverage needs both owners: the poller reconciles
147+ // the affected roots authoritatively (including tombstoning
148+ // missed deletions) and the loop re-pushes the refreshed
149+ // archive on its floor.
150+ if err := poller .AddObligation (pollingObligation {
151+ Key : "watcher-fallback" , Roots : roots ,
152+ }); err != nil {
153+ return err
154+ }
155+ return loop .NotifyCoverageDegraded (roots )
156+ },
157+ OnPollingRequired : func (obligation syncpkg.PollingObligation ) error {
158+ return poller .AddObligation (pollingObligation {
159+ Key : obligation .Key ,
160+ Roots : obligation .Roots ,
161+ Probe : obligation .Probe ,
162+ })
163+ },
164+ OnPollingReleased : poller .RemoveObligation ,
165+ }
166+ }
167+
168+ func archivePushWatchBatchCallback (
169+ appCfg config.Config ,
170+ engine * syncpkg.Engine ,
171+ loop * pushLoop ,
172+ ) syncpkg.WatchCallback {
173+ return func (callbackCtx context.Context , batch syncpkg.WatchBatch ) error {
174+ scope := func () watchRecoveryScope {
175+ return probeWatchRecoveryScope (appCfg )
176+ }
177+ if err := syncWatchBatch (callbackCtx , engine , batch , scope ); err != nil {
178+ return err
179+ }
180+ return notifyPushForWatchBatch (callbackCtx , loop , batch )
181+ }
182+ }
183+
141184func completeDuckDBWatchPush (
142185 res duckdbsync.PushResult , reason pushReason ,
143186) error {
@@ -691,6 +734,22 @@ func (b *localArchiveWriteBackend) duckDBPush(
691734 forceFull := cfg .Full || didResync
692735
693736 fmt .Println ("Starting DuckDB push..." )
737+ return b .duckDBMirrorPush (
738+ ctx , duckCfg , cfg , projects , excludeProjects , forceFull ,
739+ )
740+ }
741+
742+ func (b * localArchiveWriteBackend ) duckDBMirrorPush (
743+ ctx context.Context ,
744+ duckCfg config.DuckDBConfig ,
745+ cfg DuckDBPushConfig ,
746+ projects []string ,
747+ excludeProjects []string ,
748+ forceFull bool ,
749+ ) (duckdbsync.PushResult , error ) {
750+ if err := duckdbsync .ValidatePushTarget (duckCfg ); err != nil {
751+ return duckdbsync.PushResult {}, err
752+ }
694753 opts := duckdbsync.SyncOptions {
695754 Projects : projects ,
696755 ExcludeProjects : excludeProjects ,
@@ -712,6 +771,37 @@ func (b *localArchiveWriteBackend) duckDBPush(
712771 return result , nil
713772}
714773
774+ func (b * localArchiveWriteBackend ) newDuckDBPusher (
775+ engine * syncpkg.Engine ,
776+ duckCfg config.DuckDBConfig ,
777+ cfg DuckDBPushConfig ,
778+ projects , exclude []string ,
779+ ) * duckDBPusher {
780+ pushCfg := cfg
781+ pushCfg .Automatic = true
782+ return & duckDBPusher {
783+ localSync : func (c context.Context ) error {
784+ stats := engine .SyncAll (c , nil )
785+ if err := c .Err (); err != nil {
786+ return err
787+ }
788+ if ! stats .AuthoritativeDiscoveryComplete () {
789+ return errors .New ("local sync discovery incomplete" )
790+ }
791+ engine .FlushSignals ()
792+ return nil
793+ },
794+ ensurePricing : b .ensureCurrentPricing ,
795+ mirrorPush : func (c context.Context , forceFull bool ) (
796+ duckdbsync.PushResult , error ,
797+ ) {
798+ return b .duckDBMirrorPush (
799+ c , duckCfg , pushCfg , projects , exclude , forceFull ,
800+ )
801+ },
802+ }
803+ }
804+
715805func (b * localArchiveWriteBackend ) DuckDBPushWatch (
716806 ctx context.Context ,
717807 duckCfg config.DuckDBConfig ,
@@ -747,20 +837,7 @@ func (b *localArchiveWriteBackend) DuckDBPushWatch(
747837 if b .watchHooks != nil && b .watchHooks .newDuckDBPusher != nil {
748838 pusher = b .watchHooks .newDuckDBPusher (engine )
749839 } else {
750- pusher = b .newDuckDBPusher (
751- func (c context.Context ) error {
752- stats := engine .SyncAll (c , nil )
753- if err := c .Err (); err != nil {
754- return err
755- }
756- if ! stats .AuthoritativeDiscoveryComplete () {
757- return errors .New ("local sync discovery incomplete" )
758- }
759- engine .FlushSignals ()
760- return nil
761- },
762- duckCfg , projects , exclude ,
763- )
840+ pusher = b .newDuckDBPusher (engine , duckCfg , cfg , projects , exclude )
764841 }
765842
766843 fmt .Printf (
@@ -769,20 +846,19 @@ func (b *localArchiveWriteBackend) DuckDBPushWatch(
769846 debounce , interval ,
770847 )
771848
849+
772850 loop , stopLoop := newArchivePushLoop (
773851 b .watchHooks ,
774852 "duckdb watch" , debounce , interval ,
775853 func (c context.Context , r pushReason ) error {
776- pushCfg := cfg
777- pushCfg .Automatic = true
778854 if b .watchHooks != nil && b .watchHooks .duckDBPush != nil {
779855 res , err := b .watchHooks .duckDBPush (c , r , false )
780856 if err != nil {
781857 return err
782858 }
783859 return completeDuckDBWatchPush (res , r )
784860 }
785- return pusher .push (c , r , false , pushCfg )
861+ return pusher .push (c , r , false )
786862 },
787863 )
788864 defer stopLoop ()
@@ -792,33 +868,8 @@ func (b *localArchiveWriteBackend) DuckDBPushWatch(
792868
793869 stopWatcher , openDispatch , unwatchedDirs := startArchivePushWatcher (
794870 b .watchHooks , b .appCfg , engine ,
795- func (callbackCtx context.Context , batch syncpkg.WatchBatch ) error {
796- scope := func () watchRecoveryScope {
797- return probeWatchRecoveryScope (b .appCfg )
798- }
799- if err := syncWatchBatch (callbackCtx , engine , batch , scope ); err != nil {
800- return err
801- }
802- return notifyPushForWatchBatch (callbackCtx , loop , batch )
803- },
804- syncpkg.WatcherOptions {
805- OnCoverageDegraded : func (roots []string ) error {
806- if err := poller .AddObligation (pollingObligation {
807- Key : "watcher-fallback" , Roots : roots ,
808- }); err != nil {
809- return err
810- }
811- return loop .NotifyCoverageDegraded (roots )
812- },
813- OnPollingRequired : func (obligation syncpkg.PollingObligation ) error {
814- return poller .AddObligation (pollingObligation {
815- Key : obligation .Key ,
816- Roots : obligation .Roots ,
817- Probe : obligation .Probe ,
818- })
819- },
820- OnPollingReleased : poller .RemoveObligation ,
821- },
871+ archivePushWatchBatchCallback (b .appCfg , engine , loop ),
872+ archivePushWatchWatcherOptions (loop , poller ),
822873 )
823874 defer stopWatcher ()
824875 if len (unwatchedDirs ) > 0 {
@@ -843,8 +894,6 @@ func (b *localArchiveWriteBackend) DuckDBPushWatch(
843894 }
844895 initialErr := startupErr
845896 if initialErr == nil {
846- pushCfg := cfg
847- pushCfg .Automatic = true
848897 if b .watchHooks != nil && b .watchHooks .duckDBPush != nil {
849898 res , err := b .watchHooks .duckDBPush (
850899 ctx , reasonStartup , cfg .Full || didResync ,
@@ -855,9 +904,7 @@ func (b *localArchiveWriteBackend) DuckDBPushWatch(
855904 initialErr = completeDuckDBWatchPush (res , reasonStartup )
856905 }
857906 } else {
858- initialErr = pusher .push (
859- ctx , reasonStartup , cfg .Full || didResync , pushCfg ,
860- )
907+ initialErr = pusher .push (ctx , reasonStartup , cfg .Full || didResync )
861908 }
862909 }
863910 if initialErr != nil {
@@ -994,37 +1041,8 @@ func (b *localArchiveWriteBackend) PGPushWatch(
9941041
9951042 stopWatcher , openDispatch , unwatchedDirs := startArchivePushWatcher (
9961043 b .watchHooks , b .appCfg , engine ,
997- func (callbackCtx context.Context , batch syncpkg.WatchBatch ) error {
998- scope := func () watchRecoveryScope {
999- return probeWatchRecoveryScope (b .appCfg )
1000- }
1001- if err := syncWatchBatch (callbackCtx , engine , batch , scope ); err != nil {
1002- return err
1003- }
1004- return notifyPushForWatchBatch (callbackCtx , loop , batch )
1005- },
1006- syncpkg.WatcherOptions {
1007- OnCoverageDegraded : func (roots []string ) error {
1008- // Degraded coverage needs both owners: the poller reconciles
1009- // the affected roots authoritatively (including tombstoning
1010- // missed deletions) and the loop re-pushes the refreshed
1011- // archive on its floor.
1012- if err := poller .AddObligation (pollingObligation {
1013- Key : "watcher-fallback" , Roots : roots ,
1014- }); err != nil {
1015- return err
1016- }
1017- return loop .NotifyCoverageDegraded (roots )
1018- },
1019- OnPollingRequired : func (obligation syncpkg.PollingObligation ) error {
1020- return poller .AddObligation (pollingObligation {
1021- Key : obligation .Key ,
1022- Roots : obligation .Roots ,
1023- Probe : obligation .Probe ,
1024- })
1025- },
1026- OnPollingReleased : poller .RemoveObligation ,
1027- },
1044+ archivePushWatchBatchCallback (b .appCfg , engine , loop ),
1045+ archivePushWatchWatcherOptions (loop , poller ),
10281046 )
10291047 defer stopWatcher ()
10301048 if len (unwatchedDirs ) > 0 {
0 commit comments