gifs/api

Paddy 2014-10-17 Parent:08ec88016e2f

6:eb450538f079 Go to Latest

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.

History
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 }