@@ -54,6 +54,8 @@ const (
54
54
// LogUnexpectedResultId is logged when response with unknown id was received.
55
55
// Most probably it is due to request timeout.
56
56
LogUnexpectedResultId
57
+ // LogReadWatchEventFailed is logged when failed to read a watch event.
58
+ LogReadWatchEventFailed
57
59
)
58
60
59
61
// ConnEvent is sent throw Notify channel specified in Opts.
@@ -63,6 +65,12 @@ type ConnEvent struct {
63
65
When time.Time
64
66
}
65
67
68
+ // A raw watch event.
69
+ type connWatchEvent struct {
70
+ key string
71
+ value interface {}
72
+ }
73
+
66
74
var epoch = time .Now ()
67
75
68
76
// Logger is logger type expected to be passed in options.
@@ -84,6 +92,9 @@ func (d defaultLogger) Report(event ConnLogKind, conn *Connection, v ...interfac
84
92
case LogUnexpectedResultId :
85
93
resp := v [0 ].(* Response )
86
94
log .Printf ("tarantool: connection %s got unexpected resultId (%d) in response" , conn .addr , resp .RequestId )
95
+ case LogReadWatchEventFailed :
96
+ err := v [0 ].(error )
97
+ log .Printf ("tarantool: unable to parse watch event: %s" , err )
87
98
default :
88
99
args := append ([]interface {}{"tarantool: unexpected event " , event , conn }, v ... )
89
100
log .Print (args ... )
@@ -149,6 +160,8 @@ type Connection struct {
149
160
lastStreamId uint64
150
161
151
162
serverProtocolInfo ProtocolInfo
163
+ // watchMap is a map of key -> watchSharedData.
164
+ watchMap sync.Map
152
165
}
153
166
154
167
var _ = Connector (& Connection {}) // Check compatibility with connector interface.
@@ -531,7 +544,7 @@ func (conn *Connection) dial() (err error) {
531
544
return fmt .Errorf ("identify: %w" , err )
532
545
}
533
546
534
- // Auth
547
+ // Auth.
535
548
if opts .User != "" {
536
549
scr , err := scramble (conn .Greeting .auth , opts .Pass )
537
550
if err != nil {
@@ -549,7 +562,34 @@ func (conn *Connection) dial() (err error) {
549
562
}
550
563
}
551
564
552
- // Only if connected and authenticated.
565
+ // Watchers.
566
+ watchersChecked := false
567
+ conn .watchMap .Range (func (key , value interface {}) bool {
568
+ if ! watchersChecked {
569
+
570
+ watchersChecked = true
571
+ }
572
+
573
+ st := value .(chan watchState )
574
+ state := <- st
575
+ if state .cnt > 0 {
576
+ req := newWatchRequest (key .(string ))
577
+ if err = conn .writeRequest (w , req ); err != nil {
578
+ st <- state
579
+ return false
580
+ }
581
+ state .init = true
582
+ state .ack = true
583
+ }
584
+ st <- state
585
+ return true
586
+ })
587
+
588
+ if err != nil {
589
+ return fmt .Errorf ("unable to register watch: %w" , err )
590
+ }
591
+
592
+ // Only if connected and fully initialized.
553
593
conn .lockShards ()
554
594
conn .c = connection
555
595
atomic .StoreUint32 (& conn .state , connConnected )
@@ -843,7 +883,50 @@ func (conn *Connection) writer(w *bufio.Writer, c net.Conn) {
843
883
}
844
884
}
845
885
886
+ func readWatchEvent (reader io.Reader ) (connWatchEvent , error ) {
887
+ keyExist := false
888
+ event := connWatchEvent {}
889
+ d := newDecoder (reader )
890
+
891
+ if l , err := d .DecodeMapLen (); err == nil {
892
+ for ; l > 0 ; l -- {
893
+ if cd , err := d .DecodeInt (); err == nil {
894
+ switch cd {
895
+ case KeyEvent :
896
+ if event .key , err = d .DecodeString (); err != nil {
897
+ return event , err
898
+ }
899
+ keyExist = true
900
+ case KeyEventData :
901
+ if event .value , err = d .DecodeInterface (); err != nil {
902
+ return event , err
903
+ }
904
+ default :
905
+ if err = d .Skip (); err != nil {
906
+ return event , err
907
+ }
908
+ }
909
+ } else {
910
+ return event , err
911
+ }
912
+ }
913
+ } else {
914
+ return event , err
915
+ }
916
+
917
+ if ! keyExist {
918
+ return event , errors .New ("watch event does not have a key" )
919
+ }
920
+
921
+ return event , nil
922
+ }
923
+
846
924
func (conn * Connection ) reader (r * bufio.Reader , c net.Conn ) {
925
+ events := make (chan connWatchEvent , 1024 )
926
+ defer close (events )
927
+
928
+ go conn .eventer (events )
929
+
847
930
for atomic .LoadUint32 (& conn .state ) != connClosed {
848
931
respBytes , err := conn .read (r )
849
932
if err != nil {
@@ -858,7 +941,14 @@ func (conn *Connection) reader(r *bufio.Reader, c net.Conn) {
858
941
}
859
942
860
943
var fut * Future = nil
861
- if resp .Code == PushCode {
944
+ if resp .Code == EventCode {
945
+ if event , err := readWatchEvent (& resp .buf ); err == nil {
946
+ events <- event
947
+ } else {
948
+ conn .opts .Logger .Report (LogReadWatchEventFailed , conn , err )
949
+ }
950
+ continue
951
+ } else if resp .Code == PushCode {
862
952
if fut = conn .peekFuture (resp .RequestId ); fut != nil {
863
953
fut .AppendPush (resp )
864
954
}
@@ -868,12 +958,37 @@ func (conn *Connection) reader(r *bufio.Reader, c net.Conn) {
868
958
conn .markDone (fut )
869
959
}
870
960
}
961
+
871
962
if fut == nil {
872
963
conn .opts .Logger .Report (LogUnexpectedResultId , conn , resp )
873
964
}
874
965
}
875
966
}
876
967
968
+ // eventer goroutine gets watch events and updates values for watchers.
969
+ func (conn * Connection ) eventer (events <- chan connWatchEvent ) {
970
+ for {
971
+ event , ok := <- events
972
+ if ! ok {
973
+ // The channel is closed.
974
+ break
975
+ }
976
+
977
+ if value , ok := conn .watchMap .Load (event .key ); ok {
978
+ st := value .(chan watchState )
979
+ state := <- st
980
+ state .value = event .value
981
+ state .init = false
982
+ state .ack = false
983
+ if state .changed != nil {
984
+ close (state .changed )
985
+ state .changed = nil
986
+ }
987
+ st <- state
988
+ }
989
+ }
990
+ }
991
+
877
992
func (conn * Connection ) newFuture (ctx context.Context ) (fut * Future ) {
878
993
fut = NewFuture ()
879
994
if conn .rlimit != nil && conn .opts .RLimitAction == RLimitDrop {
@@ -1029,6 +1144,18 @@ func (conn *Connection) putFuture(fut *Future, req Request, streamId uint64) {
1029
1144
return
1030
1145
}
1031
1146
shard .bufmut .Unlock ()
1147
+
1148
+ if req .Async () {
1149
+ if fut = conn .fetchFuture (reqid ); fut != nil {
1150
+ resp := & Response {
1151
+ RequestId : reqid ,
1152
+ Code : OkCode ,
1153
+ }
1154
+ fut .SetResponse (resp )
1155
+ conn .markDone (fut )
1156
+ }
1157
+ }
1158
+
1032
1159
if firstWritten {
1033
1160
conn .dirtyShard <- shardn
1034
1161
}
@@ -1233,6 +1360,167 @@ func (conn *Connection) NewStream() (*Stream, error) {
1233
1360
}, nil
1234
1361
}
1235
1362
1363
+ // watchState is the current state of the watcher. See the idea at p. 70, 105:
1364
+ // https://drive.google.com/file/d/1nPdvhB0PutEJzdCq5ms6UI58dp50fcAN/view
1365
+ type watchState struct {
1366
+ // value is a current value.
1367
+ value interface {}
1368
+ // init is true if it is an initial state (no events received).
1369
+ init bool
1370
+ // ack true if the acknowledge is already sended.
1371
+ ack bool
1372
+ // cnt is a count of active watchers for the key.
1373
+ cnt int
1374
+ // changed is a channel for broadcast the value changes.
1375
+ changed chan struct {}
1376
+ }
1377
+
1378
+ // connWatcher is an internal implementation of the Watcher interface.
1379
+ type connWatcher struct {
1380
+ unregister sync.Once
1381
+ done chan struct {}
1382
+ finished chan struct {}
1383
+ }
1384
+
1385
+ // Unregister unregisters the connection watcher.
1386
+ func (w * connWatcher ) Unregister () {
1387
+ w .unregister .Do (func () {
1388
+ close (w .done )
1389
+ })
1390
+ <- w .finished
1391
+ }
1392
+
1393
+ // NewWatcher creates a new Watcher object for the connection.
1394
+ //
1395
+ // After watcher creation, the watcher callback is invoked for the first time.
1396
+ // In this case, the callback is triggered whether or not the key has already
1397
+ // been broadcast. All subsequent invocations are triggered with
1398
+ // box.broadcast() called on the remote host. If a watcher is subscribed for a
1399
+ // key that has not been broadcast yet, the callback is triggered only once,
1400
+ // after the registration of the watcher.
1401
+ //
1402
+ // The watcher callbacks are always invoked in a separate goroutine. A watcher
1403
+ // callback is never executed in parallel with itself, but they can be executed
1404
+ // in parallel to other watchers.
1405
+ //
1406
+ // If the key is updated while the watcher callback is running, the callback
1407
+ // will be invoked again with the latest value as soon as it returns.
1408
+ //
1409
+ // Watchers survive reconnection. All registered watchers are automatically
1410
+ // resubscribed when the connection is reestablished.
1411
+ //
1412
+ // Keep in mind that garbage collection of a watcher handle doesn’t lead to the
1413
+ // watcher’s destruction. In this case, the watcher remains registered. You
1414
+ // need to call Unregister() directly.
1415
+ //
1416
+ // Unregister() guarantees that there will be no the watcher's callback calls
1417
+ // after it, but Unregister() call from the callback leads to a deadlock.
1418
+ //
1419
+ // See:
1420
+ // https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_events/#box-watchers
1421
+ //
1422
+ // Since 1.10.0
1423
+ func (conn * Connection ) NewWatcher (key string , callback WatchCallback ) (Watcher , error ) {
1424
+ watchersSupported := false
1425
+ for _ , feature := range conn .ServerProtocolInfo ().Features {
1426
+ if feature == WatchersFeature {
1427
+ watchersSupported = true
1428
+ break
1429
+ }
1430
+ }
1431
+ if ! watchersSupported {
1432
+ return nil , errors .New ("watchers does not supported by a server" )
1433
+ }
1434
+
1435
+ var st chan watchState
1436
+ // Get or create a shared data for the key.
1437
+ if val , ok := conn .watchMap .Load (key ); ! ok {
1438
+ st = make (chan watchState , 1 )
1439
+ st <- watchState {
1440
+ value : nil ,
1441
+ init : true ,
1442
+ ack : false ,
1443
+ cnt : 0 ,
1444
+ changed : nil ,
1445
+ }
1446
+
1447
+ if val , ok := conn .watchMap .LoadOrStore (key , st ); ok {
1448
+ close (st )
1449
+ st = val .(chan watchState )
1450
+ }
1451
+ } else {
1452
+ st = val .(chan watchState )
1453
+ }
1454
+
1455
+ state := <- st
1456
+ // Send an initial watch request if needed.
1457
+ if state .cnt == 0 {
1458
+ if _ , err := conn .Do (newWatchRequest (key )).Get (); err != nil {
1459
+ st <- state
1460
+ return nil , err
1461
+ }
1462
+ state .init = true
1463
+ state .ack = true
1464
+ }
1465
+ state .cnt += 1
1466
+ st <- state
1467
+
1468
+ // Start the watcher goroutine.
1469
+ done := make (chan struct {})
1470
+ finished := make (chan struct {})
1471
+
1472
+ go func () {
1473
+ for {
1474
+ state := <- st
1475
+ if state .changed == nil {
1476
+ state .changed = make (chan struct {})
1477
+ }
1478
+ st <- state
1479
+
1480
+ if ! state .init {
1481
+ callback (WatchEvent {
1482
+ Conn : conn ,
1483
+ Key : key ,
1484
+ Value : state .value ,
1485
+ })
1486
+
1487
+ // Acknowledge the notification.
1488
+ state = <- st
1489
+ ack := state .ack
1490
+ state .ack = true
1491
+ st <- state
1492
+
1493
+ if ! ack {
1494
+ conn .Do (newWatchRequest (key )).Get ()
1495
+ // We expect a reconnect and re-subscribe if it fails to
1496
+ // send the watch request. So it looks ok do not check a
1497
+ // result.
1498
+ }
1499
+ }
1500
+
1501
+ select {
1502
+ case <- done :
1503
+ state := <- st
1504
+ state .cnt -= 1
1505
+ if state .cnt == 0 {
1506
+ // The last one sends IPROTO_UNWATCH.
1507
+ conn .Do (newUnwatchRequest (key )).Get ()
1508
+ }
1509
+ st <- state
1510
+
1511
+ close (finished )
1512
+ return
1513
+ case <- state .changed :
1514
+ }
1515
+ }
1516
+ }()
1517
+
1518
+ return & connWatcher {
1519
+ done : done ,
1520
+ finished : finished ,
1521
+ }, nil
1522
+ }
1523
+
1236
1524
// checkProtocolInfo checks that expected protocol version is
1237
1525
// and protocol features are supported.
1238
1526
func checkProtocolInfo (expected ProtocolInfo , actual ProtocolInfo ) error {
0 commit comments