2021-12-15 15:07:15 -05:00
|
|
|
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
|
|
//
|
|
|
|
// This file is part of MinIO Object Storage stack
|
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU Affero General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2022-05-24 12:05:39 -04:00
|
|
|
"bufio"
|
|
|
|
"bytes"
|
2021-12-15 15:07:15 -05:00
|
|
|
"encoding/binary"
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"hash/crc32"
|
|
|
|
"log"
|
2022-05-24 12:05:39 -04:00
|
|
|
"os"
|
|
|
|
"strings"
|
2021-12-15 15:07:15 -05:00
|
|
|
|
|
|
|
"github.com/dchest/siphash"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
)
|
|
|
|
|
|
|
|
// hashes the key returning an integer based on the input algorithm.
|
|
|
|
// This function currently supports
|
|
|
|
// - SIPMOD
|
|
|
|
func sipHashMod(key string, cardinality int, id [16]byte) int {
|
|
|
|
if cardinality <= 0 {
|
|
|
|
return -1
|
|
|
|
}
|
|
|
|
// use the faster version as per siphash docs
|
|
|
|
// https://github.com/dchest/siphash#usage
|
|
|
|
k0, k1 := binary.LittleEndian.Uint64(id[0:8]), binary.LittleEndian.Uint64(id[8:16])
|
|
|
|
sum64 := siphash.Hash(k0, k1, []byte(key))
|
|
|
|
return int(sum64 % uint64(cardinality))
|
|
|
|
}
|
|
|
|
|
|
|
|
// hashOrder - hashes input key to return consistent
|
|
|
|
// hashed integer slice. Returned integer order is salted
|
|
|
|
// with an input key. This results in consistent order.
|
|
|
|
// NOTE: collisions are fine, we are not looking for uniqueness
|
|
|
|
// in the slices returned.
|
|
|
|
func hashOrder(key string, cardinality int) []int {
|
|
|
|
if cardinality <= 0 {
|
|
|
|
// Returns an empty int slice for cardinality < 0.
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
nums := make([]int, cardinality)
|
|
|
|
keyCrc := crc32.Checksum([]byte(key), crc32.IEEETable)
|
|
|
|
|
|
|
|
start := int(keyCrc % uint32(cardinality))
|
|
|
|
for i := 1; i <= cardinality; i++ {
|
|
|
|
nums[i-1] = 1 + ((start + i) % cardinality)
|
|
|
|
}
|
|
|
|
return nums
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
2022-05-24 12:05:39 -04:00
|
|
|
file, object, deploymentID, prefix string
|
|
|
|
setCount, shards int
|
|
|
|
verbose bool
|
2021-12-15 15:07:15 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
2022-05-24 12:05:39 -04:00
|
|
|
flag.StringVar(&file, "file", "", "Read all objects from file, newline separated")
|
|
|
|
flag.StringVar(&prefix, "prefix", "", "Add prefix to all objects")
|
2021-12-15 15:07:15 -05:00
|
|
|
flag.StringVar(&object, "object", "", "Select an object")
|
|
|
|
flag.StringVar(&deploymentID, "deployment-id", "", "MinIO deployment ID, obtained from 'format.json'")
|
|
|
|
flag.IntVar(&setCount, "set-count", 0, "Total set count")
|
|
|
|
flag.IntVar(&shards, "shards", 0, "Total shards count")
|
2022-05-24 12:05:39 -04:00
|
|
|
flag.BoolVar(&verbose, "v", false, "Display all objects")
|
2021-12-15 15:07:15 -05:00
|
|
|
|
|
|
|
flag.Parse()
|
|
|
|
|
|
|
|
if deploymentID == "" {
|
|
|
|
log.Fatalln("deployment ID is mandatory")
|
|
|
|
}
|
|
|
|
|
|
|
|
if setCount == 0 {
|
|
|
|
log.Fatalln("set count cannot be zero")
|
|
|
|
}
|
|
|
|
|
2022-05-24 12:05:39 -04:00
|
|
|
id := uuid.MustParse(deploymentID)
|
|
|
|
|
|
|
|
if file != "" {
|
|
|
|
distrib := make([][]string, setCount)
|
2022-09-19 14:05:16 -04:00
|
|
|
b, err := os.ReadFile(file)
|
2022-05-24 12:05:39 -04:00
|
|
|
if err != nil {
|
|
|
|
log.Fatalln(err)
|
|
|
|
}
|
|
|
|
b = bytes.ReplaceAll(b, []byte("\r"), []byte{})
|
|
|
|
sc := bufio.NewScanner(bytes.NewBuffer(b))
|
|
|
|
for sc.Scan() {
|
|
|
|
object = strings.TrimSpace(sc.Text())
|
|
|
|
set := sipHashMod(prefix+object, setCount, id)
|
|
|
|
distrib[set] = append(distrib[set], prefix+object)
|
|
|
|
}
|
|
|
|
for set, files := range distrib {
|
|
|
|
fmt.Println("Set:", set+1, "Objects:", len(files))
|
|
|
|
if !verbose {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
for _, s := range files {
|
|
|
|
fmt.Printf("\t%s\n", s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
os.Exit(0)
|
2021-12-15 15:07:15 -05:00
|
|
|
}
|
|
|
|
|
2022-05-24 12:05:39 -04:00
|
|
|
if object == "" {
|
|
|
|
log.Fatalln("object name is mandatory")
|
|
|
|
}
|
2021-12-15 15:07:15 -05:00
|
|
|
|
2022-05-24 12:05:39 -04:00
|
|
|
if shards != 0 {
|
|
|
|
fmt.Println("Erasure distribution for the object", hashOrder(prefix+object, shards))
|
|
|
|
}
|
|
|
|
fmt.Println("Erasure setNumber for the object", sipHashMod(prefix+object, setCount, id)+1)
|
2021-12-15 15:07:15 -05:00
|
|
|
}
|