// Moves data from one server to another // Does the following: // 1. Lock all necessary clients // 2. Move Data // 3. Update router table on servers // 4. Delete partion from origin // 5. Unlock func MovePartition(services *Services, routerTable *shards.RouterTable, partition int, from, to *shards.RouterEntry) error { err := LockPartition(services, routerTable, partition) if err != nil { return err } //Unlock partition no matter what defer UnlockPartition(services, routerTable, partition) //copy the data _, err = CopyData(services, routerTable, partition, from, to) if err != nil { return err } //update the router table. parts := make([]int, 0) for _, p := range from.Partitions { if p != partition { parts = append(parts, p) } } from.Partitions = parts to.Partitions = append(to.Partitions, partition) routerTable, err = routerTable.AddEntries(from, to) routerTable, ok := RouterTableUpdate(services, routerTable, len(routerTable.Entries)) if !ok { services.Logger.Printf("Uh oh, Didnt update any router tables") } //Delete the data on the from server. err = DeletePartition(services, from, partition) if err != nil { return err } return nil }
// Moves a single partition from the largest entry to the smallest. only // if the smallest entry is smaller then the rest. // if all entries are the same size, then a random entry is chosen func RebalanceSingle(services *Services, routerTable *shards.RouterTable) error { var smallest *shards.RouterEntry = nil var largest *shards.RouterEntry = nil min := int(routerTable.TotalPartitions / len(routerTable.Entries)) services.Logger.Printf("Looking for entries with more then %d partitions", min) //shuffle the entries array entries := make([]*shards.RouterEntry, len(routerTable.Entries)) perm := rand.Perm(len(routerTable.Entries)) for i, v := range perm { entries[v] = routerTable.Entries[i] } for _, entry := range entries { if len(entry.Partitions) < min { if smallest == nil { smallest = entry } else if len(entry.Partitions) < len(smallest.Partitions) { smallest = entry } if largest == nil { largest = entry } else if len(entry.Partitions) > len(largest.Partitions) { largest = entry } } } if smallest == nil || largest == nil { services.Logger.Printf("Cluster appears to be balanced") return nil } services.Logger.Printf("Moving from %s to %s", largest.Id(), smallest.Id()) return nil }
// Checkin to an entry. will update their router table if it is out of date. will update our router table if out of date. func EntryCheckin(routerTable *shards.RouterTable, entry *shards.RouterEntry) (*shards.RouterTable, bool, error) { // make sure our routertable is up to date. response, err := client.HttpApiCallSync( fmt.Sprintf("%s:%d", entry.Address, entry.HttpPort), cheshire.NewRequest(shards.CHECKIN, "GET"), 5*time.Second) if err != nil { return routerTable, false, fmt.Errorf("ERROR While contacting %s -- %s", entry.Address, err) } entry.LastSeenAt = time.Now() rev := response.MustInt64("rt_revision", 0) if rev == routerTable.Revision { return routerTable, false, nil } if rev < routerTable.Revision { //updating server. //set the new routertable. req := cheshire.NewRequest(shards.ROUTERTABLE_SET, "POST") req.Params().Put("router_table", req.ToDynMap()) response, err = client.HttpApiCallSync( fmt.Sprintf("%s:%d", entry.Address, entry.HttpPort), req, 5*time.Second) if err != nil { return routerTable, false, fmt.Errorf("ERROR While contacting for router table update %s -- %s", entry.Address, err) } if response.StatusCode() != 200 { return routerTable, false, fmt.Errorf("Error trying to Set router table %s -- %s", entry.Address, response.StatusMessage()) } } else { //updating local log.Printf("Found updated router table at: %s", entry.Address) //get the new routertable. response, err = client.HttpApiCallSync( fmt.Sprintf("%s:%d", entry.Address, entry.HttpPort), cheshire.NewRequest(shards.ROUTERTABLE_GET, "GET"), 5*time.Second) if err != nil { return routerTable, false, fmt.Errorf("ERROR While contacting %s -- %s", entry.Address, err) } mp, ok := response.GetDynMap("router_table") if !ok { return routerTable, false, fmt.Errorf("ERROR from %s -- BAD ROUTER TABLE RESPONSE %s", entry.Address, response) } rt, err := shards.ToRouterTable(mp) if err != nil { return routerTable, false, fmt.Errorf("ERROR While parsing router table %s -- %s", entry.Address, err) } log.Printf("SUCCESSFULLY update router table to revision %d", rt.Revision) routerTable = rt return routerTable, true, nil } return routerTable, false, nil }