tidwall 6257ddba78 Faster point in polygon / GeoJSON updates
The big change is that the GeoJSON package has been completely
rewritten to fix a few of geometry calculation bugs, increase
performance, and to better follow the GeoJSON spec RFC 7946.

GeoJSON updates

- A LineString now requires at least two points.
- All json members, even foreign, now persist with the object.
- The bbox member persists too but is no longer used for geometry
  calculations. This is change in behavior. Previously Tile38 would
  treat the bbox as the object's physical rectangle.
- Corrections to geometry intersects and within calculations.

Faster spatial queries

- The performance of Point-in-polygon and object intersect operations
  are greatly improved for complex polygons and line strings. It went
  from O(n) to roughly O(log n).
- The same for all collection types with many children, including
  FeatureCollection, GeometryCollection, MultiPoint, MultiLineString,
  and MultiPolygon.

Codebase changes

- The pkg directory has been renamed to internal
- The GeoJSON internal package has been moved to a seperate repo at
  https://github.com/tidwall/geojson. It's now vendored.

Please look out for higher memory usage for datasets using complex
shapes. A complex shape is one that has 64 or more points. For these
shapes it's expected that there will be increase of least 54 bytes per
point.
2018-10-13 04:30:48 -07:00

369 lines
7.7 KiB
Go

// Copyright 2013-2018 The NATS Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package test
import (
"fmt"
"runtime"
"testing"
"time"
"github.com/nats-io/go-nats"
)
func TestBadChan(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
if err := ec.BindSendChan("foo", "not a chan"); err == nil {
t.Fatalf("Expected an Error when sending a non-channel\n")
}
if _, err := ec.BindRecvChan("foo", "not a chan"); err == nil {
t.Fatalf("Expected an Error when sending a non-channel\n")
}
if err := ec.BindSendChan("foo", "not a chan"); err != nats.ErrChanArg {
t.Fatalf("Expected an ErrChanArg when sending a non-channel\n")
}
if _, err := ec.BindRecvChan("foo", "not a chan"); err != nats.ErrChanArg {
t.Fatalf("Expected an ErrChanArg when sending a non-channel\n")
}
}
func TestSimpleSendChan(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
recv := make(chan bool)
numSent := int32(22)
ch := make(chan int32)
if err := ec.BindSendChan("foo", ch); err != nil {
t.Fatalf("Failed to bind to a send channel: %v\n", err)
}
ec.Subscribe("foo", func(num int32) {
if num != numSent {
t.Fatalf("Failed to receive correct value: %d vs %d\n", num, numSent)
}
recv <- true
})
// Send to 'foo'
ch <- numSent
if e := Wait(recv); e != nil {
if ec.LastError() != nil {
e = ec.LastError()
}
t.Fatalf("Did not receive the message: %s", e)
}
close(ch)
}
func TestFailedChannelSend(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
nc := ec.Conn
ch := make(chan bool)
wch := make(chan bool)
nc.Opts.AsyncErrorCB = func(c *nats.Conn, s *nats.Subscription, e error) {
wch <- true
}
if err := ec.BindSendChan("foo", ch); err != nil {
t.Fatalf("Failed to bind to a receive channel: %v\n", err)
}
nc.Flush()
go func() {
time.Sleep(100 * time.Millisecond)
nc.Close()
}()
func() {
for {
select {
case ch <- true:
case <-wch:
return
case <-time.After(time.Second):
t.Fatal("Failed to get async error cb")
}
}
}()
ec = NewEConn(t)
defer ec.Close()
nc = ec.Conn
bch := make(chan []byte)
nc.Opts.AsyncErrorCB = func(c *nats.Conn, s *nats.Subscription, e error) {
wch <- true
}
if err := ec.BindSendChan("foo", bch); err != nil {
t.Fatalf("Failed to bind to a receive channel: %v\n", err)
}
buf := make([]byte, 2*1024*1024)
bch <- buf
if e := Wait(wch); e != nil {
t.Fatal("Failed to call async err handler")
}
}
func TestSimpleRecvChan(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
numSent := int32(22)
ch := make(chan int32)
if _, err := ec.BindRecvChan("foo", ch); err != nil {
t.Fatalf("Failed to bind to a receive channel: %v\n", err)
}
ec.Publish("foo", numSent)
// Receive from 'foo'
select {
case num := <-ch:
if num != numSent {
t.Fatalf("Failed to receive correct value: %d vs %d\n", num, numSent)
}
case <-time.After(1 * time.Second):
t.Fatalf("Failed to receive a value, timed-out\n")
}
close(ch)
}
func TestQueueRecvChan(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
numSent := int32(22)
ch := make(chan int32)
if _, err := ec.BindRecvQueueChan("foo", "bar", ch); err != nil {
t.Fatalf("Failed to bind to a queue receive channel: %v\n", err)
}
ec.Publish("foo", numSent)
// Receive from 'foo'
select {
case num := <-ch:
if num != numSent {
t.Fatalf("Failed to receive correct value: %d vs %d\n", num, numSent)
}
case <-time.After(1 * time.Second):
t.Fatalf("Failed to receive a value, timed-out\n")
}
close(ch)
}
func TestDecoderErrRecvChan(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
nc := ec.Conn
wch := make(chan bool)
nc.Opts.AsyncErrorCB = func(c *nats.Conn, s *nats.Subscription, e error) {
wch <- true
}
ch := make(chan *int32)
if _, err := ec.BindRecvChan("foo", ch); err != nil {
t.Fatalf("Failed to bind to a send channel: %v\n", err)
}
ec.Publish("foo", "Hello World")
if e := Wait(wch); e != nil {
t.Fatal("Failed to call async err handler")
}
}
func TestRecvChanPanicOnClosedChan(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
ch := make(chan int)
if _, err := ec.BindRecvChan("foo", ch); err != nil {
t.Fatalf("Failed to bind to a send channel: %v\n", err)
}
close(ch)
ec.Publish("foo", 22)
ec.Flush()
}
func TestRecvChanAsyncLeakGoRoutines(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
// Call this to make sure that we have everything setup connection wise
ec.Flush()
before := runtime.NumGoroutine()
ch := make(chan int)
if _, err := ec.BindRecvChan("foo", ch); err != nil {
t.Fatalf("Failed to bind to a send channel: %v\n", err)
}
// Close the receive Channel
close(ch)
// The publish will trigger the close and shutdown of the Go routines
ec.Publish("foo", 22)
ec.Flush()
waitFor(t, 2*time.Second, 100*time.Millisecond, func() error {
delta := (runtime.NumGoroutine() - before)
if delta > 0 {
return fmt.Errorf("Leaked Go routine(s) : %d, closing channel should have closed them", delta)
}
return nil
})
}
func TestRecvChanLeakGoRoutines(t *testing.T) {
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
// Call this to make sure that we have everything setup connection wise
ec.Flush()
before := runtime.NumGoroutine()
ch := make(chan int)
sub, err := ec.BindRecvChan("foo", ch)
if err != nil {
t.Fatalf("Failed to bind to a send channel: %v\n", err)
}
sub.Unsubscribe()
waitFor(t, 2*time.Second, 100*time.Millisecond, func() error {
delta := (runtime.NumGoroutine() - before)
if delta > 0 {
return fmt.Errorf("Leaked Go routine(s) : %d, closing channel should have closed them", delta)
}
return nil
})
}
func TestRecvChanMultipleMessages(t *testing.T) {
// Make sure we can receive more than one message.
// In response to #25, which is a bug from fixing #22.
s := RunDefaultServer()
defer s.Shutdown()
ec := NewEConn(t)
defer ec.Close()
// Num to send, should == len of messages queued.
size := 10
ch := make(chan int, size)
if _, err := ec.BindRecvChan("foo", ch); err != nil {
t.Fatalf("Failed to bind to a send channel: %v\n", err)
}
for i := 0; i < size; i++ {
ec.Publish("foo", 22)
}
ec.Flush()
time.Sleep(10 * time.Millisecond)
if lch := len(ch); lch != size {
t.Fatalf("Expected %d messages queued, got %d.", size, lch)
}
}
func BenchmarkPublishSpeedViaChan(b *testing.B) {
b.StopTimer()
s := RunDefaultServer()
defer s.Shutdown()
nc, err := nats.Connect(nats.DefaultURL)
if err != nil {
b.Fatalf("Could not connect: %v\n", err)
}
ec, err := nats.NewEncodedConn(nc, nats.DEFAULT_ENCODER)
if err != nil {
b.Fatalf("Failed creating encoded connection: %v\n", err)
}
defer ec.Close()
ch := make(chan int32, 1024)
if err := ec.BindSendChan("foo", ch); err != nil {
b.Fatalf("Failed to bind to a send channel: %v\n", err)
}
b.StartTimer()
num := int32(22)
for i := 0; i < b.N; i++ {
ch <- num
}
// Make sure they are all processed.
nc.Flush()
b.StopTimer()
}