2015-08-25 21:44:42 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/binary"
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"sort"
|
|
|
|
"strings"
|
|
|
|
"text/tabwriter"
|
|
|
|
|
|
|
|
"github.com/influxdb/influxdb/tsdb"
|
|
|
|
_ "github.com/influxdb/influxdb/tsdb/engine"
|
|
|
|
)
|
|
|
|
|
2015-10-13 23:26:23 +00:00
|
|
|
func usage() {
|
|
|
|
println(`Usage: influx_inspect <command> [options]
|
|
|
|
|
|
|
|
Displays detailed information about InfluxDB data files.
|
|
|
|
`)
|
|
|
|
|
|
|
|
println(`Commands:
|
|
|
|
info - displays series meta-data for all shards. Default location [$HOME/.influxdb]
|
|
|
|
dumptsm - dumps low-level details about tsm1 files.`)
|
|
|
|
println()
|
|
|
|
}
|
|
|
|
|
2015-08-25 21:44:42 +00:00
|
|
|
func main() {
|
|
|
|
|
2015-10-13 23:26:23 +00:00
|
|
|
flag.Usage = usage
|
2015-08-25 21:44:42 +00:00
|
|
|
flag.Parse()
|
|
|
|
|
2015-10-13 23:26:23 +00:00
|
|
|
if len(flag.Args()) == 0 {
|
|
|
|
flag.Usage()
|
|
|
|
os.Exit(0)
|
|
|
|
}
|
|
|
|
|
|
|
|
switch flag.Args()[0] {
|
|
|
|
case "info":
|
|
|
|
var path string
|
|
|
|
fs := flag.NewFlagSet("info", flag.ExitOnError)
|
|
|
|
fs.StringVar(&path, "dir", os.Getenv("HOME")+"/.influxdb", "Root storage path. [$HOME/.influxdb]")
|
|
|
|
|
|
|
|
fs.Usage = func() {
|
|
|
|
println("Usage: influx_inspect info [options]\n\n Displays series meta-data for all shards..")
|
|
|
|
println()
|
|
|
|
println("Options:")
|
|
|
|
fs.PrintDefaults()
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := fs.Parse(flag.Args()[1:]); err != nil {
|
|
|
|
fmt.Printf("%v", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
cmdInfo(path)
|
|
|
|
case "dumptsm":
|
2015-10-13 23:47:37 +00:00
|
|
|
|
|
|
|
var dumpAll bool
|
|
|
|
opts := &tsdmDumpOpts{}
|
2015-10-13 23:26:23 +00:00
|
|
|
fs := flag.NewFlagSet("file", flag.ExitOnError)
|
2015-10-14 15:24:54 +00:00
|
|
|
fs.BoolVar(&opts.dumpIndex, "index", false, "Dump raw index data")
|
|
|
|
fs.BoolVar(&opts.dumpBlocks, "blocks", false, "Dump raw block data")
|
|
|
|
fs.BoolVar(&dumpAll, "all", false, "Dump all data. Caution: This may print a lot of information")
|
2015-10-14 00:16:16 +00:00
|
|
|
fs.StringVar(&opts.filterKey, "filter-key", "", "Only display index and block data match this key substring")
|
2015-10-13 23:47:37 +00:00
|
|
|
|
2015-10-13 23:26:23 +00:00
|
|
|
fs.Usage = func() {
|
|
|
|
println("Usage: influx_inspect dumptsm [options] <path>\n\n Dumps low-level details about tsm1 files.")
|
|
|
|
println()
|
2015-10-13 23:47:37 +00:00
|
|
|
println("Options:")
|
|
|
|
fs.PrintDefaults()
|
|
|
|
os.Exit(0)
|
2015-10-13 23:26:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := fs.Parse(flag.Args()[1:]); err != nil {
|
|
|
|
fmt.Printf("%v", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(fs.Args()) == 0 || fs.Args()[0] == "" {
|
|
|
|
fmt.Printf("TSM file not specified\n\n")
|
|
|
|
fs.Usage()
|
|
|
|
fs.PrintDefaults()
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
2015-10-13 23:47:37 +00:00
|
|
|
opts.path = fs.Args()[0]
|
2015-10-14 00:16:16 +00:00
|
|
|
opts.dumpBlocks = opts.dumpBlocks || dumpAll || opts.filterKey != ""
|
|
|
|
opts.dumpIndex = opts.dumpIndex || dumpAll || opts.filterKey != ""
|
2015-10-13 23:47:37 +00:00
|
|
|
dumpTsm1(opts)
|
2015-10-10 04:32:11 +00:00
|
|
|
return
|
2015-10-13 23:26:23 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
flag.Usage()
|
|
|
|
os.Exit(1)
|
2015-10-10 04:32:11 +00:00
|
|
|
}
|
2015-10-13 23:26:23 +00:00
|
|
|
}
|
2015-10-10 04:32:11 +00:00
|
|
|
|
2015-10-13 23:26:23 +00:00
|
|
|
func cmdInfo(path string) {
|
2015-08-25 21:44:42 +00:00
|
|
|
tstore := tsdb.NewStore(filepath.Join(path, "data"))
|
|
|
|
tstore.Logger = log.New(ioutil.Discard, "", log.LstdFlags)
|
2015-08-27 15:37:10 +00:00
|
|
|
tstore.EngineOptions.Config.Dir = filepath.Join(path, "data")
|
2015-09-04 16:50:11 +00:00
|
|
|
tstore.EngineOptions.Config.WALLoggingEnabled = false
|
2015-08-25 21:44:42 +00:00
|
|
|
tstore.EngineOptions.Config.WALDir = filepath.Join(path, "wal")
|
|
|
|
if err := tstore.Open(); err != nil {
|
|
|
|
fmt.Printf("Failed to open dir: %v\n", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
size, err := tstore.DiskSize()
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("Failed to determine disk usage: %v\n", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Summary stats
|
|
|
|
fmt.Printf("Shards: %d, Indexes: %d, Databases: %d, Disk Size: %d, Series: %d\n",
|
|
|
|
tstore.ShardN(), tstore.DatabaseIndexN(), len(tstore.Databases()), size, countSeries(tstore))
|
|
|
|
fmt.Println()
|
|
|
|
|
|
|
|
tw := tabwriter.NewWriter(os.Stdout, 16, 8, 0, '\t', 0)
|
|
|
|
|
2015-08-27 15:37:10 +00:00
|
|
|
fmt.Fprintln(tw, strings.Join([]string{"Shard", "DB", "Measurement", "Tags [#K/#V]", "Fields [Name:Type]", "Series"}, "\t"))
|
2015-08-25 21:44:42 +00:00
|
|
|
|
|
|
|
shardIDs := tstore.ShardIDs()
|
|
|
|
|
|
|
|
databases := tstore.Databases()
|
|
|
|
sort.Strings(databases)
|
|
|
|
|
|
|
|
for _, db := range databases {
|
|
|
|
index := tstore.DatabaseIndex(db)
|
|
|
|
measurements := index.Measurements()
|
|
|
|
sort.Sort(measurements)
|
|
|
|
for _, m := range measurements {
|
|
|
|
tags := m.TagKeys()
|
|
|
|
tagValues := 0
|
|
|
|
for _, tag := range tags {
|
|
|
|
tagValues += len(m.TagValues(tag))
|
|
|
|
}
|
|
|
|
fields := m.FieldNames()
|
|
|
|
sort.Strings(fields)
|
|
|
|
series := m.SeriesKeys()
|
|
|
|
sort.Strings(series)
|
2015-08-27 15:37:10 +00:00
|
|
|
sort.Sort(ShardIDs(shardIDs))
|
2015-08-25 21:44:42 +00:00
|
|
|
|
|
|
|
// Sample a point from each measurement to determine the field types
|
|
|
|
for _, shardID := range shardIDs {
|
|
|
|
shard := tstore.Shard(shardID)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("Failed to get transaction: %v", err)
|
|
|
|
}
|
|
|
|
|
2015-10-10 04:32:11 +00:00
|
|
|
codec := shard.FieldCodec(m.Name)
|
|
|
|
for _, field := range codec.Fields() {
|
|
|
|
ft := fmt.Sprintf("%s:%s", field.Name, field.Type)
|
|
|
|
fmt.Fprintf(tw, "%d\t%s\t%s\t%d/%d\t%d [%s]\t%d\n", shardID, db, m.Name, len(tags), tagValues,
|
|
|
|
len(fields), ft, len(series))
|
|
|
|
|
2015-08-25 21:44:42 +00:00
|
|
|
}
|
2015-10-10 04:32:11 +00:00
|
|
|
|
2015-08-25 21:44:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tw.Flush()
|
|
|
|
}
|
|
|
|
|
|
|
|
func countSeries(tstore *tsdb.Store) int {
|
|
|
|
var count int
|
|
|
|
for _, shardID := range tstore.ShardIDs() {
|
|
|
|
shard := tstore.Shard(shardID)
|
|
|
|
cnt, err := shard.SeriesCount()
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("series count failed: %v\n", err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
count += cnt
|
|
|
|
}
|
|
|
|
return count
|
|
|
|
}
|
|
|
|
|
|
|
|
func btou64(b []byte) uint64 {
|
|
|
|
return binary.BigEndian.Uint64(b)
|
|
|
|
}
|
|
|
|
|
|
|
|
// u64tob converts a uint64 into an 8-byte slice.
|
|
|
|
func u64tob(v uint64) []byte {
|
|
|
|
b := make([]byte, 8)
|
|
|
|
binary.BigEndian.PutUint64(b, v)
|
|
|
|
return b
|
|
|
|
}
|
2015-08-27 15:37:10 +00:00
|
|
|
|
|
|
|
type ShardIDs []uint64
|
|
|
|
|
|
|
|
func (a ShardIDs) Len() int { return len(a) }
|
|
|
|
func (a ShardIDs) Less(i, j int) bool { return a[i] < a[j] }
|
|
|
|
func (a ShardIDs) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|