func (c *etcdCtx) GetINodeIndex(vid torus.VolumeID) (torus.INodeID, error) {
	promOps.WithLabelValues("get-inode-index").Inc()
	c.etcd.mut.Lock()
	defer c.etcd.mut.Unlock()
	resp, err := c.etcd.Client.Get(c.getContext(), MkKey("volumemeta", Uint64ToHex(uint64(vid)), "inode"))
	if err != nil {
		return torus.INodeID(0), err
	}
	if len(resp.Kvs) != 1 {
		return torus.INodeID(0), torus.ErrNotExist
	}
	id := BytesToUint64(resp.Kvs[0].Value)
	return torus.INodeID(id), nil
}
func TestRebalanceCheck(t *testing.T) {
	test := make([]torus.BlockRef, nchecks)
	m := &mockBlockRPC{}
	for i, _ := range test {
		test[i].Index = 3
		test[i].INodeRef = torus.NewINodeRef(1, torus.INodeID(rand.Intn(40)))
	}
	s, err := Serve("localhost:0", m, m.BlockSize())
	if err != nil {
		t.Fatal(err)
	}
	defer s.Close()
	c, err := Dial(s.ListenAddr().String(), time.Second, m.BlockSize())
	if err != nil {
		t.Fatal(err)
	}
	defer c.Close()
	bs, err := c.RebalanceCheck(context.TODO(), test)
	if err != nil {
		t.Fatal(err)
	}
	for i, x := range bs {
		if x {
			if test[i].INode%2 == 0 {
				t.Fatal("unequal")
			}
		} else {
			if test[i].INode%2 == 1 {
				t.Fatal("unequal")
			}
		}
	}
}
func (c *etcdCtx) CommitINodeIndex(vid torus.VolumeID) (torus.INodeID, error) {
	promOps.WithLabelValues("commit-inode-index").Inc()
	c.etcd.mut.Lock()
	defer c.etcd.mut.Unlock()
	k := []byte(MkKey("volumemeta", Uint64ToHex(uint64(vid)), "inode"))
	newID, err := c.AtomicModifyKey(k, BytesAddOne)
	if err != nil {
		return 0, err
	}
	return torus.INodeID(newID.(uint64)), nil
}
func main() {
	var err error
	flag.Parse()
	if *replicationEnd == 0 {
		*replicationEnd = *replication
	}
	nPeers := *nodes + *delta
	if *delta <= 0 {
		nPeers = *nodes
	}
	peers = make([]*models.PeerInfo, nPeers)
	for i := 0; i < nPeers; i++ {
		u, _ := metadata.MakeOrGetUUID("")
		peers[i] = &models.PeerInfo{
			UUID:        u,
			TotalBlocks: 100 * 1024 * 1024 * 1024, // 100giga-blocks for testing
		}
	}
	blockSize, err = humanize.ParseBytes(*blockSizeStr)
	if err != nil {
		fmt.Fprintf(os.Stderr, "error parsing block-size: %s\n", err)
		os.Exit(1)
	}
	totalData, err = humanize.ParseBytes(*totalDataStr)
	if err != nil {
		fmt.Fprintf(os.Stderr, "error parsing total-data: %s\n", err)
		os.Exit(1)
	}
	nblocks := totalData / blockSize
	var blocks []torus.BlockRef
	inode := torus.INodeID(1)
	part := float64(*partition) / 100.0
	for len(blocks) < int(nblocks) {
		perFile := rand.Intn(1000) + 1
		f := rand.NormFloat64()
		var out []torus.BlockRef
		if f < part {
			out, inode = generateRewrittenFile(torus.VolumeID(1), inode, perFile)
		} else {
			out, inode = generateLinearFile(torus.VolumeID(1), inode, perFile)
		}
		blocks = append(blocks, out...)
	}
	r1, r2 := createRings()
	fmt.Printf("Unique blocks: %d\n", len(blocks))
	cluster := assignData(blocks, r1)
	fmt.Println("@START *****")
	cluster.printBalance()
	newc, rebalance := cluster.Rebalance(r1, r2)
	fmt.Println("@END *****")
	newc.printBalance()
	fmt.Println("Changes:")
	rebalance.printStats()
}
func BenchmarkGRPCRebalanceCheck(b *testing.B) {
	test := make([]torus.BlockRef, nchecks)
	m := &mockBlockGRPC{}
	for i, _ := range test {
		test[i].Index = 3
		test[i].INodeRef = torus.NewINodeRef(1, torus.INodeID(rand.Intn(40)))
	}
	lis, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		b.Fatal(err)
	}
	grpcSrv := grpc.NewServer()
	models.RegisterTorusStorageServer(grpcSrv, m)
	go grpcSrv.Serve(lis)
	defer grpcSrv.Stop()
	conn, err := grpc.Dial(lis.Addr().String(), grpc.WithInsecure(), grpc.WithTimeout(10*time.Second))
	if err != nil {
		b.Fatal(err)
	}
	defer conn.Close()
	client := models.NewTorusStorageClient(conn)
	total := 0
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		refs := make([]*models.BlockRef, 0, len(test))
		for _, x := range test {
			refs = append(refs, x.ToProto())
		}
		resp, err := client.RebalanceCheck(context.TODO(), &models.RebalanceCheckRequest{
			BlockRefs: refs,
		})
		if err != nil {
			b.Fatal(err)
		}
		for i, x := range resp.Valid {
			if x {
				if test[i].INode%2 == 0 {
					b.Fatal("unequal")
				}
			} else {
				if test[i].INode%2 == 1 {
					b.Fatal("unequal")
				}
			}
		}
		total += len(resp.Valid)
	}
	b.SetBytes(int64(total / b.N))
}
func BenchmarkRebalanceCheck(b *testing.B) {
	test := make([]torus.BlockRef, nchecks)
	m := &mockBlockRPC{}
	for i, _ := range test {
		test[i].Index = 3
		test[i].INodeRef = torus.NewINodeRef(1, torus.INodeID(rand.Intn(40)))
	}
	s, err := Serve("localhost:0", m, m.BlockSize())
	if err != nil {
		b.Fatal(err)
	}
	defer s.Close()
	c, err := Dial(s.ListenAddr().String(), time.Second, m.BlockSize())
	if err != nil {
		b.Fatal(err)
	}
	defer c.Close()
	total := 0
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		bs, err := c.RebalanceCheck(context.TODO(), test)
		if err != nil {
			b.Fatal(err)
		}
		for i, x := range bs {
			if x {
				if test[i].INode%2 == 0 {
					b.Fatal("unequal")
				}
			} else {
				if test[i].INode%2 == 1 {
					b.Fatal("unequal")
				}
			}
		}
		total += len(bs)
	}
	b.SetBytes(int64(total / b.N))
}