Example #1
0
// ComputePullServers creates a map from block locator to PullServers
// with one entry for each under-replicated block.
//
// This method ignores zero-replica blocks since there are no servers
// to pull them from, so callers should feel free to omit them, but
// this function will ignore them if they are provided.
func ComputePullServers(kc *keepclient.KeepClient,
	keepServerInfo *keep.ReadServers,
	blockToDesiredReplication map[blockdigest.DigestWithSize]int,
	underReplicated BlockSet) (m map[Locator]PullServers) {
	m = map[Locator]PullServers{}
	// We use CanonicalString to avoid filling memory with dupicate
	// copies of the same string.
	var cs CanonicalString

	// Servers that are writeable
	writableServers := map[string]struct{}{}
	for _, url := range kc.WritableLocalRoots() {
		writableServers[cs.Get(url)] = struct{}{}
	}

	for block := range underReplicated {
		serversStoringBlock := keepServerInfo.BlockToServers[block]
		numCopies := len(serversStoringBlock)
		numCopiesMissing := blockToDesiredReplication[block] - numCopies
		if numCopiesMissing > 0 {
			// We expect this to always be true, since the block was listed
			// in underReplicated.

			if numCopies > 0 {
				// Not much we can do with blocks with no copies.

				// A server's host-port string appears as a key in this map
				// iff it contains the block.
				serverHasBlock := map[string]struct{}{}
				for _, info := range serversStoringBlock {
					sa := keepServerInfo.KeepServerIndexToAddress[info.ServerIndex]
					serverHasBlock[cs.Get(sa.URL())] = struct{}{}
				}

				roots := keepclient.NewRootSorter(kc.LocalRoots(),
					block.String()).GetSortedRoots()

				l := Locator(block)
				m[l] = CreatePullServers(cs, serverHasBlock, writableServers,
					roots, numCopiesMissing)
			}
		}
	}
	return m
}
Example #2
0
// balanceBlock compares current state to desired state for a single
// block, and makes the appropriate ChangeSet calls.
func (bal *Balancer) balanceBlock(blkid arvados.SizedDigest, blk *BlockState) {
	debugf("balanceBlock: %v %+v", blkid, blk)
	uuids := keepclient.NewRootSorter(bal.serviceRoots, string(blkid[:32])).GetSortedRoots()
	hasRepl := make(map[string]Replica, len(bal.serviceRoots))
	for _, repl := range blk.Replicas {
		hasRepl[repl.UUID] = repl
		// TODO: when multiple copies are on one server, use
		// the oldest one that doesn't have a timestamp
		// collision with other replicas.
	}
	// number of replicas already found in positions better than
	// the position we're contemplating now.
	reportedBestRepl := 0
	// To be safe we assume two replicas with the same Mtime are
	// in fact the same replica being reported more than
	// once. len(uniqueBestRepl) is the number of distinct
	// replicas in the best rendezvous positions we've considered
	// so far.
	uniqueBestRepl := make(map[int64]bool, len(bal.serviceRoots))
	// pulls is the number of Pull changes we have already
	// requested. (For purposes of deciding whether to Pull to
	// rendezvous position N, we should assume all pulls we have
	// requested on rendezvous positions M<N will be successful.)
	pulls := 0
	var changes []string
	for _, uuid := range uuids {
		change := changeNone
		srv := bal.KeepServices[uuid]
		// TODO: request a Touch if Mtime is duplicated.
		repl, ok := hasRepl[srv.UUID]
		if ok {
			// This service has a replica. We should
			// delete it if [1] we already have enough
			// distinct replicas in better rendezvous
			// positions and [2] this replica's Mtime is
			// distinct from all of the better replicas'
			// Mtimes.
			if !srv.ReadOnly &&
				repl.Mtime < bal.MinMtime &&
				len(uniqueBestRepl) >= blk.Desired &&
				!uniqueBestRepl[repl.Mtime] {
				srv.AddTrash(Trash{
					SizedDigest: blkid,
					Mtime:       repl.Mtime,
				})
				change = changeTrash
			} else {
				change = changeStay
			}
			uniqueBestRepl[repl.Mtime] = true
			reportedBestRepl++
		} else if pulls+reportedBestRepl < blk.Desired &&
			len(blk.Replicas) > 0 &&
			!srv.ReadOnly {
			// This service doesn't have a replica. We
			// should pull one to this server if we don't
			// already have enough (existing+requested)
			// replicas in better rendezvous positions.
			srv.AddPull(Pull{
				SizedDigest: blkid,
				Source:      blk.Replicas[0].KeepService,
			})
			pulls++
			change = changePull
		}
		if bal.Dumper != nil {
			changes = append(changes, fmt.Sprintf("%s:%d=%s,%d", srv.ServiceHost, srv.ServicePort, changeName[change], repl.Mtime))
		}
	}
	if bal.Dumper != nil {
		bal.Dumper.Printf("%s have=%d want=%d %s", blkid, len(blk.Replicas), blk.Desired, strings.Join(changes, " "))
	}
}