github.com/wormhole-foundation/wormhole-explorer/common@v0.0.0-20240604151348-09585b5b97c5/pool/pool.go (about) 1 package pool 2 3 import ( 4 "sort" 5 "time" 6 7 "golang.org/x/net/context" 8 "golang.org/x/time/rate" 9 ) 10 11 // Pool is a pool of items. 12 type Pool struct { 13 items []Item 14 } 15 16 // Item defines the item of the pool. 17 type Item struct { 18 // Id is the item ID. 19 Id string 20 // description of the item. 21 Description string 22 // priority is the priority of the item. 23 // The lower the value, the higher the priority. 24 priority uint8 25 // rateLimit is the rate limiter for the item. 26 rateLimit *rate.Limiter 27 } 28 29 // NewPool creates a new pool. 30 func NewPool(cfg []Config) *Pool { 31 p := &Pool{} 32 for _, c := range cfg { 33 p.addItem(c) 34 } 35 return p 36 } 37 38 // addItem adds a new item to the pool. 39 func (p *Pool) addItem(cfg Config) { 40 i := Item{ 41 Id: cfg.Id, 42 Description: cfg.Description, 43 priority: cfg.Priority, 44 rateLimit: rate.NewLimiter( 45 rate.Every(time.Minute/time.Duration(cfg.RequestsPerMinute)), 1), 46 } 47 p.items = append(p.items, i) 48 } 49 50 // GetItem returns the next available item of the pool. 51 func (p *Pool) GetItem() Item { 52 // check if there is no item 53 if len(p.items) == 0 { 54 return Item{} 55 } 56 57 // get the next available item 58 itemWithScore := []struct { 59 item Item 60 score float64 61 }{} 62 63 now := time.Now() 64 for _, i := range p.items { 65 tokenAt := i.rateLimit.TokensAt(now) 66 itemWithScore = append(itemWithScore, struct { 67 item Item 68 score float64 69 }{ 70 item: i, 71 score: tokenAt, 72 }) 73 } 74 75 // sort by score and priority 76 sort.Slice(itemWithScore, func(i, j int) bool { 77 if itemWithScore[i].score == itemWithScore[j].score { 78 return itemWithScore[i].item.priority < itemWithScore[j].item.priority 79 } 80 return itemWithScore[i].score > itemWithScore[j].score 81 }) 82 83 return itemWithScore[0].item 84 } 85 86 // GetItems returns the list of items sorted by score and priority. 87 // Once there is an event on the item, it must be notified using the method NotifyEvent. 88 func (p *Pool) GetItems() []Item { 89 if len(p.items) == 0 { 90 return []Item{} 91 } 92 93 itemsWithScore := []struct { 94 item Item 95 score float64 96 }{} 97 98 now := time.Now() 99 for _, i := range p.items { 100 tokenAt := i.rateLimit.TokensAt(now) 101 itemsWithScore = append(itemsWithScore, struct { 102 item Item 103 score float64 104 }{ 105 item: i, 106 score: tokenAt, 107 }) 108 } 109 110 // sort by score and priority 111 sort.Slice(itemsWithScore, func(i, j int) bool { 112 if itemsWithScore[i].score == itemsWithScore[j].score { 113 return itemsWithScore[i].item.priority < itemsWithScore[j].item.priority 114 } 115 return itemsWithScore[i].score > itemsWithScore[j].score 116 }) 117 118 // convert itemsWithScore to items 119 items := []Item{} 120 for _, i := range itemsWithScore { 121 items = append(items, i.item) 122 } 123 return items 124 } 125 126 // Wait waits for the rate limiter to allow the next item request. 127 func (i *Item) Wait(ctx context.Context) error { 128 return i.rateLimit.Wait(ctx) 129 }