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.
| paddy@0 | 1 package api |
| paddy@0 | 2 |
| paddy@0 | 3 import ( |
| paddy@6 | 4 "sort" |
| paddy@0 | 5 "sync" |
| paddy@6 | 6 |
| paddy@6 | 7 "code.secondbit.org/uuid.hg" |
| paddy@0 | 8 ) |
| paddy@0 | 9 |
| paddy@6 | 10 type UsageTracker interface { |
| paddy@6 | 11 TrackUpload(id uuid.ID, bytes int64) error |
| paddy@6 | 12 TrackDownload(id uuid.ID, bytes int64) error |
| paddy@6 | 13 GetUsage(id uuid.ID) (Usage, error) |
| paddy@6 | 14 GetTopUsages(id uuid.ID, num, offset int64) ([]Usage, error) |
| paddy@6 | 15 } |
| paddy@6 | 16 |
| paddy@6 | 17 type Usage struct { |
| paddy@6 | 18 UserID uuid.ID |
| paddy@6 | 19 UploadedBytes int64 |
| paddy@6 | 20 DownloadedBytes int64 |
| paddy@6 | 21 UploadRequests int64 |
| paddy@6 | 22 DownloadRequests int64 |
| paddy@6 | 23 } |
| paddy@6 | 24 |
| paddy@6 | 25 func (u Usage) score() float64 { |
| paddy@6 | 26 storageCost := .026 * float64(u.UploadedBytes) / 1073741824 |
| paddy@6 | 27 bandwidthCost := .012 * float64(u.DownloadedBytes) / 1073741824 |
| paddy@6 | 28 uploadReqCost := .02 * float64(u.UploadRequests) / 1000 |
| paddy@6 | 29 downloadReqCost := .01 * float64(u.DownloadRequests) / 10000 |
| paddy@6 | 30 return storageCost + bandwidthCost + uploadReqCost + downloadReqCost |
| paddy@6 | 31 } |
| paddy@6 | 32 |
| paddy@6 | 33 type topUsages []Usage |
| paddy@6 | 34 |
| paddy@6 | 35 func (t topUsages) Less(i, j int) bool { |
| paddy@6 | 36 return t[i].score() > t[j].score() |
| paddy@6 | 37 } |
| paddy@6 | 38 |
| paddy@6 | 39 func (t topUsages) Swap(i, j int) { |
| paddy@6 | 40 t[i], t[j] = t[j], t[i] |
| paddy@6 | 41 } |
| paddy@6 | 42 |
| paddy@6 | 43 func (t topUsages) Len() int { |
| paddy@6 | 44 return len(t) |
| paddy@6 | 45 } |
| paddy@6 | 46 |
| paddy@6 | 47 type MemTracker struct { |
| paddy@6 | 48 usages map[string]Usage |
| paddy@6 | 49 sync.RWMutex |
| paddy@6 | 50 } |
| paddy@6 | 51 |
| paddy@6 | 52 func NewMemTracker() *MemTracker { |
| paddy@6 | 53 return &MemTracker{ |
| paddy@6 | 54 usages: map[string]Usage{}, |
| paddy@0 | 55 } |
| paddy@0 | 56 } |
| paddy@0 | 57 |
| paddy@6 | 58 func (m *MemTracker) TrackUpload(id uuid.ID, bytes int64) error { |
| paddy@6 | 59 m.Lock() |
| paddy@6 | 60 defer m.Unlock() |
| paddy@6 | 61 |
| paddy@6 | 62 use := m.usages[id.String()] |
| paddy@6 | 63 |
| paddy@6 | 64 use.UploadedBytes = use.UploadedBytes + bytes |
| paddy@6 | 65 use.UploadRequests = use.UploadRequests + 1 |
| paddy@6 | 66 m.usages[id.String()] = use |
| paddy@6 | 67 return nil |
| paddy@0 | 68 } |
| paddy@0 | 69 |
| paddy@6 | 70 func (m *MemTracker) TrackDownload(id uuid.ID, bytes int64) error { |
| paddy@6 | 71 m.Lock() |
| paddy@6 | 72 defer m.Unlock() |
| paddy@6 | 73 |
| paddy@6 | 74 use := m.usages[id.String()] |
| paddy@6 | 75 |
| paddy@6 | 76 use.DownloadedBytes = use.DownloadedBytes + bytes |
| paddy@6 | 77 use.DownloadRequests = use.DownloadRequests + 1 |
| paddy@6 | 78 m.usages[id.String()] = use |
| paddy@6 | 79 return nil |
| paddy@0 | 80 } |
| paddy@0 | 81 |
| paddy@6 | 82 func (m *MemTracker) GetUsage(id uuid.ID) (Usage, error) { |
| paddy@6 | 83 m.RLock() |
| paddy@6 | 84 defer m.RUnlock() |
| paddy@6 | 85 |
| paddy@6 | 86 return m.usages[id.String()], nil |
| paddy@0 | 87 } |
| paddy@0 | 88 |
| paddy@6 | 89 func (m *MemTracker) GetTopUsages(id uuid.ID, num, offset int64) ([]Usage, error) { |
| paddy@6 | 90 m.RLock() |
| paddy@6 | 91 defer m.RUnlock() |
| paddy@0 | 92 |
| paddy@6 | 93 usages := []Usage{} |
| paddy@6 | 94 for _, usage := range m.usages { |
| paddy@6 | 95 usages = append(usages, usage) |
| paddy@6 | 96 } |
| paddy@6 | 97 sortedUsages := topUsages(usages) |
| paddy@6 | 98 sort.Sort(sortedUsages) |
| paddy@6 | 99 usages = []Usage(sortedUsages) |
| paddy@6 | 100 |
| paddy@6 | 101 if int64(len(usages)) <= offset { |
| paddy@6 | 102 return []Usage{}, nil |
| paddy@6 | 103 } |
| paddy@6 | 104 if int64(len(usages)) > offset+num { |
| paddy@6 | 105 return usages[offset : offset+num], nil |
| paddy@6 | 106 } else { |
| paddy@6 | 107 return usages[offset:], nil |
| paddy@0 | 108 } |
| paddy@0 | 109 } |