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  }