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