예제 #1
0
// outputDotFile generates a .dot file describing the current state of
// the gossip network. nodes is a map from network address to gossip
// node. edgeSet is empty on the first invocation, but
// its content is set to encompass the entire set of edges in the
// network when this method returns. It should be resupplied with each
// successive invocation, as it is used to determine which edges are
// new and which have been deleted and show those changes visually in
// the output graph. New edges are drawn green; edges which were
// removed over the course of the last simulation step(s) are drawn in
// a lightly-dashed red.
//
// The format of the output looks like this:
//
//   digraph G {
//   node [shape=record];
//        node1 [fontsize=12,label="{Node 1|MH=3}"]
//        node1 -> node3 [color=green]
//        node1 -> node4
//        node1 -> node5 [color=red,style=dotted]
//        node2 [fontsize=24,label="{Node 2|MH=2}"]
//        node2 -> node5
//        node3 [fontsize=18,label="{Node 3|MH=5}"]
//        node3 -> node5
//        node3 -> node4
//        node4 [fontsize=24,label="{Node 4|MH=4}"]
//        node4 -> node2
//        node5 [fontsize=24,label="{Node 5|MH=1}"]
//        node5 -> node2
//        node5 -> node3
//   }
func outputDotFile(dotFN string, cycle int, nodes map[string]*gossip.Gossip, edgeSet map[string]edge) string {
	f, err := os.Create(dotFN)
	if err != nil {
		log.Fatalf("unable to create temp file: %s", err)
	}
	defer f.Close()

	// Determine maximum number of incoming connections. Create outgoing
	// edges, keeping track of which are new since last time (added=true).
	outgoingMap := make(edgeMap)
	sortedAddresses := util.MapKeys(nodes).([]string)
	sort.Strings(sortedAddresses)
	var maxIncoming int
	// The order the graph file is written influences the arrangement
	// of nodes in the output image, so it makes sense to eliminate
	// randomness here. Unfortunately with graphviz it's fairly hard
	// to get a consistent ordering.
	for _, addr := range sortedAddresses {
		node := nodes[addr]
		incoming := node.Incoming()
		for _, iAddr := range incoming {
			e := edge{dest: addr}
			key := fmt.Sprintf("%s:%s", iAddr.String(), addr)
			if _, ok := edgeSet[key]; !ok {
				e.added = true
			}
			delete(edgeSet, key)
			outgoingMap.addEdge(iAddr.String(), e)
		}
		if len(incoming) > maxIncoming {
			maxIncoming = len(incoming)
		}
	}

	// Find all edges which were deleted.
	for key, e := range edgeSet {
		e.added = false
		e.deleted = true
		outgoingMap.addEdge(strings.Split(key, ":")[0], e)
		delete(edgeSet, key)
	}

	f.WriteString("digraph G {\n")
	f.WriteString("node [shape=record];\n")
	for _, addr := range sortedAddresses {
		node := nodes[addr]
		var incomplete int
		var totalAge int64
		for infoKey := range nodes {
			if infoKey == addr {
				continue // skip the node's own info
			}
			if val, err := node.GetInfo(infoKey); err != nil {
				log.Infof("error getting info for key %q: %s", infoKey, err)
				incomplete++
			} else {
				totalAge += int64(cycle) - val.(int64)
			}
		}

		var sentinelAge int64
		if val, err := node.GetInfo(gossip.KeySentinel); err != nil {
			log.Infof("error getting info for sentinel gossip key %q: %s", gossip.KeySentinel, err)
		} else {
			sentinelAge = int64(cycle) - val.(int64)
		}

		var age, nodeColor string
		if incomplete > 0 {
			nodeColor = "color=red,"
			age = fmt.Sprintf("missing %d", incomplete)
		} else {
			age = strconv.FormatFloat(float64(totalAge)/float64(len(nodes)-1), 'f', 2, 64)
		}
		fontSize := minDotFontSize
		if maxIncoming > 0 {
			fontSize = minDotFontSize + int(math.Floor(float64(len(node.Incoming())*
				(maxDotFontSize-minDotFontSize))/float64(maxIncoming)))
		}
		f.WriteString(fmt.Sprintf("\t%s [%sfontsize=%d,label=\"{%s|MH=%d, AA=%s, SA=%d}\"]\n",
			node.Name, nodeColor, fontSize, node.Name, node.MaxHops(), age, sentinelAge))
		outgoing := outgoingMap[addr]
		for _, e := range outgoing {
			dest := nodes[e.dest]
			style := ""
			if e.added {
				style = " [color=green]"
			} else if e.deleted {
				style = " [color=red,style=dotted]"
			}
			f.WriteString(fmt.Sprintf("\t%s -> %s%s\n", node.Name, dest.Name, style))
			if !e.deleted {
				edgeSet[fmt.Sprintf("%s:%s", addr, e.dest)] = e
			}
		}
	}
	f.WriteString("}\n")
	return f.Name()
}
예제 #2
0
	ConditionalPut:        struct{}{},
	Increment:             struct{}{},
	Delete:                struct{}{},
	DeleteRange:           struct{}{},
	EndTransaction:        struct{}{},
	AccumulateTS:          struct{}{},
	ReapQueue:             struct{}{},
	EnqueueUpdate:         struct{}{},
	EnqueueMessage:        struct{}{},
	InternalHeartbeatTxn:  struct{}{},
	InternalPushTxn:       struct{}{},
	InternalResolveIntent: struct{}{},
}

// ReadMethods lists the read-only methods supported by a range.
var ReadMethods = util.MapKeys(readMethods).([]string)

// WriteMethods lists the methods supported by a range which write data.
var WriteMethods = util.MapKeys(writeMethods).([]string)

// Methods lists all the methods supported by a range.
var Methods = append(ReadMethods, WriteMethods...)

// NeedReadPerm returns true if the specified method requires read permissions.
func NeedReadPerm(method string) bool {
	_, ok := readMethods[method]
	return ok
}

// NeedWritePerm returns true if the specified method requires write permissions.
func NeedWritePerm(method string) bool {