gifs/api
2014-10-17
Parent:08ec88016e2f
gifs/api/usage.go
Make UsageTracker an interface, ditch the channel interface. Stop using channels to track usage. Just call functions. Make UsageTracker an interface, so there can be multiple implementations, and create the in-memory implementation.
1.1 --- a/usage.go Fri Oct 17 07:20:06 2014 -0400 1.2 +++ b/usage.go Fri Oct 17 07:21:02 2014 -0400 1.3 @@ -1,87 +1,109 @@ 1.4 package api 1.5 1.6 import ( 1.7 + "sort" 1.8 "sync" 1.9 + 1.10 + "code.secondbit.org/uuid.hg" 1.11 ) 1.12 1.13 -func NewUsageTracker() *UsageTracker { 1.14 - return &UsageTracker{ 1.15 - usages: make(map[string]*Usage), 1.16 +type UsageTracker interface { 1.17 + TrackUpload(id uuid.ID, bytes int64) error 1.18 + TrackDownload(id uuid.ID, bytes int64) error 1.19 + GetUsage(id uuid.ID) (Usage, error) 1.20 + GetTopUsages(id uuid.ID, num, offset int64) ([]Usage, error) 1.21 +} 1.22 + 1.23 +type Usage struct { 1.24 + UserID uuid.ID 1.25 + UploadedBytes int64 1.26 + DownloadedBytes int64 1.27 + UploadRequests int64 1.28 + DownloadRequests int64 1.29 +} 1.30 + 1.31 +func (u Usage) score() float64 { 1.32 + storageCost := .026 * float64(u.UploadedBytes) / 1073741824 1.33 + bandwidthCost := .012 * float64(u.DownloadedBytes) / 1073741824 1.34 + uploadReqCost := .02 * float64(u.UploadRequests) / 1000 1.35 + downloadReqCost := .01 * float64(u.DownloadRequests) / 10000 1.36 + return storageCost + bandwidthCost + uploadReqCost + downloadReqCost 1.37 +} 1.38 + 1.39 +type topUsages []Usage 1.40 + 1.41 +func (t topUsages) Less(i, j int) bool { 1.42 + return t[i].score() > t[j].score() 1.43 +} 1.44 + 1.45 +func (t topUsages) Swap(i, j int) { 1.46 + t[i], t[j] = t[j], t[i] 1.47 +} 1.48 + 1.49 +func (t topUsages) Len() int { 1.50 + return len(t) 1.51 +} 1.52 + 1.53 +type MemTracker struct { 1.54 + usages map[string]Usage 1.55 + sync.RWMutex 1.56 +} 1.57 + 1.58 +func NewMemTracker() *MemTracker { 1.59 + return &MemTracker{ 1.60 + usages: map[string]Usage{}, 1.61 } 1.62 } 1.63 1.64 -type UsageTracker struct { 1.65 - usages map[string]*Usage 1.66 - sync.Mutex 1.67 +func (m *MemTracker) TrackUpload(id uuid.ID, bytes int64) error { 1.68 + m.Lock() 1.69 + defer m.Unlock() 1.70 + 1.71 + use := m.usages[id.String()] 1.72 + 1.73 + use.UploadedBytes = use.UploadedBytes + bytes 1.74 + use.UploadRequests = use.UploadRequests + 1 1.75 + m.usages[id.String()] = use 1.76 + return nil 1.77 } 1.78 1.79 -func (u *UsageTracker) TrackUploads(id string) (bytes, requests chan int64) { 1.80 - u.Lock() 1.81 - defer u.Unlock() 1.82 - if _, ok := u.usages[id]; !ok { 1.83 - u.usages[id] = &Usage{ 1.84 - UploadBytesChan: make(chan int64), 1.85 - DownloadBytesChan: make(chan int64), 1.86 - UploadRequestsChan: make(chan int64), 1.87 - DownloadRequestsChan: make(chan int64), 1.88 - } 1.89 - go u.usages[id].collect() 1.90 - } 1.91 - return u.usages[id].UploadBytesChan, u.usages[id].UploadRequestsChan 1.92 +func (m *MemTracker) TrackDownload(id uuid.ID, bytes int64) error { 1.93 + m.Lock() 1.94 + defer m.Unlock() 1.95 + 1.96 + use := m.usages[id.String()] 1.97 + 1.98 + use.DownloadedBytes = use.DownloadedBytes + bytes 1.99 + use.DownloadRequests = use.DownloadRequests + 1 1.100 + m.usages[id.String()] = use 1.101 + return nil 1.102 } 1.103 1.104 -func (u *UsageTracker) TrackDownloads(id string) (bytes, requests chan int64) { 1.105 - u.Lock() 1.106 - defer u.Unlock() 1.107 - if _, ok := u.usages[id]; !ok { 1.108 - u.usages[id] = &Usage{ 1.109 - UploadBytesChan: make(chan int64), 1.110 - DownloadBytesChan: make(chan int64), 1.111 - UploadRequestsChan: make(chan int64), 1.112 - DownloadRequestsChan: make(chan int64), 1.113 - } 1.114 - go u.usages[id].collect() 1.115 - } 1.116 - return u.usages[id].DownloadBytesChan, u.usages[id].DownloadRequestsChan 1.117 +func (m *MemTracker) GetUsage(id uuid.ID) (Usage, error) { 1.118 + m.RLock() 1.119 + defer m.RUnlock() 1.120 + 1.121 + return m.usages[id.String()], nil 1.122 } 1.123 1.124 -type Usage struct { 1.125 - UploadedBytes int64 1.126 - UploadBytesChan chan int64 1.127 - DownloadedBytes int64 1.128 - DownloadBytesChan chan int64 1.129 - UploadRequests int64 1.130 - UploadRequestsChan chan int64 1.131 - DownloadRequests int64 1.132 - DownloadRequestsChan chan int64 1.133 -} 1.134 +func (m *MemTracker) GetTopUsages(id uuid.ID, num, offset int64) ([]Usage, error) { 1.135 + m.RLock() 1.136 + defer m.RUnlock() 1.137 1.138 -func (u *Usage) collect() { 1.139 - for { 1.140 - select { 1.141 - case b, ok := <-u.UploadBytesChan: 1.142 - if !ok { 1.143 - u.UploadBytesChan = nil 1.144 - } 1.145 - u.UploadedBytes += b 1.146 - case r, ok := <-u.UploadRequestsChan: 1.147 - if !ok { 1.148 - u.UploadRequestsChan = nil 1.149 - } 1.150 - u.UploadRequests += r 1.151 - case b, ok := <-u.DownloadBytesChan: 1.152 - if !ok { 1.153 - u.DownloadBytesChan = nil 1.154 - } 1.155 - u.DownloadedBytes += b 1.156 - case r, ok := <-u.DownloadRequestsChan: 1.157 - if !ok { 1.158 - u.DownloadRequestsChan = nil 1.159 - } 1.160 - u.DownloadRequests += r 1.161 - } 1.162 - if u.UploadBytesChan == nil && u.UploadRequestsChan == nil && u.DownloadBytesChan == nil && u.DownloadRequestsChan == nil { 1.163 - break 1.164 - } 1.165 + usages := []Usage{} 1.166 + for _, usage := range m.usages { 1.167 + usages = append(usages, usage) 1.168 + } 1.169 + sortedUsages := topUsages(usages) 1.170 + sort.Sort(sortedUsages) 1.171 + usages = []Usage(sortedUsages) 1.172 + 1.173 + if int64(len(usages)) <= offset { 1.174 + return []Usage{}, nil 1.175 + } 1.176 + if int64(len(usages)) > offset+num { 1.177 + return usages[offset : offset+num], nil 1.178 + } else { 1.179 + return usages[offset:], nil 1.180 } 1.181 }