gifs/api

Paddy 2014-10-17 Parent:b5d88d57d587 Child:21787ed8a185

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.

context.go usage.go

     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  }