Example #1
0
func (client *client) StreamKeyRange(req *proto.KeyRangeRequest, responseChan chan *proto.BinlogTransaction) binlogplayer.BinlogPlayerResponse {
	query := &pb.StreamKeyRangeRequest{
		Position:       myproto.ReplicationPositionToProto(req.Position),
		KeyspaceIdType: key.KeyspaceIdTypeToProto(req.KeyspaceIdType),
		KeyRange:       key.KeyRangeToProto(req.KeyRange),
		Charset:        mproto.CharsetToProto(req.Charset),
	}

	response := &response{}
	stream, err := client.c.StreamKeyRange(client.ctx, query)
	if err != nil {
		response.err = err
		close(responseChan)
		return response
	}
	go func() {
		for {
			r, err := stream.Recv()
			if err != nil {
				if err != io.EOF {
					response.err = err
				}
				close(responseChan)
				return
			}
			responseChan <- proto.ProtoToBinlogTransaction(r.BinlogTransaction)
		}
	}()
	return response
}
Example #2
0
// SrvKeyspaceToProto turns a Tablet into a proto
func SrvKeyspaceToProto(s *SrvKeyspace) *pb.SrvKeyspace {
	result := &pb.SrvKeyspace{
		ShardingColumnName: s.ShardingColumnName,
		ShardingColumnType: key.KeyspaceIdTypeToProto(s.ShardingColumnType),
		SplitShardCount:    s.SplitShardCount,
	}
	for tt, p := range s.Partitions {
		partition := &pb.SrvKeyspace_KeyspacePartition{
			ServedType: TabletTypeToProto(tt),
		}
		for _, sr := range p.ShardReferences {
			partition.ShardReferences = append(partition.ShardReferences, &pb.ShardReference{
				Name:     sr.Name,
				KeyRange: key.KeyRangeToProto(sr.KeyRange),
			})
		}
		result.Partitions = append(result.Partitions, partition)
	}
	for tt, k := range s.ServedFrom {
		result.ServedFrom = append(result.ServedFrom, &pb.SrvKeyspace_ServedFrom{
			TabletType: TabletTypeToProto(tt),
			Keyspace:   k,
		})
	}
	return result
}
Example #3
0
func (client *client) StreamKeyRange(ctx context.Context, position string, keyspaceIdType key.KeyspaceIdType, keyRange key.KeyRange, charset *mproto.Charset) (chan *proto.BinlogTransaction, binlogplayer.ErrFunc, error) {
	response := make(chan *proto.BinlogTransaction, 10)
	query := &pb.StreamKeyRangeRequest{
		Position:       position,
		KeyspaceIdType: key.KeyspaceIdTypeToProto(keyspaceIdType),
		KeyRange:       key.KeyRangeToProto(keyRange),
		Charset:        mproto.CharsetToProto(charset),
	}

	stream, err := client.c.StreamKeyRange(ctx, query)
	if err != nil {
		return nil, nil, err
	}
	var finalErr error
	go func() {
		for {
			r, err := stream.Recv()
			if err != nil {
				if err != io.EOF {
					finalErr = err
				}
				close(response)
				return
			}
			response <- proto.ProtoToBinlogTransaction(r.BinlogTransaction)
		}
	}()
	return response, func() error {
		return finalErr
	}, nil
}
Example #4
0
// RefreshMap reads the right data from topo.Server and makes sure
// we're playing the right logs.
func (blm *BinlogPlayerMap) RefreshMap(ctx context.Context, tablet *topo.Tablet, keyspaceInfo *topo.KeyspaceInfo, shardInfo *topo.ShardInfo) {
	log.Infof("Refreshing map of binlog players")
	if shardInfo == nil {
		log.Warningf("Could not read shardInfo, not changing anything")
		return
	}

	if len(shardInfo.SourceShards) > 0 && keyspaceInfo == nil {
		log.Warningf("Could not read keyspaceInfo, not changing anything")
		return
	}

	blm.mu.Lock()
	if blm.dbConfig.DbName == "" {
		blm.dbConfig.DbName = tablet.DbName()
	}

	// get the existing sources and build a map of sources to remove
	toRemove := make(map[uint32]bool)
	hadPlayers := false
	for source := range blm.players {
		toRemove[source] = true
		hadPlayers = true
	}

	// for each source, add it if not there, and delete from toRemove
	for _, sourceShard := range shardInfo.SourceShards {
		blm.addPlayer(ctx, tablet.Alias.Cell, keyspaceInfo.ShardingColumnType, key.KeyRangeToProto(tablet.KeyRange), sourceShard, tablet.DbName())
		delete(toRemove, sourceShard.Uid)
	}
	hasPlayers := len(shardInfo.SourceShards) > 0

	// remove all entries from toRemove
	for source := range toRemove {
		blm.players[source].Stop()
		delete(blm.players, source)
	}

	blm.mu.Unlock()

	if hadPlayers && !hasPlayers {
		// We're done streaming, so turn off special playback settings.
		blm.mysqld.DisableBinlogPlayback()
	}
}
Example #5
0
func TestKeyRangeToShardMap(t *testing.T) {
	ts := new(sandboxTopo)
	var testCases = []struct {
		keyspace string
		keyRange string
		shards   []string
	}{
		{keyspace: KsTestSharded, keyRange: "20-40", shards: []string{"20-40"}},
		// check for partial keyrange, spanning one shard
		{keyspace: KsTestSharded, keyRange: "10-18", shards: []string{"-20"}},
		// check for keyrange intersecting with multiple shards
		{keyspace: KsTestSharded, keyRange: "10-40", shards: []string{"-20", "20-40"}},
		// check for keyrange intersecting with multiple shards
		{keyspace: KsTestSharded, keyRange: "1c-2a", shards: []string{"-20", "20-40"}},
		// check for keyrange where kr.End is Max Key ""
		{keyspace: KsTestSharded, keyRange: "80-", shards: []string{"80-a0", "a0-c0", "c0-e0", "e0-"}},
		// test for sharded, non-partial keyrange spanning the entire space.
		{keyspace: KsTestSharded, keyRange: "", shards: []string{"-20", "20-40", "40-60", "60-80", "80-a0", "a0-c0", "c0-e0", "e0-"}},
		// test for unsharded, non-partial keyrange spanning the entire space.
		{keyspace: KsTestUnsharded, keyRange: "", shards: []string{"0"}},
	}

	for _, testCase := range testCases {
		var keyRange *pb.KeyRange
		var err error
		if testCase.keyRange == "" {
			keyRange = &pb.KeyRange{}
		} else {
			krArray, err := key.ParseShardingSpec(testCase.keyRange)
			if err != nil {
				t.Errorf("Got error while parsing sharding spec %v", err)
			}
			keyRange = key.KeyRangeToProto(krArray[0])
		}
		_, _, allShards, err := getKeyspaceShards(context.Background(), ts, "", testCase.keyspace, pb.TabletType_MASTER)
		gotShards, err := resolveKeyRangeToShards(allShards, keyRange)
		if err != nil {
			t.Errorf("want nil, got %v", err)
		}
		if !reflect.DeepEqual(testCase.shards, gotShards) {
			t.Errorf("want \n%#v, got \n%#v", testCase.shards, gotShards)
		}
	}
}
Example #6
0
// TabletToProto turns a Tablet into a proto
func TabletToProto(t *Tablet) *pb.Tablet {
	result := &pb.Tablet{
		Alias:          TabletAliasToProto(t.Alias),
		Hostname:       t.Hostname,
		Ip:             t.IPAddr,
		PortMap:        make(map[string]int32),
		Keyspace:       t.Keyspace,
		Shard:          t.Shard,
		KeyRange:       key.KeyRangeToProto(t.KeyRange),
		Type:           TabletTypeToProto(t.Type),
		DbNameOverride: t.DbNameOverride,
		Tags:           t.Tags,
		HealthMap:      t.Health,
	}
	for k, v := range t.Portmap {
		result.PortMap[k] = int32(v)
	}
	return result
}
Example #7
0
// SetSourceShards is a utility function to override the SourceShards fields
// on a Shard.
func (wr *Wrangler) SetSourceShards(ctx context.Context, keyspace, shard string, sources []topo.TabletAlias, tables []string) error {
	// read the shard
	shardInfo, err := wr.ts.GetShard(ctx, keyspace, shard)
	if err != nil {
		return err
	}

	// If the shard already has sources, maybe it's already been restored,
	// so let's be safe and abort right here.
	if len(shardInfo.SourceShards) > 0 {
		return fmt.Errorf("Shard %v/%v already has SourceShards, not overwriting them", keyspace, shard)
	}

	// read the source tablets
	sourceTablets, err := topo.GetTabletMap(ctx, wr.TopoServer(), sources)
	if err != nil {
		return err
	}

	// Insert their KeyRange in the SourceShards array.
	// We use a linear 0-based id, that matches what mysqlctld/split.go
	// inserts into _vt.blp_checkpoint.
	shardInfo.SourceShards = make([]*pb.Shard_SourceShard, len(sourceTablets))
	i := 0
	for _, ti := range sourceTablets {
		shardInfo.SourceShards[i] = &pb.Shard_SourceShard{
			Uid:      uint32(i),
			Keyspace: ti.Keyspace,
			Shard:    ti.Shard,
			KeyRange: key.KeyRangeToProto(ti.KeyRange),
			Tables:   tables,
		}
		i++
	}

	// and write the shard
	if err = topo.UpdateShard(ctx, wr.ts, shardInfo); err != nil {
		return err
	}

	return nil
}
Example #8
0
// StreamKeyRange is part of the gorpc UpdateStream service
func (server *UpdateStream) StreamKeyRange(req *proto.KeyRangeRequest, sendReply func(reply interface{}) error) (err error) {
	defer server.updateStream.HandlePanic(&err)
	return server.updateStream.StreamKeyRange(req.Position, req.KeyspaceIdType, key.KeyRangeToProto(req.KeyRange), req.Charset, func(reply *proto.BinlogTransaction) error {
		return sendReply(reply)
	})
}