github.com/prebid/prebid-server/v2@v2.18.0/usersync/ejector.go (about)

     1  package usersync
     2  
     3  import (
     4  	"errors"
     5  	"time"
     6  )
     7  
     8  type Ejector interface {
     9  	Choose(uids map[string]UIDEntry) (string, error)
    10  }
    11  
    12  type OldestEjector struct{}
    13  
    14  type PriorityBidderEjector struct {
    15  	PriorityGroups   [][]string
    16  	SyncersByBidder  map[string]Syncer
    17  	IsSyncerPriority bool
    18  	TieEjector       Ejector
    19  }
    20  
    21  // Choose method for oldest ejector will return the oldest uid
    22  func (o *OldestEjector) Choose(uids map[string]UIDEntry) (string, error) {
    23  	var oldestElement string
    24  	var oldestDate time.Time = time.Unix(1<<63-62135596801, 999999999) // Max value for time
    25  
    26  	for key, value := range uids {
    27  		if value.Expires.Before(oldestDate) {
    28  			oldestElement = key
    29  			oldestDate = value.Expires
    30  		}
    31  	}
    32  	return oldestElement, nil
    33  }
    34  
    35  // Choose method for priority ejector will return the oldest lowest priority element
    36  func (p *PriorityBidderEjector) Choose(uids map[string]UIDEntry) (string, error) {
    37  	nonPriorityUids := getNonPriorityUids(uids, p.PriorityGroups, p.SyncersByBidder)
    38  	if err := p.checkSyncerPriority(nonPriorityUids); err != nil {
    39  		return "", err
    40  	}
    41  
    42  	if len(nonPriorityUids) > 0 {
    43  		return p.TieEjector.Choose(nonPriorityUids)
    44  	}
    45  
    46  	lowestPriorityGroup := p.PriorityGroups[len(p.PriorityGroups)-1]
    47  	if len(lowestPriorityGroup) == 1 {
    48  		uidToDelete := lowestPriorityGroup[0]
    49  		p.PriorityGroups = removeElementFromPriorityGroup(p.PriorityGroups, uidToDelete)
    50  		return uidToDelete, nil
    51  	}
    52  
    53  	lowestPriorityUids := getPriorityUids(lowestPriorityGroup, uids, p.SyncersByBidder)
    54  	uidToDelete, err := p.TieEjector.Choose(lowestPriorityUids)
    55  	if err != nil {
    56  		return "", err
    57  	}
    58  	p.PriorityGroups = removeElementFromPriorityGroup(p.PriorityGroups, uidToDelete)
    59  	return uidToDelete, nil
    60  }
    61  
    62  // updatePriorityGroup will remove the selected element from the priority groups, and will remove the entire priority group if it's empty
    63  func removeElementFromPriorityGroup(priorityGroups [][]string, oldestElement string) [][]string {
    64  	lowestPriorityGroup := priorityGroups[len(priorityGroups)-1]
    65  	if len(lowestPriorityGroup) <= 1 {
    66  		return priorityGroups[:len(priorityGroups)-1]
    67  	}
    68  
    69  	for index, elem := range lowestPriorityGroup {
    70  		if elem == oldestElement {
    71  			updatedPriorityGroup := append(lowestPriorityGroup[:index], lowestPriorityGroup[index+1:]...)
    72  			priorityGroups[len(priorityGroups)-1] = updatedPriorityGroup
    73  			return priorityGroups
    74  		}
    75  	}
    76  	return priorityGroups
    77  }
    78  
    79  func getNonPriorityUids(uids map[string]UIDEntry, priorityGroups [][]string, syncersByBidder map[string]Syncer) map[string]UIDEntry {
    80  	// If no priority groups, then all keys in uids are non-priority
    81  	if len(priorityGroups) == 0 {
    82  		return uids
    83  	}
    84  
    85  	// Create map of keys that are a priority
    86  	isPriority := make(map[string]bool)
    87  	for _, group := range priorityGroups {
    88  		for _, bidder := range group {
    89  			if bidderSyncer, ok := syncersByBidder[bidder]; ok {
    90  				isPriority[bidderSyncer.Key()] = true
    91  			}
    92  		}
    93  	}
    94  
    95  	// Create a map for non-priority uids
    96  	nonPriorityUIDs := make(map[string]UIDEntry)
    97  
    98  	// Loop over uids and populate the nonPriorityUIDs map with non-priority keys
    99  	for key, value := range uids {
   100  		if _, found := isPriority[key]; !found {
   101  			nonPriorityUIDs[key] = value
   102  		}
   103  	}
   104  
   105  	return nonPriorityUIDs
   106  }
   107  
   108  func getPriorityUids(lowestPriorityGroup []string, uids map[string]UIDEntry, syncersByBidder map[string]Syncer) map[string]UIDEntry {
   109  	lowestPriorityUIDs := make(map[string]UIDEntry)
   110  
   111  	// Loop over lowestPriorityGroup and populate the lowestPriorityUIDs map
   112  	for _, bidder := range lowestPriorityGroup {
   113  		if bidderSyncer, ok := syncersByBidder[bidder]; ok {
   114  			if uidEntry, exists := uids[bidderSyncer.Key()]; exists {
   115  				lowestPriorityUIDs[bidderSyncer.Key()] = uidEntry
   116  			}
   117  		}
   118  	}
   119  	return lowestPriorityUIDs
   120  }
   121  
   122  func (p *PriorityBidderEjector) checkSyncerPriority(nonPriorityUids map[string]UIDEntry) error {
   123  	if len(nonPriorityUids) == 1 && !p.IsSyncerPriority && len(p.PriorityGroups) > 0 {
   124  		return errors.New("syncer key is not a priority, and there are only priority elements left")
   125  	}
   126  	return nil
   127  }