vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletserver/messager/engine.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 messager 18 19 import ( 20 "context" 21 "sync" 22 23 "vitess.io/vitess/go/sqltypes" 24 "vitess.io/vitess/go/sync2" 25 "vitess.io/vitess/go/vt/log" 26 "vitess.io/vitess/go/vt/vterrors" 27 "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" 28 "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" 29 30 binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" 31 querypb "vitess.io/vitess/go/vt/proto/query" 32 vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" 33 ) 34 35 // TabletService defines the functions of TabletServer 36 // that the messager needs for callback. 37 type TabletService interface { 38 tabletenv.Env 39 PostponeMessages(ctx context.Context, target *querypb.Target, querygen QueryGenerator, ids []string) (count int64, err error) 40 PurgeMessages(ctx context.Context, target *querypb.Target, querygen QueryGenerator, timeCutoff int64) (count int64, err error) 41 } 42 43 // VStreamer defines the functions of VStreamer 44 // that the messager needs. 45 type VStreamer interface { 46 Stream(ctx context.Context, startPos string, tablePKs []*binlogdatapb.TableLastPK, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error 47 StreamResults(ctx context.Context, query string, send func(*binlogdatapb.VStreamResultsResponse) error) error 48 } 49 50 // Engine is the engine for handling messages. 51 type Engine struct { 52 mu sync.Mutex 53 isOpen bool 54 managers map[string]*messageManager 55 56 tsv TabletService 57 se *schema.Engine 58 vs VStreamer 59 postponeSema *sync2.Semaphore 60 } 61 62 // NewEngine creates a new Engine. 63 func NewEngine(tsv TabletService, se *schema.Engine, vs VStreamer) *Engine { 64 return &Engine{ 65 tsv: tsv, 66 se: se, 67 vs: vs, 68 postponeSema: sync2.NewSemaphore(tsv.Config().MessagePostponeParallelism, 0), 69 managers: make(map[string]*messageManager), 70 } 71 } 72 73 // Open starts the Engine service. 74 func (me *Engine) Open() { 75 me.mu.Lock() 76 if me.isOpen { 77 me.mu.Unlock() 78 return 79 } 80 me.isOpen = true 81 me.mu.Unlock() 82 log.Info("Messager: opening") 83 // Unlock before invoking RegisterNotifier because it 84 // obtains the same lock. 85 me.se.RegisterNotifier("messages", me.schemaChanged) 86 } 87 88 // Close closes the Engine service. 89 func (me *Engine) Close() { 90 log.Infof("messager Engine - started execution of Close. Acquiring mu lock") 91 me.mu.Lock() 92 log.Infof("messager Engine - acquired mu lock") 93 defer me.mu.Unlock() 94 if !me.isOpen { 95 log.Infof("messager Engine is not open") 96 return 97 } 98 me.isOpen = false 99 log.Infof("messager Engine - unregistering notifiers") 100 me.se.UnregisterNotifier("messages") 101 log.Infof("messager Engine - closing all managers") 102 for _, mm := range me.managers { 103 mm.Close() 104 } 105 me.managers = make(map[string]*messageManager) 106 log.Info("Messager: closed") 107 } 108 109 func (me *Engine) GetGenerator(name string) (QueryGenerator, error) { 110 me.mu.Lock() 111 defer me.mu.Unlock() 112 mm := me.managers[name] 113 if mm == nil { 114 return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "message table %s not found in schema", name) 115 } 116 return mm, nil 117 } 118 119 // Subscribe subscribes to messages from the requested table. 120 // The function returns a done channel that will be closed when 121 // the subscription ends, which can be initiated by the send function 122 // returning io.EOF. The engine can also end a subscription which is 123 // usually triggered by Close. It's the responsibility of the send 124 // function to promptly return if the done channel is closed. Otherwise, 125 // the engine's Close function will hang indefinitely. 126 func (me *Engine) Subscribe(ctx context.Context, name string, send func(*sqltypes.Result) error) (done <-chan struct{}, err error) { 127 me.mu.Lock() 128 defer me.mu.Unlock() 129 if !me.isOpen { 130 return nil, vterrors.Errorf(vtrpcpb.Code_UNAVAILABLE, "messager engine is closed, probably because this is not a primary any more") 131 } 132 mm := me.managers[name] 133 if mm == nil { 134 return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "message table %s not found", name) 135 } 136 return mm.Subscribe(ctx, send), nil 137 } 138 139 func (me *Engine) schemaChanged(tables map[string]*schema.Table, created, altered, dropped []string) { 140 me.mu.Lock() 141 defer me.mu.Unlock() 142 for _, name := range append(dropped, altered...) { 143 mm := me.managers[name] 144 if mm == nil { 145 continue 146 } 147 log.Infof("Stopping messager for dropped/updated table: %v", name) 148 mm.Close() 149 delete(me.managers, name) 150 } 151 152 for _, name := range append(created, altered...) { 153 t := tables[name] 154 if t.Type != schema.Message { 155 continue 156 } 157 if me.managers[name] != nil { 158 me.tsv.Stats().InternalErrors.Add("Messages", 1) 159 log.Errorf("Newly created table already exists in messages: %s", name) 160 continue 161 } 162 mm := newMessageManager(me.tsv, me.vs, t, me.postponeSema) 163 me.managers[name] = mm 164 log.Infof("Starting messager for table: %v", name) 165 mm.Open() 166 } 167 }