github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/destinationfetchersvc/syncjob.go (about) 1 package destinationfetchersvc 2 3 import ( 4 "context" 5 "sync" 6 "sync/atomic" 7 "time" 8 9 "github.com/kyma-incubator/compass/components/director/pkg/cronjob" 10 "github.com/kyma-incubator/compass/components/director/pkg/log" 11 "golang.org/x/sync/semaphore" 12 ) 13 14 // DestinationSyncer missing godoc 15 // 16 //go:generate mockery --name=DestinationSyncer --output=automock --outpkg=automock --case=underscore --disable-version-string 17 type DestinationSyncer interface { 18 SyncTenantDestinations(ctx context.Context, tenantID string) error 19 GetSubscribedTenantIDs(ctx context.Context) ([]string, error) 20 } 21 22 // SyncJobConfig configuration for destination sync job 23 type SyncJobConfig struct { 24 ElectionCfg cronjob.ElectionConfig 25 JobSchedulePeriod time.Duration 26 TenantSyncTimeout time.Duration 27 ParallelTenants int 28 } 29 30 func syncSubscribedTenantsDestinations(ctx context.Context, subscribedTenants []string, cfg SyncJobConfig, 31 destinationSyncer DestinationSyncer) int { 32 parallelTenantsSemaphore := semaphore.NewWeighted(int64(cfg.ParallelTenants)) 33 wg := sync.WaitGroup{} 34 var syncedTenants uint32 35 for _, tenantID := range subscribedTenants { 36 wg.Add(1) 37 go func(tenantID string) { 38 defer wg.Done() 39 if err := parallelTenantsSemaphore.Acquire(ctx, 1); err != nil { 40 log.C(ctx).WithError(err).Errorf("Could not acquire semaphore") 41 return 42 } 43 defer parallelTenantsSemaphore.Release(1) 44 err := syncTenantDestinationsWithTimeout(ctx, destinationSyncer, tenantID, cfg.TenantSyncTimeout) 45 if err != nil { 46 log.C(ctx).WithError(err).Error() 47 return 48 } 49 atomic.AddUint32(&syncedTenants, 1) 50 currentlySynced := int(atomic.LoadUint32(&syncedTenants)) 51 // Log on each ParallelTenants synced to track progress 52 if currentlySynced%cfg.ParallelTenants == 0 { 53 log.C(ctx).Infof("%d/%d tenants have been synced", currentlySynced, len(subscribedTenants)) 54 } 55 }(tenantID) 56 } 57 wg.Wait() 58 return int(syncedTenants) 59 } 60 61 // StartDestinationFetcherSyncJob starts destination sync job and blocks 62 func StartDestinationFetcherSyncJob(ctx context.Context, cfg SyncJobConfig, destinationSyncer DestinationSyncer) error { 63 resyncJob := cronjob.CronJob{ 64 Name: "DestinationFetcherSync", 65 Fn: func(jobCtx context.Context) { 66 subscribedTenants, err := destinationSyncer.GetSubscribedTenantIDs(jobCtx) 67 if err != nil { 68 log.C(jobCtx).WithError(err).Errorf("Could not fetch subscribed tenants for destination resync") 69 return 70 } 71 if len(subscribedTenants) == 0 { 72 log.C(jobCtx).Info("No subscribed tenants found. Skipping destination sync job") 73 return 74 } 75 log.C(jobCtx).Infof("Found %d subscribed tenants. Starting destination sync...", len(subscribedTenants)) 76 syncedTenantsCount := syncSubscribedTenantsDestinations(jobCtx, subscribedTenants, cfg, destinationSyncer) 77 log.C(jobCtx).Infof("Destination sync finished with %d/%d tenants synced", 78 syncedTenantsCount, len(subscribedTenants)) 79 }, 80 SchedulePeriod: cfg.JobSchedulePeriod, 81 } 82 return cronjob.RunCronJob(ctx, cfg.ElectionCfg, resyncJob) 83 } 84 85 func syncTenantDestinationsWithTimeout( 86 ctx context.Context, destinationSyncer DestinationSyncer, tenantID string, timeout time.Duration) error { 87 tenantSyncTimeoutCtx, cancel := context.WithTimeout(ctx, timeout) 88 defer cancel() 89 start := time.Now() 90 defer func() { 91 duration := time.Since(start) 92 log.C(ctx).Debugf("Destinations synced for tenant '%s' for %s", tenantID, duration) 93 if duration > timeout/2 { 94 log.C(ctx).Warnf("Destinations synchronization for tenant '%s' took %s", tenantID, duration) 95 } 96 }() 97 return destinationSyncer.SyncTenantDestinations(tenantSyncTimeoutCtx, tenantID) 98 }