6.5840/src/tester1/group.go
Frans Kaashoek fa6877b02d update
2025-04-07 19:39:55 -04:00

317 lines
6.6 KiB
Go

package tester
import (
//"log"
"strconv"
"sync"
"6.5840/labrpc"
)
type Tgid int
// A service must support Kill(); the tester will Kill()
// on service returned by FstartServer()
type IService interface {
Kill()
}
// Start server and return the services to register with labrpc
type FstartServer func(ends []*labrpc.ClientEnd, grp Tgid, srv int, persister *Persister) []IService
// Each server has a name: i'th server of group gid. If there is only a single
// server, it its gid = 0 and its i is 0.
func ServerName(gid Tgid, i int) string {
return "server-" + strconv.Itoa(int(gid)) + "-" + strconv.Itoa(i)
}
// The tester may have many groups of servers (e.g., one per Raft group).
// Groups are named 0, 1, and so on.
type Groups struct {
mu sync.Mutex
net *labrpc.Network
grps map[Tgid]*ServerGrp
}
func newGroups(net *labrpc.Network) *Groups {
return &Groups{net: net, grps: make(map[Tgid]*ServerGrp)}
}
func (gs *Groups) MakeGroup(gid Tgid, nsrv int, mks FstartServer) {
gs.mu.Lock()
defer gs.mu.Unlock()
gs.grps[gid] = makeSrvGrp(gs.net, gid, nsrv, mks)
}
func (gs *Groups) lookupGroup(gid Tgid) *ServerGrp {
gs.mu.Lock()
defer gs.mu.Unlock()
return gs.grps[gid]
}
func (gs *Groups) delete(gid Tgid) {
gs.mu.Lock()
defer gs.mu.Unlock()
delete(gs.grps, gid)
}
func (gs *Groups) cleanup() {
gs.mu.Lock()
defer gs.mu.Unlock()
for _, sg := range gs.grps {
sg.cleanup()
}
}
type ServerGrp struct {
net *labrpc.Network
srvs []*Server
servernames []string
gid Tgid
connected []bool // whether each server is on the net
mks FstartServer
mu sync.Mutex
}
func makeSrvGrp(net *labrpc.Network, gid Tgid, n int, mks FstartServer) *ServerGrp {
sg := &ServerGrp{
net: net,
srvs: make([]*Server, n),
gid: gid,
connected: make([]bool, n),
mks: mks,
}
for i, _ := range sg.srvs {
sg.srvs[i] = makeServer(net, gid, n)
}
sg.servernames = make([]string, n)
for i := 0; i < n; i++ {
sg.servernames[i] = ServerName(gid, i)
}
return sg
}
func (sg *ServerGrp) N() int {
return len(sg.srvs)
}
func (sg *ServerGrp) SrvNames() []string {
return sg.servernames
}
func (sg *ServerGrp) SrvName(i int) string {
return sg.servernames[i]
}
func (sg *ServerGrp) Services() [][]IService {
ss := make([][]IService, 0, len(sg.srvs))
for _, s := range sg.srvs {
ss = append(ss, s.svcs)
}
return ss
}
func (sg *ServerGrp) SrvNamesTo(to []int) []string {
ns := make([]string, 0, len(to))
for _, i := range to {
ns = append(ns, sg.servernames[i])
}
return ns
}
func (sg *ServerGrp) all() []int {
all := make([]int, len(sg.srvs))
for i, _ := range sg.srvs {
all[i] = i
}
return all
}
func (sg *ServerGrp) ConnectAll() {
for i, _ := range sg.srvs {
sg.ConnectOne(i)
}
}
func (sg *ServerGrp) ConnectOne(i int) {
sg.connect(i, sg.all())
}
func (sg *ServerGrp) cleanup() {
for _, s := range sg.srvs {
if s.svcs != nil {
for _, svc := range s.svcs {
svc.Kill()
}
}
}
}
// attach server i to servers listed in to caller must hold cfg.mu.
func (sg *ServerGrp) connect(i int, to []int) {
//log.Printf("connect peer %d to %v\n", i, to)
sg.connected[i] = true
// connect outgoing end points
sg.srvs[i].connect(sg, to)
// connect incoming end points to me
for j := 0; j < len(to); j++ {
if sg.IsConnected(to[j]) {
//log.Printf("connect %d (%v) to %d", to[j], sg.srvs[to[j]].endNames[i], i)
endname := sg.srvs[to[j]].endNames[i]
sg.net.Enable(endname, true)
}
}
}
// detach server from the servers listed in from
// caller must hold cfg.mu
func (sg *ServerGrp) disconnect(i int, from []int) {
// log.Printf("%p: disconnect peer %d from %v\n", sg, i, from)
sg.mu.Lock()
sg.connected[i] = false
sg.mu.Unlock()
// outgoing socket files
sg.srvs[i].disconnect(from)
// incoming socket files
for j := 0; j < len(from); j++ {
s := sg.srvs[from[j]]
if s.endNames != nil {
endname := s.endNames[i]
// log.Printf("%p: disconnect: %v", sg, endname)
sg.net.Enable(endname, false)
}
}
}
func (sg *ServerGrp) DisconnectAll(i int) {
sg.disconnect(i, sg.all())
}
func (sg *ServerGrp) IsConnected(i int) bool {
defer sg.mu.Unlock()
sg.mu.Lock()
return sg.connected[i]
}
func (sg *ServerGrp) GetConnected() []bool {
return sg.connected
}
// Maximum log size across all servers
func (sg *ServerGrp) LogSize() int {
logsize := 0
for _, s := range sg.srvs {
n := s.saved.RaftStateSize()
if n > logsize {
logsize = n
}
}
return logsize
}
// Maximum snapshot size across all servers
func (sg *ServerGrp) SnapshotSize() int {
snapshotsize := 0
for _, s := range sg.srvs {
n := s.saved.SnapshotSize()
if n > snapshotsize {
snapshotsize = n
}
}
return snapshotsize
}
// If restart servers, first call shutdownserver
func (sg *ServerGrp) StartServer(i int) {
srv := sg.srvs[i].startServer(sg.gid)
sg.srvs[i] = srv
srv.svcs = sg.mks(srv.clntEnds, sg.gid, i, srv.saved)
labsrv := labrpc.MakeServer()
for _, svc := range srv.svcs {
s := labrpc.MakeService(svc)
labsrv.AddService(s)
}
sg.net.AddServer(ServerName(sg.gid, i), labsrv)
}
// create a full set of KV servers.
func (sg *ServerGrp) StartServers() {
sg.start()
sg.ConnectAll()
}
// Shutdown a server by isolating it
func (sg *ServerGrp) ShutdownServer(i int) {
//log.Printf("ShutdownServer %v", ServerName(sg.gid, i))
sg.disconnect(i, sg.all())
// disable client connections to the server.
// it's important to do this before creating
// the new Persister in saved[i], to avoid
// the possibility of the server returning a
// positive reply to an Append but persisting
// the result in the superseded Persister.
sg.net.DeleteServer(ServerName(sg.gid, i))
sg.srvs[i].shutdownServer()
}
func (sg *ServerGrp) Shutdown() {
for i, _ := range sg.srvs {
sg.ShutdownServer(i)
}
}
func (sg *ServerGrp) start() {
for i, _ := range sg.srvs {
sg.StartServer(i)
}
}
// Partition servers into 2 groups and put current leader in minority
func (sg *ServerGrp) MakePartition(l int) ([]int, []int) {
n := len(sg.srvs)
p1 := make([]int, n/2+1)
p2 := make([]int, n/2)
j := 0
for i := 0; i < n; i++ {
if i != l {
if j < len(p1) {
p1[j] = i
} else {
p2[j-len(p1)] = i
}
j++
}
}
p2[len(p2)-1] = l
return p1, p2
}
func (sg *ServerGrp) Partition(p1 []int, p2 []int) {
//log.Printf("partition servers into: %v %v\n", p1, p2)
for i := 0; i < len(p1); i++ {
sg.disconnect(p1[i], p2)
sg.connect(p1[i], p1)
}
for i := 0; i < len(p2); i++ {
sg.disconnect(p2[i], p1)
sg.connect(p2[i], p2)
}
}
func (sg *ServerGrp) RpcCount(server int) int {
return sg.net.GetCount(ServerName(sg.gid, server))
}