vitess.io/vitess@v0.16.2/go/vt/topo/srv_vschema.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 topo
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"sync"
    23  
    24  	"google.golang.org/protobuf/proto"
    25  
    26  	"vitess.io/vitess/go/vt/log"
    27  	"vitess.io/vitess/go/vt/vterrors"
    28  
    29  	vschemapb "vitess.io/vitess/go/vt/proto/vschema"
    30  )
    31  
    32  // This file contains the utility methods to manage SrvVSchema objects.
    33  
    34  // WatchSrvVSchemaData is returned / streamed by WatchSrvVSchema.
    35  // The WatchSrvVSchema API guarantees exactly one of Value or Err will be set.
    36  type WatchSrvVSchemaData struct {
    37  	Value *vschemapb.SrvVSchema
    38  	Err   error
    39  }
    40  
    41  // WatchSrvVSchema will set a watch on the SrvVSchema object.
    42  // It has the same contract as Conn.Watch, but it also unpacks the
    43  // contents into a SrvVSchema object.
    44  func (ts *Server) WatchSrvVSchema(ctx context.Context, cell string) (*WatchSrvVSchemaData, <-chan *WatchSrvVSchemaData, error) {
    45  	conn, err := ts.ConnForCell(ctx, cell)
    46  	if err != nil {
    47  		return nil, nil, err
    48  	}
    49  
    50  	ctx, cancel := context.WithCancel(ctx)
    51  	current, wdChannel, err := conn.Watch(ctx, SrvVSchemaFile)
    52  	if err != nil {
    53  		cancel()
    54  		return nil, nil, err
    55  	}
    56  	value := &vschemapb.SrvVSchema{}
    57  	if err := proto.Unmarshal(current.Contents, value); err != nil {
    58  		// Cancel the watch, drain channel.
    59  		cancel()
    60  		for range wdChannel {
    61  		}
    62  		return nil, nil, vterrors.Wrapf(err, "error unpacking initial SrvVSchema object")
    63  	}
    64  
    65  	changes := make(chan *WatchSrvVSchemaData, 10)
    66  
    67  	// The background routine reads any event from the watch channel,
    68  	// translates it, and sends it to the caller.
    69  	// If cancel() is called, the underlying Watch() code will
    70  	// send an ErrInterrupted and then close the channel. We'll
    71  	// just propagate that back to our caller.
    72  	go func() {
    73  		defer cancel()
    74  		defer close(changes)
    75  
    76  		for wd := range wdChannel {
    77  			if wd.Err != nil {
    78  				// Last error value, we're done.
    79  				// wdChannel will be closed right after
    80  				// this, no need to do anything.
    81  				changes <- &WatchSrvVSchemaData{Err: wd.Err}
    82  				return
    83  			}
    84  
    85  			value := &vschemapb.SrvVSchema{}
    86  			if err := proto.Unmarshal(wd.Contents, value); err != nil {
    87  				cancel()
    88  				for range wdChannel {
    89  				}
    90  				changes <- &WatchSrvVSchemaData{Err: vterrors.Wrapf(err, "error unpacking SrvVSchema object")}
    91  				return
    92  			}
    93  			changes <- &WatchSrvVSchemaData{Value: value}
    94  		}
    95  	}()
    96  
    97  	return &WatchSrvVSchemaData{Value: value}, changes, nil
    98  }
    99  
   100  // UpdateSrvVSchema updates the SrvVSchema file for a cell.
   101  func (ts *Server) UpdateSrvVSchema(ctx context.Context, cell string, srvVSchema *vschemapb.SrvVSchema) error {
   102  	conn, err := ts.ConnForCell(ctx, cell)
   103  	if err != nil {
   104  		return err
   105  	}
   106  
   107  	nodePath := SrvVSchemaFile
   108  	data, err := proto.Marshal(srvVSchema)
   109  	if err != nil {
   110  		return err
   111  	}
   112  	_, err = conn.Update(ctx, nodePath, data, nil)
   113  	return err
   114  }
   115  
   116  // GetSrvVSchema returns the SrvVSchema for a cell.
   117  func (ts *Server) GetSrvVSchema(ctx context.Context, cell string) (*vschemapb.SrvVSchema, error) {
   118  	conn, err := ts.ConnForCell(ctx, cell)
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  
   123  	nodePath := SrvVSchemaFile
   124  	data, _, err := conn.Get(ctx, nodePath)
   125  	if err != nil {
   126  		return nil, err
   127  	}
   128  	srvVSchema := &vschemapb.SrvVSchema{}
   129  	if err := proto.Unmarshal(data, srvVSchema); err != nil {
   130  		return nil, vterrors.Wrapf(err, "SrvVSchema unmarshal failed: %v", data)
   131  	}
   132  	return srvVSchema, nil
   133  }
   134  
   135  // DeleteSrvVSchema deletes the SrvVSchema file for a cell.
   136  func (ts *Server) DeleteSrvVSchema(ctx context.Context, cell string) error {
   137  	conn, err := ts.ConnForCell(ctx, cell)
   138  	if err != nil {
   139  		return err
   140  	}
   141  
   142  	nodePath := SrvVSchemaFile
   143  	return conn.Delete(ctx, nodePath, nil)
   144  }
   145  
   146  // RebuildSrvVSchema rebuilds the SrvVSchema for the provided cell list
   147  // (or all cells if cell list is empty).
   148  func (ts *Server) RebuildSrvVSchema(ctx context.Context, cells []string) error {
   149  	// get the actual list of cells
   150  	if len(cells) == 0 {
   151  		var err error
   152  		cells, err = ts.GetKnownCells(ctx)
   153  		if err != nil {
   154  			return fmt.Errorf("GetKnownCells failed: %v", err)
   155  		}
   156  	}
   157  
   158  	// get the keyspaces
   159  	keyspaces, err := ts.GetKeyspaces(ctx)
   160  	if err != nil {
   161  		return fmt.Errorf("GetKeyspaces failed: %v", err)
   162  	}
   163  
   164  	// build the SrvVSchema in parallel, protected by mu
   165  	wg := sync.WaitGroup{}
   166  	mu := sync.Mutex{}
   167  	var finalErr error
   168  	srvVSchema := &vschemapb.SrvVSchema{
   169  		Keyspaces: map[string]*vschemapb.Keyspace{},
   170  	}
   171  	for _, keyspace := range keyspaces {
   172  		wg.Add(1)
   173  		go func(keyspace string) {
   174  			defer wg.Done()
   175  
   176  			k, err := ts.GetVSchema(ctx, keyspace)
   177  			if IsErrType(err, NoNode) {
   178  				err = nil
   179  				k = &vschemapb.Keyspace{}
   180  			}
   181  
   182  			mu.Lock()
   183  			defer mu.Unlock()
   184  			if err != nil {
   185  				log.Errorf("%v: GetVSchema(%v) failed", err, keyspace)
   186  				finalErr = err
   187  				return
   188  			}
   189  			srvVSchema.Keyspaces[keyspace] = k
   190  		}(keyspace)
   191  	}
   192  	wg.Wait()
   193  	if finalErr != nil {
   194  		return finalErr
   195  	}
   196  
   197  	rr, err := ts.GetRoutingRules(ctx)
   198  	if err != nil {
   199  		return fmt.Errorf("GetRoutingRules failed: %v", err)
   200  	}
   201  	srvVSchema.RoutingRules = rr
   202  
   203  	srr, err := ts.GetShardRoutingRules(ctx)
   204  	if err != nil {
   205  		return fmt.Errorf("GetShardRoutingRules failed: %v", err)
   206  	}
   207  	srvVSchema.ShardRoutingRules = srr
   208  
   209  	// now save the SrvVSchema in all cells in parallel
   210  	for _, cell := range cells {
   211  		wg.Add(1)
   212  		go func(cell string) {
   213  			defer wg.Done()
   214  			if err := ts.UpdateSrvVSchema(ctx, cell, srvVSchema); err != nil {
   215  				log.Errorf("%v: UpdateSrvVSchema(%v) failed", err, cell)
   216  				mu.Lock()
   217  				finalErr = err
   218  				mu.Unlock()
   219  			}
   220  		}(cell)
   221  	}
   222  	wg.Wait()
   223  
   224  	return finalErr
   225  }