Skip to content

Commit

Permalink
Merge pull request Imgur#67 from Imgur/dev-redbtn-longpoll
Browse files Browse the repository at this point in the history
Add button to disable longpoll (via redis)
  • Loading branch information
jacobgreenleaf committed Sep 10, 2015
2 parents 3fc5765 + fc8bf19 commit bbef822
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 1 deletion.
1 change: 1 addition & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func NewConfig(configFilePath string) {
ConfigOption("datadog_host", "127.0.0.1")
}

ConfigOption("longpoll_killswitch", "longpoll_killswitch")
ConfigOption("redis_enabled", false)

if viper.GetBool("redis_enabled") {
Expand Down
3 changes: 3 additions & 0 deletions config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ datadog_enabled: false
# Datadog host if datadog enabled
datadog_host: "127.0.0.1"

# Redis key to monitor. If it exists, longpolling is disabled, for performance reasons.
longpoll_killswitch: "longpoll_killswitch"

# ----- Redis Support -----

# Bool; Redis must be enabled if running Incus in a cluster.
Expand Down
2 changes: 2 additions & 0 deletions incus/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ func main() {
go server.ListenFromRedis()
go server.ListenFromSockets()
go server.ListenFromLongpoll()
go server.MonitorLongpollKillswitch()

go server.ListenForHTTPPings()
go server.SendHeartbeatsPeriodically(20 * time.Second)

Expand Down
35 changes: 35 additions & 0 deletions redis_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"time"

"github.com/garyburd/redigo/redis"
"github.com/spf13/viper"
)

const ClientsKey = "SocketClients"
Expand Down Expand Up @@ -229,6 +230,40 @@ func (this *RedisStore) QueryIsUserActive(user string, nowTimestamp int64) (bool
}
}

func (this *RedisStore) GetIsLongpollKillswitchActive() (bool, error) {
killswitchKey := viper.Get("longpoll_killswitch")

result := this.redisPendingQueue.RunAsyncTimeout(5*time.Second, func(conn redis.Conn) (result interface{}, err error) {
return conn.Do("TTL", killswitchKey)
})

if result.Error == nil {
if result.Value.(int64) >= -1 {
return true, nil
} else {
return false, nil
}
}

return false, timedOut
}

func (this *RedisStore) ActivateLongpollKillswitch(seconds int64) error {
killswitchKey := viper.Get("longpoll_killswitch")

return this.redisPendingQueue.RunAsyncTimeout(5*time.Second, func(conn redis.Conn) (result interface{}, err error) {
return conn.Do("SETEX", killswitchKey, seconds, "1")
}).Error
}

func (this *RedisStore) DeactivateLongpollKillswitch() error {
killswitchKey := viper.Get("longpoll_killswitch")

return this.redisPendingQueue.RunAsyncTimeout(5*time.Second, func(conn redis.Conn) (result interface{}, err error) {
return conn.Do("DEL", killswitchKey)
}).Error
}

func (this *RedisStore) Publish(channel string, message string) {
publisher, err := this.GetConn()
if err != nil {
Expand Down
37 changes: 37 additions & 0 deletions redis_store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,3 +254,40 @@ func TestUserPresenceIsImmediatelyRemovedUponMarkingInactive(t *testing.T) {
t.Fatalf("Expected 'bazbar' to be inactive after affirmatively marking as inactive")
}
}

func TestKillswitch(t *testing.T) {
store := newTestRedisStore()

store.DeactivateLongpollKillswitch()

active, err := store.GetIsLongpollKillswitchActive()

if err != nil {
t.Fatalf("Unexpected error: %s", err.Error())
}
if active {
t.Fatalf("Expected precondition that killswitch is inactive")
}

store.ActivateLongpollKillswitch(3)

active, err = store.GetIsLongpollKillswitchActive()

if err != nil {
t.Fatalf("Unexpected error: %s", err.Error())
}
if !active {
t.Fatalf("Expected killswitch to be active")
}

time.Sleep(4 * time.Second)

active, err = store.GetIsLongpollKillswitchActive()

if err != nil {
t.Fatalf("Unexpected error: %s", err.Error())
}
if active {
t.Fatalf("Expected killswitch to be inactive")
}
}
31 changes: 30 additions & 1 deletion server.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"net/http"
"os"
"os/signal"
"sync/atomic"
"syscall"
"time"

Expand All @@ -31,6 +32,10 @@ const (
closeCodeUnexpectedError = 1011
)

var (
disableLongpoll atomic.Value
)

type GCMClient interface {
Send(*gcm.Message, int) (*gcm.Response, error)
}
Expand Down Expand Up @@ -162,13 +167,21 @@ func (this *Server) ListenFromLongpoll() {
}
}()

sock := newSocket(nil, w, this, "")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Cache-Control", "private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0")
w.Header().Set("Connection", "keep-alive")
//w.Header().Set("Content-Encoding", "gzip")

longpollIsDisabled := disableLongpoll.Load()
if longpollIsDisabled != nil && longpollIsDisabled.(bool) == true {
w.Header().Set("Connection", "close")
w.WriteHeader(503)
return
}

sock := newSocket(nil, w, this, "")

if DEBUG {
log.Printf("Long poll connected via \n")
}
Expand Down Expand Up @@ -316,3 +329,19 @@ func (this *Server) GetAPNSClient(build string) apns.APNSClient {
func (this *Server) GetGCMClient() GCMClient {
return this.gcmProvider()
}

func (this *Server) MonitorLongpollKillswitch() {
if !viper.GetBool("redis_enabled") {
return
}

for {
longpollSwitchedOff, err := this.Store.redis.GetIsLongpollKillswitchActive()

if err == nil {
disableLongpoll.Store(longpollSwitchedOff)
}

time.Sleep(5 * time.Second)
}
}

0 comments on commit bbef822

Please sign in to comment.