Skip to content

Commit da159e5

Browse files
committed
Add zone sync endpoint to stats
1 parent 9fb6bf1 commit da159e5

File tree

5 files changed

+155
-4
lines changed

5 files changed

+155
-4
lines changed

Makefile

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ docker-build:
1010
docker build --build-arg NGINX_PLUS_VERSION=$(NGINX_PLUS_VERSION)~stretch -t $(NGINX_IMAGE) docker
1111

1212
run-nginx-plus:
13-
docker run -d --name nginx-plus-test --rm -p 8080:8080 -p 8081:8081 $(NGINX_IMAGE)
13+
docker network create --driver bridge test
14+
docker run --network=test -d --name nginx-plus-test --network-alias=nginx-plus-test --rm -p 8080:8080 -p 8081:8081 $(NGINX_IMAGE)
15+
docker run --network=test -d --name nginx-plus-test-helper --network-alias=nginx-plus-test --rm -p 8090:8080 -p 8091:8081 $(NGINX_IMAGE)
1416

1517
test-run:
1618
go test client/*
@@ -26,4 +28,6 @@ test-run-no-stream-block:
2628
go test tests/client_no_stream_test.go
2729

2830
clean:
29-
docker kill nginx-plus-test
31+
-docker kill nginx-plus-test
32+
-docker kill nginx-plus-test-helper
33+
-docker network rm test

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ Run Tests:
4040
$ make test
4141
```
4242

43-
This will build and run an NGINX Plus container, execute the client tests against NGINX Plus API, and then clean up. If it fails and you want to clean up (i.e. stop the running container), please use `$ make clean`
43+
This will build and run two NGINX Plus containers and create one docker network of type bridge, execute the client tests against both NGINX Plus APIs, and then clean up. If it fails and you want to clean up (i.e. stop the running containers and remove the docker network), please use `$ make clean`
4444

4545
## Support
4646
This project is not covered by the NGINX Plus support contract.

client/nginx.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ type Stats struct {
8888
Upstreams Upstreams
8989
StreamServerZones StreamServerZones
9090
StreamUpstreams StreamUpstreams
91+
StreamZoneSync StreamZoneSync
9192
}
9293

9394
// NginxInfo contains general information about NGINX Plus.
@@ -149,6 +150,27 @@ type StreamServerZone struct {
149150
Sent uint64
150151
}
151152

153+
// StreamZoneSync represents the sync information per each shared memory zone and the sync information per node in a cluster
154+
type StreamZoneSync struct {
155+
Zones map[string]SyncZone
156+
Status StreamZoneSyncStatus
157+
}
158+
159+
// SyncZone represents the syncronization status of a shared memory zone
160+
type SyncZone struct {
161+
RecordsPending uint64 `json:"records_pending"`
162+
RecordsTotal uint64 `json:"records_total"`
163+
}
164+
165+
// StreamZoneSyncStatus represents the status of a shared memory zone
166+
type StreamZoneSyncStatus struct {
167+
BytesIn uint64 `json:"bytes_in"`
168+
MsgsIn uint64 `json:"msgs_in"`
169+
MsgsOut uint64 `json:"msgs_out"`
170+
BytesOut uint64 `json:"bytes_out"`
171+
NodesOnline uint64 `json:"nodes_online"`
172+
}
173+
152174
// Responses represents HTTP response related stats.
153175
type Responses struct {
154176
Responses1xx uint64 `json:"1xx"`
@@ -728,6 +750,11 @@ func (client *NginxClient) GetStats() (*Stats, error) {
728750
return nil, fmt.Errorf("failed to get stats: %v", err)
729751
}
730752

753+
streamZoneSync, err := client.getStreamZoneSync()
754+
if err != nil {
755+
return nil, fmt.Errorf("failed to get stats: %v", err)
756+
}
757+
731758
return &Stats{
732759
NginxInfo: *info,
733760
Connections: *cons,
@@ -737,6 +764,7 @@ func (client *NginxClient) GetStats() (*Stats, error) {
737764
StreamServerZones: *streamZones,
738765
Upstreams: *upstreams,
739766
StreamUpstreams: *streamUpstreams,
767+
StreamZoneSync: *streamZoneSync,
740768
}, nil
741769
}
742770

@@ -822,6 +850,22 @@ func (client *NginxClient) getStreamUpstreams() (*StreamUpstreams, error) {
822850
return &upstreams, nil
823851
}
824852

853+
func (client *NginxClient) getStreamZoneSync() (*StreamZoneSync, error) {
854+
var streamZoneSync StreamZoneSync
855+
err := client.get("stream/zone_sync", &streamZoneSync)
856+
if err != nil {
857+
if err, ok := err.(*internalError); ok {
858+
859+
if err.Code == pathNotFoundCode {
860+
return &streamZoneSync, nil
861+
}
862+
}
863+
return nil, fmt.Errorf("failed to get stream zone sync: %v", err)
864+
}
865+
866+
return &streamZoneSync, err
867+
}
868+
825869
// KeyValPairs are the key-value pairs stored in a zone.
826870
type KeyValPairs map[string]string
827871

docker/nginx.conf

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ http {
3737
stream {
3838
keyval_zone zone=zone_one_stream:32k;
3939
keyval $hostname $text zone=zone_one_stream;
40+
keyval_zone zone=zone_test_sync:32k timeout=5s sync;
4041

4142
upstream stream_test {
4243
zone stream_test 64k;
@@ -49,4 +50,12 @@ stream {
4950
health_check interval=10 fails=3 passes=1;
5051
}
5152

53+
resolver 127.0.0.11 valid=5s;
54+
55+
server {
56+
listen 7777;
57+
58+
zone_sync;
59+
zone_sync_server nginx-plus-test:7777 resolve;
60+
}
5261
}

tests/client_test.go

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
const (
1414
upstream = "test"
1515
streamUpstream = "stream_test"
16+
streamZoneSync = "zone_test_sync"
1617
)
1718

1819
func TestStreamClient(t *testing.T) {
@@ -673,7 +674,8 @@ func TestKeyValueStream(t *testing.T) {
673674
t.Errorf("Couldn't get keyvals, %v", err)
674675
}
675676
expectedKeyValuePairsByZone := client.KeyValPairsByZone{
676-
zoneName: expectedKeyValPairs,
677+
zoneName: expectedKeyValPairs,
678+
streamZoneSync: client.KeyValPairs{},
677679
}
678680
if !reflect.DeepEqual(expectedKeyValuePairsByZone, keyValPairsByZone) {
679681
t.Errorf("maps are not equal. expected: %+v, got: %+v", expectedKeyValuePairsByZone, keyValPairsByZone)
@@ -741,6 +743,98 @@ func TestKeyValueStream(t *testing.T) {
741743
}
742744
}
743745

746+
func TestStreamZoneSync(t *testing.T) {
747+
c1, err := client.NewNginxClient(&http.Client{}, "http://127.0.0.1:8080/api")
748+
if err != nil {
749+
t.Fatalf("Error connecting to nginx: %v", err)
750+
}
751+
752+
c2, err := client.NewNginxClient(&http.Client{}, "http://127.0.0.1:8090/api")
753+
if err != nil {
754+
t.Fatalf("Error connecting to nginx: %v", err)
755+
}
756+
757+
err = c1.AddStreamKeyValPair(streamZoneSync, "key1", "val1")
758+
if err != nil {
759+
t.Errorf("Couldn't set keyvals: %v", err)
760+
}
761+
762+
// wait for nodes to sync information of synced zones
763+
time.Sleep(5 * time.Second)
764+
765+
statsC1, err := c1.GetStats()
766+
if err != nil {
767+
t.Errorf("Error getting stats: %v", err)
768+
}
769+
770+
if statsC1.StreamZoneSync.Status.NodesOnline == 0 {
771+
t.Errorf("At least 1 node must be online")
772+
}
773+
774+
if statsC1.StreamZoneSync.Status.MsgsOut == 0 {
775+
t.Errorf("Msgs out cannot be 0")
776+
}
777+
778+
if statsC1.StreamZoneSync.Status.MsgsIn == 0 {
779+
t.Errorf("Msgs in cannot be 0")
780+
}
781+
782+
if statsC1.StreamZoneSync.Status.BytesIn == 0 {
783+
t.Errorf("Bytes in cannot be 0")
784+
}
785+
786+
if statsC1.StreamZoneSync.Status.BytesOut == 0 {
787+
t.Errorf("Bytes Out cannot be 0")
788+
}
789+
790+
if zone, ok := statsC1.StreamZoneSync.Zones[streamZoneSync]; ok {
791+
if zone.RecordsTotal == 0 {
792+
t.Errorf("Total records cannot be 0 after adding keyvals")
793+
}
794+
if zone.RecordsPending != 0 {
795+
t.Errorf("Pending records must be 0 after adding keyvals")
796+
}
797+
} else {
798+
t.Errorf("Sync zone %v missing in stats", streamZoneSync)
799+
}
800+
801+
statsC2, err := c2.GetStats()
802+
if err != nil {
803+
t.Errorf("Error getting stats: %v", err)
804+
}
805+
806+
if statsC2.StreamZoneSync.Status.NodesOnline == 0 {
807+
t.Errorf("At least 1 node must be online")
808+
}
809+
810+
if statsC2.StreamZoneSync.Status.MsgsOut != 0 {
811+
t.Errorf("Msgs out must be 0")
812+
}
813+
814+
if statsC2.StreamZoneSync.Status.MsgsIn == 0 {
815+
t.Errorf("Msgs in cannot be 0")
816+
}
817+
818+
if statsC2.StreamZoneSync.Status.BytesIn == 0 {
819+
t.Errorf("Bytes in cannot be 0")
820+
}
821+
822+
if statsC2.StreamZoneSync.Status.BytesOut != 0 {
823+
t.Errorf("Bytes out must be 0")
824+
}
825+
826+
if zone, ok := statsC2.StreamZoneSync.Zones[streamZoneSync]; ok {
827+
if zone.RecordsTotal == 0 {
828+
t.Errorf("Total records cannot be 0 after adding keyvals")
829+
}
830+
if zone.RecordsPending != 0 {
831+
t.Errorf("Pending records must be 0 after adding keyvals")
832+
}
833+
} else {
834+
t.Errorf("Sync zone %v missing in stats", streamZoneSync)
835+
}
836+
}
837+
744838
func compareUpstreamServers(x []client.UpstreamServer, y []client.UpstreamServer) bool {
745839
var xServers []string
746840
for _, us := range x {

0 commit comments

Comments
 (0)