gifs/api
6:eb450538f079 Browse Files
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/context.go Fri Oct 17 07:20:06 2014 -0400 1.2 +++ b/context.go Fri Oct 17 07:21:02 2014 -0400 1.3 @@ -1,21 +1,35 @@ 1.4 package api 1.5 1.6 import ( 1.7 - "code.google.com/p/goauth2/oauth/jwt" 1.8 - "code.google.com/p/google-api-go-client/storage/v1beta2" 1.9 - _ "github.com/go-sql-driver/mysql" 1.10 + "errors" 1.11 + "net/http" 1.12 + 1.13 + "code.google.com/p/google-api-go-client/storage/v1" 1.14 + "github.com/golang/oauth2/google" 1.15 +) 1.16 + 1.17 +var ( 1.18 + ErrNoBucketSet = errors.New("no bucket set") 1.19 + ErrNoTmpBucketSet = errors.New("no temporary bucket set") 1.20 ) 1.21 1.22 type Context struct { 1.23 Storage Storage 1.24 Datastore Datastore 1.25 - UsageTracker *UsageTracker 1.26 + UsageTracker UsageTracker 1.27 + TmpBucket string 1.28 Bucket string 1.29 RootDomain string 1.30 } 1.31 1.32 -func NewMemStorage() Storage { 1.33 - return Memstorage{} 1.34 +func (c Context) Validate() error { 1.35 + if c.Bucket == "" { 1.36 + return ErrNoBucketSet 1.37 + } 1.38 + if c.TmpBucket == "" { 1.39 + return ErrNoTmpBucketSet 1.40 + } 1.41 + return nil 1.42 } 1.43 1.44 func NewMemDatastore() Datastore { 1.45 @@ -23,19 +37,13 @@ 1.46 collections: map[string]Collection{}, 1.47 domains: map[string]Domain{}, 1.48 items: map[string]Item{}, 1.49 - users: map[string]User{}, 1.50 } 1.51 } 1.52 1.53 -func NewGCSStorage(gcsClientEmail, gcsTokenURI string, gcsPemBytes []byte) (Storage, error) { 1.54 - t := jwt.NewToken(gcsClientEmail, storage.DevstorageFull_controlScope, gcsPemBytes) 1.55 - t.ClaimSet.Aud = gcsTokenURI 1.56 - transport, err := jwt.NewTransport(t) 1.57 - if err != nil { 1.58 - return nil, err 1.59 - } 1.60 - client := transport.Client() 1.61 - gcsService, err := storage.New(client) 1.62 +func NewGCSServiceStorage() (*GoogleCloudStorage, error) { 1.63 + config := google.NewComputeEngineConfig("") 1.64 + client := http.Client{Transport: config.NewTransport()} 1.65 + gcsService, err := storage.New(&client) 1.66 if err != nil { 1.67 return nil, err 1.68 }
2.1 --- a/usage.go Fri Oct 17 07:20:06 2014 -0400 2.2 +++ b/usage.go Fri Oct 17 07:21:02 2014 -0400 2.3 @@ -1,87 +1,109 @@ 2.4 package api 2.5 2.6 import ( 2.7 + "sort" 2.8 "sync" 2.9 + 2.10 + "code.secondbit.org/uuid.hg" 2.11 ) 2.12 2.13 -func NewUsageTracker() *UsageTracker { 2.14 - return &UsageTracker{ 2.15 - usages: make(map[string]*Usage), 2.16 +type UsageTracker interface { 2.17 + TrackUpload(id uuid.ID, bytes int64) error 2.18 + TrackDownload(id uuid.ID, bytes int64) error 2.19 + GetUsage(id uuid.ID) (Usage, error) 2.20 + GetTopUsages(id uuid.ID, num, offset int64) ([]Usage, error) 2.21 +} 2.22 + 2.23 +type Usage struct { 2.24 + UserID uuid.ID 2.25 + UploadedBytes int64 2.26 + DownloadedBytes int64 2.27 + UploadRequests int64 2.28 + DownloadRequests int64 2.29 +} 2.30 + 2.31 +func (u Usage) score() float64 { 2.32 + storageCost := .026 * float64(u.UploadedBytes) / 1073741824 2.33 + bandwidthCost := .012 * float64(u.DownloadedBytes) / 1073741824 2.34 + uploadReqCost := .02 * float64(u.UploadRequests) / 1000 2.35 + downloadReqCost := .01 * float64(u.DownloadRequests) / 10000 2.36 + return storageCost + bandwidthCost + uploadReqCost + downloadReqCost 2.37 +} 2.38 + 2.39 +type topUsages []Usage 2.40 + 2.41 +func (t topUsages) Less(i, j int) bool { 2.42 + return t[i].score() > t[j].score() 2.43 +} 2.44 + 2.45 +func (t topUsages) Swap(i, j int) { 2.46 + t[i], t[j] = t[j], t[i] 2.47 +} 2.48 + 2.49 +func (t topUsages) Len() int { 2.50 + return len(t) 2.51 +} 2.52 + 2.53 +type MemTracker struct { 2.54 + usages map[string]Usage 2.55 + sync.RWMutex 2.56 +} 2.57 + 2.58 +func NewMemTracker() *MemTracker { 2.59 + return &MemTracker{ 2.60 + usages: map[string]Usage{}, 2.61 } 2.62 } 2.63 2.64 -type UsageTracker struct { 2.65 - usages map[string]*Usage 2.66 - sync.Mutex 2.67 +func (m *MemTracker) TrackUpload(id uuid.ID, bytes int64) error { 2.68 + m.Lock() 2.69 + defer m.Unlock() 2.70 + 2.71 + use := m.usages[id.String()] 2.72 + 2.73 + use.UploadedBytes = use.UploadedBytes + bytes 2.74 + use.UploadRequests = use.UploadRequests + 1 2.75 + m.usages[id.String()] = use 2.76 + return nil 2.77 } 2.78 2.79 -func (u *UsageTracker) TrackUploads(id string) (bytes, requests chan int64) { 2.80 - u.Lock() 2.81 - defer u.Unlock() 2.82 - if _, ok := u.usages[id]; !ok { 2.83 - u.usages[id] = &Usage{ 2.84 - UploadBytesChan: make(chan int64), 2.85 - DownloadBytesChan: make(chan int64), 2.86 - UploadRequestsChan: make(chan int64), 2.87 - DownloadRequestsChan: make(chan int64), 2.88 - } 2.89 - go u.usages[id].collect() 2.90 - } 2.91 - return u.usages[id].UploadBytesChan, u.usages[id].UploadRequestsChan 2.92 +func (m *MemTracker) TrackDownload(id uuid.ID, bytes int64) error { 2.93 + m.Lock() 2.94 + defer m.Unlock() 2.95 + 2.96 + use := m.usages[id.String()] 2.97 + 2.98 + use.DownloadedBytes = use.DownloadedBytes + bytes 2.99 + use.DownloadRequests = use.DownloadRequests + 1 2.100 + m.usages[id.String()] = use 2.101 + return nil 2.102 } 2.103 2.104 -func (u *UsageTracker) TrackDownloads(id string) (bytes, requests chan int64) { 2.105 - u.Lock() 2.106 - defer u.Unlock() 2.107 - if _, ok := u.usages[id]; !ok { 2.108 - u.usages[id] = &Usage{ 2.109 - UploadBytesChan: make(chan int64), 2.110 - DownloadBytesChan: make(chan int64), 2.111 - UploadRequestsChan: make(chan int64), 2.112 - DownloadRequestsChan: make(chan int64), 2.113 - } 2.114 - go u.usages[id].collect() 2.115 - } 2.116 - return u.usages[id].DownloadBytesChan, u.usages[id].DownloadRequestsChan 2.117 +func (m *MemTracker) GetUsage(id uuid.ID) (Usage, error) { 2.118 + m.RLock() 2.119 + defer m.RUnlock() 2.120 + 2.121 + return m.usages[id.String()], nil 2.122 } 2.123 2.124 -type Usage struct { 2.125 - UploadedBytes int64 2.126 - UploadBytesChan chan int64 2.127 - DownloadedBytes int64 2.128 - DownloadBytesChan chan int64 2.129 - UploadRequests int64 2.130 - UploadRequestsChan chan int64 2.131 - DownloadRequests int64 2.132 - DownloadRequestsChan chan int64 2.133 -} 2.134 +func (m *MemTracker) GetTopUsages(id uuid.ID, num, offset int64) ([]Usage, error) { 2.135 + m.RLock() 2.136 + defer m.RUnlock() 2.137 2.138 -func (u *Usage) collect() { 2.139 - for { 2.140 - select { 2.141 - case b, ok := <-u.UploadBytesChan: 2.142 - if !ok { 2.143 - u.UploadBytesChan = nil 2.144 - } 2.145 - u.UploadedBytes += b 2.146 - case r, ok := <-u.UploadRequestsChan: 2.147 - if !ok { 2.148 - u.UploadRequestsChan = nil 2.149 - } 2.150 - u.UploadRequests += r 2.151 - case b, ok := <-u.DownloadBytesChan: 2.152 - if !ok { 2.153 - u.DownloadBytesChan = nil 2.154 - } 2.155 - u.DownloadedBytes += b 2.156 - case r, ok := <-u.DownloadRequestsChan: 2.157 - if !ok { 2.158 - u.DownloadRequestsChan = nil 2.159 - } 2.160 - u.DownloadRequests += r 2.161 - } 2.162 - if u.UploadBytesChan == nil && u.UploadRequestsChan == nil && u.DownloadBytesChan == nil && u.DownloadRequestsChan == nil { 2.163 - break 2.164 - } 2.165 + usages := []Usage{} 2.166 + for _, usage := range m.usages { 2.167 + usages = append(usages, usage) 2.168 + } 2.169 + sortedUsages := topUsages(usages) 2.170 + sort.Sort(sortedUsages) 2.171 + usages = []Usage(sortedUsages) 2.172 + 2.173 + if int64(len(usages)) <= offset { 2.174 + return []Usage{}, nil 2.175 + } 2.176 + if int64(len(usages)) > offset+num { 2.177 + return usages[offset : offset+num], nil 2.178 + } else { 2.179 + return usages[offset:], nil 2.180 } 2.181 }