dubbo.apache.org/dubbo-go/v3@v3.1.1/xds/utils/wrr/edf.go (about) 1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 /* 19 * 20 * Copyright 2019 gRPC authors. 21 * 22 */ 23 24 package wrr 25 26 import ( 27 "container/heap" 28 "sync" 29 ) 30 31 // edfWrr is a struct for EDF weighted round robin implementation. 32 type edfWrr struct { 33 lock sync.Mutex 34 items edfPriorityQueue 35 currentOrderOffset uint64 36 currentTime float64 37 } 38 39 // NewEDF creates Earliest Deadline First (EDF) 40 // (https://en.wikipedia.org/wiki/Earliest_deadline_first_scheduling) implementation for weighted round robin. 41 // Each pick from the schedule has the earliest deadline entry selected. Entries have deadlines set 42 // at current time + 1 / weight, providing weighted round robin behavior with O(log n) pick time. 43 func NewEDF() WRR { 44 return &edfWrr{} 45 } 46 47 // edfEntry is an internal wrapper for item that also stores weight and relative position in the queue. 48 type edfEntry struct { 49 deadline float64 50 weight int64 51 orderOffset uint64 52 item interface{} 53 } 54 55 // edfPriorityQueue is a heap.Interface implementation for edfEntry elements. 56 type edfPriorityQueue []*edfEntry 57 58 func (pq edfPriorityQueue) Len() int { return len(pq) } 59 func (pq edfPriorityQueue) Less(i, j int) bool { 60 return pq[i].deadline < pq[j].deadline || pq[i].deadline == pq[j].deadline && pq[i].orderOffset < pq[j].orderOffset 61 } 62 func (pq edfPriorityQueue) Swap(i, j int) { pq[i], pq[j] = pq[j], pq[i] } 63 64 func (pq *edfPriorityQueue) Push(x interface{}) { 65 *pq = append(*pq, x.(*edfEntry)) 66 } 67 68 func (pq *edfPriorityQueue) Pop() interface{} { 69 old := *pq 70 *pq = old[0 : len(old)-1] 71 return old[len(old)-1] 72 } 73 74 func (edf *edfWrr) Add(item interface{}, weight int64) { 75 edf.lock.Lock() 76 defer edf.lock.Unlock() 77 entry := edfEntry{ 78 deadline: edf.currentTime + 1.0/float64(weight), 79 weight: weight, 80 item: item, 81 orderOffset: edf.currentOrderOffset, 82 } 83 edf.currentOrderOffset++ 84 heap.Push(&edf.items, &entry) 85 } 86 87 func (edf *edfWrr) Next() interface{} { 88 edf.lock.Lock() 89 defer edf.lock.Unlock() 90 if len(edf.items) == 0 { 91 return nil 92 } 93 item := edf.items[0] 94 edf.currentTime = item.deadline 95 item.deadline = edf.currentTime + 1.0/float64(item.weight) 96 heap.Fix(&edf.items, 0) 97 return item.item 98 }