vitess.io/vitess@v0.16.2/go/vt/topo/memorytopo/watch.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package memorytopo 18 19 import ( 20 "context" 21 "fmt" 22 23 "vitess.io/vitess/go/vt/topo" 24 ) 25 26 // Watch is part of the topo.Conn interface. 27 func (c *Conn) Watch(ctx context.Context, filePath string) (*topo.WatchData, <-chan *topo.WatchData, error) { 28 if c.closed { 29 return nil, nil, ErrConnectionClosed 30 } 31 32 c.factory.Lock() 33 defer c.factory.Unlock() 34 35 if c.factory.err != nil { 36 return nil, nil, c.factory.err 37 } 38 39 n := c.factory.nodeByPath(c.cell, filePath) 40 if n == nil { 41 return nil, nil, topo.NewError(topo.NoNode, filePath) 42 } 43 if n.contents == nil { 44 // it's a directory 45 return nil, nil, fmt.Errorf("cannot watch directory %v in cell %v", filePath, c.cell) 46 } 47 current := &topo.WatchData{ 48 Contents: n.contents, 49 Version: NodeVersion(n.version), 50 } 51 52 notifications := make(chan *topo.WatchData, 100) 53 watchIndex := nextWatchIndex 54 nextWatchIndex++ 55 n.watches[watchIndex] = watch{contents: notifications} 56 57 go func() { 58 <-ctx.Done() 59 // This function can be called at any point, so we first need 60 // to make sure the watch is still valid. 61 c.factory.Lock() 62 defer c.factory.Unlock() 63 64 n := c.factory.nodeByPath(c.cell, filePath) 65 if n == nil { 66 return 67 } 68 69 if w, ok := n.watches[watchIndex]; ok { 70 delete(n.watches, watchIndex) 71 w.contents <- &topo.WatchData{Err: topo.NewError(topo.Interrupted, "watch")} 72 close(w.contents) 73 } 74 }() 75 return current, notifications, nil 76 } 77 78 // WatchRecursive is part of the topo.Conn interface. 79 func (c *Conn) WatchRecursive(ctx context.Context, dirpath string) ([]*topo.WatchDataRecursive, <-chan *topo.WatchDataRecursive, error) { 80 if c.closed { 81 return nil, nil, ErrConnectionClosed 82 } 83 84 c.factory.Lock() 85 defer c.factory.Unlock() 86 87 if c.factory.err != nil { 88 return nil, nil, c.factory.err 89 } 90 91 n := c.factory.getOrCreatePath(c.cell, dirpath) 92 if n == nil { 93 return nil, nil, topo.NewError(topo.NoNode, dirpath) 94 } 95 96 var initialwd []*topo.WatchDataRecursive 97 n.recurseContents(func(n *node) { 98 initialwd = append(initialwd, &topo.WatchDataRecursive{ 99 Path: n.name, 100 WatchData: topo.WatchData{ 101 Contents: n.contents, 102 Version: NodeVersion(n.version), 103 }, 104 }) 105 }) 106 107 notifications := make(chan *topo.WatchDataRecursive, 100) 108 watchIndex := nextWatchIndex 109 nextWatchIndex++ 110 n.watches[watchIndex] = watch{recursive: notifications} 111 112 go func() { 113 defer close(notifications) 114 115 <-ctx.Done() 116 117 c.factory.Lock() 118 f := c.factory 119 defer f.Unlock() 120 121 n := f.nodeByPath(c.cell, dirpath) 122 if n != nil { 123 delete(n.watches, watchIndex) 124 } 125 126 notifications <- &topo.WatchDataRecursive{WatchData: topo.WatchData{Err: topo.NewError(topo.Interrupted, "watch")}} 127 }() 128 129 return initialwd, notifications, nil 130 }