func (zkts *Server) updateTabletEndpoint(oldValue string, oldStat zk.Stat, addr *topo.VtnsAddr) (newValue string, err error) { if oldStat == nil { // The incoming object doesn't exist - we haven't been placed in the serving // graph yet, so don't update. Assume the next process that rebuilds the graph // will get the updated tablet location. return "", skipUpdateErr } var addrs *topo.VtnsAddrs if oldValue != "" { addrs, err = topo.NewVtnsAddrs(oldValue, oldStat.Version()) if err != nil { return } foundTablet := false for i, entry := range addrs.Entries { if entry.Uid == addr.Uid { foundTablet = true if !topo.VtnsAddrEquality(&entry, addr) { addrs.Entries[i] = *addr } break } } if !foundTablet { addrs.Entries = append(addrs.Entries, *addr) } } else { addrs = topo.NewAddrs() addrs.Entries = append(addrs.Entries, *addr) } return jscfg.ToJson(addrs), nil }
// Write serving graph data to the cells func (wr *Wrangler) rebuildShardSrvGraph(shardInfo *topo.ShardInfo, tablets []*topo.TabletInfo, cells []string) error { relog.Info("rebuildShardSrvGraph %v/%v", shardInfo.Keyspace(), shardInfo.ShardName()) // Get all existing db types so they can be removed if nothing // had been editted. This applies to all cells, which can't // be determined until you walk through all the tablets. // // existingDbTypeLocations is a map: // key: {cell,keyspace,shard,tabletType} // value: true existingDbTypeLocations := make(map[cellKeyspaceShardType]bool) // Update db type addresses in the serving graph // // locationAddrsMap is a map: // key: {cell,keyspace,shard,tabletType} // value: topo.VtnsAddrs (list of server records) locationAddrsMap := make(map[cellKeyspaceShardType]*topo.VtnsAddrs) // we keep track of the existingDbTypeLocations we've already looked at knownShardLocations := make(map[cellKeyspaceShard]bool) for _, tablet := range tablets { // only look at tablets in the cells we want to rebuild // we also include masters from everywhere, so we can // write the right aliases if !inCellList(tablet.Tablet.Cell, cells) && tablet.Type != topo.TYPE_MASTER { continue } // this is {cell,keyspace,shard} // we'll get the children to find the existing types shardLocation := cellKeyspaceShard{tablet.Tablet.Cell, tablet.Tablet.Keyspace, tablet.Shard} // only need to do this once per cell if !knownShardLocations[shardLocation] { tabletTypes, err := wr.ts.GetSrvTabletTypesPerShard(tablet.Tablet.Cell, tablet.Tablet.Keyspace, tablet.Shard) if err != nil { if err != topo.ErrNoNode { return err } } else { for _, tabletType := range tabletTypes { existingDbTypeLocations[cellKeyspaceShardType{tablet.Tablet.Cell, tablet.Tablet.Keyspace, tablet.Shard, tabletType}] = true } } knownShardLocations[shardLocation] = true } // Check IsServingType after we have populated existingDbTypeLocations // so we properly prune data if the definition of serving type // changes. if !tablet.IsServingType() { continue } location := cellKeyspaceShardType{tablet.Tablet.Cell, tablet.Keyspace, tablet.Shard, tablet.Type} addrs, ok := locationAddrsMap[location] if !ok { addrs = topo.NewAddrs() locationAddrsMap[location] = addrs } entry, err := tm.VtnsAddrForTablet(tablet.Tablet) if err != nil { relog.Warning("VtnsAddrForTablet failed for tablet %v: %v", tablet.Alias(), err) continue } addrs.Entries = append(addrs.Entries, *entry) } // if there is a master in one cell, put it in all of them var masterRecord *topo.VtnsAddrs for shardLocation, _ := range knownShardLocations { loc := cellKeyspaceShardType{shardLocation.cell, shardLocation.keyspace, shardLocation.shard, topo.TYPE_MASTER} if addrs, ok := locationAddrsMap[loc]; ok { if masterRecord != nil { relog.Warning("Multiple master records in %v", shardLocation) } else { relog.Info("Found master record in %v", shardLocation) masterRecord = addrs } } } if masterRecord != nil { for shardLocation, _ := range knownShardLocations { location := cellKeyspaceShardType{shardLocation.cell, shardLocation.keyspace, shardLocation.shard, topo.TYPE_MASTER} if _, ok := locationAddrsMap[location]; !ok { relog.Info("Adding remote master record in %v", location) locationAddrsMap[location] = masterRecord } } } // write all the {cell,keyspace,shard,type} // nodes everywhere we want them for location, addrs := range locationAddrsMap { cell := location.cell if !inCellList(cell, cells) { continue } if err := wr.ts.UpdateSrvTabletType(location.cell, location.keyspace, location.shard, location.tabletType, addrs); err != nil { return fmt.Errorf("writing endpoints failed: %v", err) } } // Delete any pre-existing paths that were not updated by this process. // That's the existingDbTypeLocations - locationAddrsMap for dbTypeLocation, _ := range existingDbTypeLocations { if _, ok := locationAddrsMap[dbTypeLocation]; !ok { cell := dbTypeLocation.cell if !inCellList(cell, cells) { continue } relog.Info("removing stale db type from serving graph: %v", dbTypeLocation) if err := wr.ts.DeleteSrvTabletType(dbTypeLocation.cell, dbTypeLocation.keyspace, dbTypeLocation.shard, dbTypeLocation.tabletType); err != nil { relog.Warning("unable to remove stale db type from serving graph: %v", err) } } } // Update per-shard information per cell-specific serving path. // // srvShardByPath is a map: // key: {cell,keyspace,shard} // value: topo.SrvShard // this will fill in the AddrsByType part for each shard srvShardByPath := make(map[cellKeyspaceShard]*topo.SrvShard) for location, addrs := range locationAddrsMap { // location will be {cell,keyspace,shard,type} srvShardPath := cellKeyspaceShard{location.cell, location.keyspace, location.shard} tabletType := location.tabletType srvShard, ok := srvShardByPath[srvShardPath] if !ok { srvShard = &topo.SrvShard{KeyRange: shardInfo.KeyRange, AddrsByType: make(map[string]topo.VtnsAddrs)} srvShardByPath[srvShardPath] = srvShard } srvShard.AddrsByType[string(tabletType)] = *addrs } // Save the shard entries for srvPath, srvShard := range srvShardByPath { if err := wr.ts.UpdateSrvShard(srvPath.cell, srvPath.keyspace, srvPath.shard, srvShard); err != nil { return fmt.Errorf("writing serving data failed: %v", err) } } return nil }