Skip to content

Commit

Permalink
Add program
Browse files Browse the repository at this point in the history
  • Loading branch information
phedoreanu committed Oct 20, 2016
1 parent bd78a90 commit 39cf0a6
Show file tree
Hide file tree
Showing 13 changed files with 559 additions and 1 deletion.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,7 @@ _testmain.go
*.exe
*.test
*.prof

counter
results.bin
*.retry
10 changes: 10 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
language: go
go:
- 1.6
- 1.7
- tip
before_install:
- go get github.com/mattn/goveralls
- go get golang.org/x/tools/cmd/cover
script:
- $HOME/gopath/bin/goveralls -service=travis-ci
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
FROM golang:onbuild
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,27 @@
# counter
# counter [![Build Status](https://travis-ci.org/phedoreanu/counter.svg?branch=master)](https://travis-ci.org/phedoreanu/counter) [![Coverage Status](https://coveralls.io/repos/github/phedoreanu/counter/badge.svg)](https://coveralls.io/github/phedoreanu/counter) [![Go Report Card](https://goreportcard.com/badge/github.com/phedoreanu/counter)](https://goreportcard.com/report/github.com/phedoreanu/counter)

[![License](http://img.shields.io/:license-mit-blue.svg)](http://doge.mit-license.org) [![GoDoc](https://godoc.org/github.com/phedoreanu/counter?status.svg)](https://godoc.org/github.com/phedoreanu/counter)

###Installation
Counter requires Go 1.5 or later.

###Usage
```
docker-compose up
```

###Unit tests
```
go test -v -race -cover -parallel 8 -cpu 8
```

###Smoke tests
```
ansible-playbook smoke-tests.yml
```

###Load tests
Start the app and execute:
```
HOSTNAME=localhost:8080 ./load-tests.sh
```
115 changes: 115 additions & 0 deletions db/db.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package db

import (
"fmt"
"log"
"os"
"time"

"github.com/garyburd/redigo/redis"
"github.com/youtube/vitess/go/pools"
"golang.org/x/net/context"
)

// Datastore is an interface helper that allows mocking a test database
type Datastore interface {
ReadCounter() int64
IncrementCounter()
DecrementCounter()
FlushDB()
}

// DB is a wrapper over the connection pool
type DB struct {
*pools.ResourcePool
}

// ResourceConn adapts a Redigo connection to a Vitess Resource
type ResourceConn struct {
redis.Conn
}

// Close closes a connection owned by a Vitess Resource
func (r ResourceConn) Close() {
r.Conn.Close()
}

// NewPool creates a new Redis connection pool
func NewPool() *DB {
p := pools.NewResourcePool(func() (pools.Resource, error) {
c, err := redis.Dial("tcp", fmt.Sprintf("%s:6379", os.Getenv("REDIS_HOST")))
return ResourceConn{c}, err
}, 4, 100, time.Minute)
//defer p.Close()
return &DB{p}
}

// ReadCounter returns the current contents of the counter in the database
func (p *DB) ReadCounter() int64 {
ctx := context.TODO()
r, err := p.Get(ctx)
if err != nil {
log.Fatal(err)
}
defer p.Put(r)
c := r.(ResourceConn)
i64, err := redis.Int64(c.Do("GET", "counter"))
if err != nil {
fmt.Println(err)
}
return i64
}

// IncrementCounter increments the counter by 1
func (p *DB) IncrementCounter() {
ctx := context.TODO()
r, err := p.Get(ctx)
if err != nil {
log.Fatal(err)
}
defer p.Put(r)
c := r.(ResourceConn)
c.Send("MULTI")
c.Send("INCR", "counter")
//c.Send("EXEC")
_, err = c.Do("EXEC")
if err != nil {
fmt.Println(err)
}
}

// DecrementCounter decrements the counter by 1
func (p *DB) DecrementCounter() {
ctx := context.TODO()
r, err := p.Get(ctx)
if err != nil {
log.Fatal(err)
}
defer p.Put(r)
c := r.(ResourceConn)
c.Send("MULTI")
c.Send("DECR", "counter")
//c.Send("EXEC")
_, err = c.Do("EXEC")
if err != nil {
fmt.Println(err)
}
}

// FlushDB flushed the DB
func (p *DB) FlushDB() {
ctx := context.TODO()
r, err := p.Get(ctx)
if err != nil {
log.Fatal(err)
}
defer p.Put(r)
c := r.(ResourceConn)
c.Send("MULTI")
c.Send("FLUSHDB")
rep, err := c.Do("EXEC")
if err != nil {
fmt.Println(err)
}
fmt.Println(rep)
}
12 changes: 12 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
version: '2'
services:
counter:
environment:
- REDIS_HOST=redis
ports:
- 8080:8080
image: 'phedoreanu/counter:latest'
links:
- redis
redis:
image: 'redis:latest'
7 changes: 7 additions & 0 deletions load-tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/bash

if [ ! -d "$DIRECTORY" ]; then
go get -u github.com/tsenart/vegeta
fi

echo "GET http://${HOSTNAME}/get" | vegeta attack -duration=30s | tee results.bin | vegeta report
64 changes: 64 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package main

import (
"fmt"
"log"
"net/http"

"github.com/phedoreanu/counter/db"
"github.com/phedoreanu/counter/synced"
)

// Env is a dependency injection helper
type Env struct {
db db.Datastore
c chan int64
synced *synced.Synced
}

// GetHandler returns the number of hits to /get endpoint
func (env *Env) handleGet(w http.ResponseWriter, _ *http.Request) {
go func() {
env.c <- env.synced.Increment()
}()
fmt.Fprint(w, env.synced.Read())
}

// StatusHandler returns the current contents of the counter in the database
func (env *Env) handleStatus(w http.ResponseWriter, _ *http.Request) {
fmt.Fprint(w, env.db.ReadCounter())
}

// SyncCounter increments the counter in the database
// and every 10 requests to /get flips and decrements the counter
func (env *Env) SyncCounter(hits int64) {
switch env.synced.GetDirection() {
case true:
env.db.DecrementCounter()
case false:
env.db.IncrementCounter()
}
if hits%10 == 0 {
env.synced.Reverse()
}
}

func main() {
env := Env{
db: db.NewPool(),
c: make(chan int64),
synced: synced.NewSynced(),
}
env.db.FlushDB()

go func() {
for {
env.SyncCounter(<-env.c)
}
}()

http.HandleFunc("/get", env.handleGet)
http.HandleFunc("/status", env.handleStatus)

log.Fatal(http.ListenAndServe(":8080", nil))
}
Loading

0 comments on commit 39cf0a6

Please sign in to comment.