gifs/api
2014-10-17
Parent:eb450538f079
gifs/api/usage.go
Create a Dockerfile and binary. Write a Dockerfile that compiles everything and runs it. Start the binary; load a Context from etcd, and start everything running. The binary is pretty useless until we get HTTP handlers, though, obviously.
| 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 } |