Skip to content

Commit

Permalink
test app for #107
Browse files Browse the repository at this point in the history
  • Loading branch information
tidwall committed Dec 28, 2016
1 parent b8a0f59 commit 3e3d364
Show file tree
Hide file tree
Showing 8 changed files with 653 additions and 49 deletions.
41 changes: 34 additions & 7 deletions controller/expire.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,15 @@ func (c *Controller) getExpires(key, id string) (at time.Time, ok bool) {
// It's runs through every item that has been marked as expires five times
// per second.
func (c *Controller) backgroundExpiring() {
const stop = 0
const delay = 1
const nodelay = 2
for {
ok := func() bool {
c.mu.Lock()
defer c.mu.Unlock()
op := func() int {
c.mu.RLock()
defer c.mu.RUnlock()
if c.stopBackgroundExpiring {
return false
return stop
}
// Only excute for leaders. Followers should ignore.
if c.config.FollowHost == "" {
Expand All @@ -70,28 +73,52 @@ func (c *Controller) backgroundExpiring() {
for id, at := range m {
if now.After(at) {
// issue a DEL command
c.mu.RUnlock()
c.mu.Lock()

// double check because locks were swapped
var del bool
if m2, ok := c.expires[key]; ok {
if at2, ok := m2[id]; ok {
if now.After(at2) {
del = true
}
}
}
if !del {
return nodelay
}
c.statsExpired++
msg := &server.Message{}
msg.Values = resp.MultiBulkValue("del", key, id).Array()
msg.Command = "del"
_, d, err := c.cmdDel(msg)
if err != nil {
c.mu.Unlock()
log.Fatal(err)
continue
}
if err := c.writeAOF(resp.ArrayValue(msg.Values), &d); err != nil {
c.mu.Unlock()
log.Fatal(err)
continue
}
c.mu.Unlock()
c.mu.RLock()
return nodelay
}
}
}
}
return true
return delay
}()
if !ok {
switch op {
case stop:
return
case delay:
time.Sleep(time.Millisecond * 100)
case nodelay:
time.Sleep(time.Microsecond)
}
time.Sleep(time.Second / 5)
}
}
97 changes: 55 additions & 42 deletions controller/fence.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
package controller

import (
"fmt"
"math"
"strconv"
"time"

"github.com/tidwall/gjson"
"github.com/tidwall/tile38/controller/glob"
"github.com/tidwall/tile38/controller/server"
"github.com/tidwall/tile38/geojson"
)

var tmfmt = "2006-01-02T15:04:05.999999999Z07:00"

// FenceMatch executes a fence match returns back json messages for fence detection.
func FenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, details *commandDetailsT) [][]byte {
overall := time.Now()
defer func() {
return
fmt.Printf(">> %v\n", time.Since(overall))
}()
msgs := fenceMatch(hookName, sw, fence, details)
if len(fence.accept) == 0 {
return msgs
Expand All @@ -26,58 +31,64 @@ func FenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, detai
}
return nmsgs
}
func appendJSONTimeFormat(b []byte, t time.Time) []byte {
b = append(b, '"')
b = t.AppendFormat(b, "2006-01-02T15:04:05.999999999Z07:00")
b = append(b, '"')
return b
}
func jsonTimeFormat(t time.Time) string {
var b []byte
b = appendJSONTimeFormat(b, t)
return string(b)
}

func fenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, details *commandDetailsT) [][]byte {
jshookName := jsonString(hookName)
jstime := jsonString(details.timestamp.Format(tmfmt))
pattern := fence.glob
if details.command == "drop" {
return [][]byte{[]byte(`{"command":"drop","hook":` + jshookName + `,"time":` + jstime + `}`)}
return [][]byte{[]byte(`{"command":"drop","hook":` + jsonString(hookName) + `,"time":` + jsonTimeFormat(details.timestamp) + `}`)}
}
match := true
if pattern != "" && pattern != "*" {
match, _ = glob.Match(pattern, details.id)
if len(fence.glob) > 0 && !(len(fence.glob) == 1 && fence.glob[0] == '*') {
match, _ := glob.Match(fence.glob, details.id)
if !match {
return nil
}
}
if !match {
if details.obj == nil || !details.obj.IsGeometry() {
return nil
}

sw.mu.Lock()
nofields := sw.nofields
sw.mu.Unlock()

if details.obj == nil || !details.obj.IsGeometry() || (details.command == "fset" && nofields) {
return nil
if details.command == "fset" {
sw.mu.Lock()
nofields := sw.nofields
sw.mu.Unlock()
if nofields {
return nil
}
}
if details.command == "del" {
return [][]byte{[]byte(`{"command":"del","hook":` + jsonString(hookName) + `,"id":` + jsonString(details.id) + `,"time":` + jsonTimeFormat(details.timestamp) + `}`)}
}
match = false

var roamkeys, roamids []string
var roammeters []float64
detect := "outside"
var detect string = "outside"
if fence != nil {
if fence.roam.on {
if details.command == "set" {
// println("roam", fence.roam.key, fence.roam.id, strconv.FormatFloat(fence.roam.meters, 'f', -1, 64))
roamkeys, roamids, roammeters = fenceMatchRoam(sw.c, fence, details.key, details.id, details.obj)
}
if len(roamids) == 0 || len(roamids) != len(roamkeys) {
return nil
}
match = true
detect = "roam"
} else {

// not using roaming
match1 := fenceMatchObject(fence, details.oldObj)
match2 := fenceMatchObject(fence, details.obj)
if match1 && match2 {
match = true
detect = "inside"
} else if match1 && !match2 {
match = true
detect = "exit"
} else if !match1 && match2 {
match = true
detect = "enter"
if details.command == "fset" {
detect = "inside"
Expand All @@ -101,7 +112,6 @@ func fenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, detai
temp = true
}
if fenceMatchObject(fence, ls) {
//match = true
detect = "cross"
}
if temp {
Expand All @@ -112,12 +122,14 @@ func fenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, detai
}
}
}
if details.command == "del" {
return [][]byte{[]byte(`{"command":"del","hook":` + jshookName + `,"id":` + jsonString(details.id) + `,"time":` + jstime + `}`)}
}

if details.fmap == nil {
return nil
}
if fence.detect != nil && !fence.detect[detect] {
return nil
}

sw.mu.Lock()
sw.fmap = details.fmap
sw.fullFields = true
Expand Down Expand Up @@ -159,23 +171,21 @@ func fenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, detai
}
}

jskey := jsonString(details.key)

msgs := make([][]byte, 0, 4)
if fence.detect == nil || fence.detect[detect] {
if len(res) > 0 && res[0] == '{' {
res = makemsg(details.command, group, detect, jshookName, jskey, jstime, res[1:])
res = makemsg(details.command, group, detect, hookName, details.key, details.timestamp, res[1:])
}
msgs = append(msgs, res)
}
switch detect {
case "enter":
if fence.detect == nil || fence.detect["inside"] {
msgs = append(msgs, makemsg(details.command, group, "inside", jshookName, jskey, jstime, res[1:]))
msgs = append(msgs, makemsg(details.command, group, "inside", hookName, details.key, details.timestamp, res[1:]))
}
case "exit", "cross":
if fence.detect == nil || fence.detect["outside"] {
msgs = append(msgs, makemsg(details.command, group, "outside", jshookName, jskey, jstime, res[1:]))
msgs = append(msgs, makemsg(details.command, group, "outside", hookName, details.key, details.timestamp, res[1:]))
}
case "roam":
if len(msgs) > 0 {
Expand All @@ -185,9 +195,9 @@ func fenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, detai

nmsg := append([]byte(nil), msg...)
nmsg = append(nmsg, `,"nearby":{"key":`...)
nmsg = append(nmsg, jsonString(roamkeys[i])...)
nmsg = appendJSONString(nmsg, roamkeys[i])
nmsg = append(nmsg, `,"id":`...)
nmsg = append(nmsg, jsonString(id)...)
nmsg = appendJSONString(nmsg, id)
nmsg = append(nmsg, `,"meters":`...)
nmsg = append(nmsg, strconv.FormatFloat(roammeters[i], 'f', -1, 64)...)

Expand Down Expand Up @@ -234,14 +244,14 @@ func fenceMatch(hookName string, sw *scanWriter, fence *liveFenceSwitches, detai
return msgs
}

func makemsg(command, group, detect, jshookName, jskey, jstime string, tail []byte) []byte {
func makemsg(command, group, detect, hookName string, key string, t time.Time, tail []byte) []byte {
var buf []byte
buf = append(append(buf, `{"command":"`...), command...)
buf = append(append(buf, `","group":"`...), group...)
buf = append(append(buf, `","detect":"`...), detect...)
buf = append(append(buf, `","hook":`...), jshookName...)
buf = append(append(buf, `,"key":`...), jskey...)
buf = append(append(buf, `,"time":`...), jstime...)
buf = appendJSONString(append(buf, `","hook":`...), hookName)
buf = appendJSONString(append(buf, `,"key":`...), key)
buf = appendJSONTimeFormat(append(buf, `,"time":`...), t)
buf = append(append(buf, ','), tail...)
return buf
}
Expand All @@ -254,17 +264,20 @@ func fenceMatchObject(fence *liveFenceSwitches, obj geojson.Object) bool {
// we need to check this object against
return false
}

if fence.cmd == "nearby" {
return obj.Nearby(geojson.Position{X: fence.lon, Y: fence.lat, Z: 0}, fence.meters)
} else if fence.cmd == "within" {
}
if fence.cmd == "within" {
if fence.o != nil {
return obj.Within(fence.o)
}
return obj.WithinBBox(geojson.BBox{
Min: geojson.Position{X: fence.minLon, Y: fence.minLat, Z: 0},
Max: geojson.Position{X: fence.maxLon, Y: fence.maxLat, Z: 0},
})
} else if fence.cmd == "intersects" {
}
if fence.cmd == "intersects" {
if fence.o != nil {
return obj.Intersects(fence.o)
}
Expand Down
13 changes: 13 additions & 0 deletions controller/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@ import (
"github.com/tidwall/tile38/geojson"
)

func appendJSONString(b []byte, s string) []byte {
for i := 0; i < len(s); i++ {
if s[i] < ' ' || s[i] == '\\' || s[i] == '"' || s[i] > 126 {
d, _ := json.Marshal(s)
return append(b, string(d)...)
}
}
b = append(b, '"')
b = append(b, s...)
b = append(b, '"')
return b
}

func jsonString(s string) string {
for i := 0; i < len(s); i++ {
if s[i] < ' ' || s[i] == '\\' || s[i] == '"' || s[i] > 126 {
Expand Down
3 changes: 3 additions & 0 deletions tests/107/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
appendonly.aof
log
data/
1 change: 1 addition & 0 deletions tests/107/LINK
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
https://github.com/tidwall/tile38/issues/107
Loading

0 comments on commit 3e3d364

Please sign in to comment.