package migration import ( "fmt" "io/ioutil" "os" "path/filepath" "sort" "time" "github.com/influxdb/influxdb/cluster" "github.com/influxdb/influxdb/configuration" "github.com/influxdb/influxdb/coordinator" "github.com/influxdb/influxdb/engine" "github.com/influxdb/influxdb/metastore" "github.com/influxdb/influxdb/parser" "github.com/influxdb/influxdb/protocol" log "code.google.com/p/log4go" // "github.com/BurntSushi/toml" "github.com/jmhodges/levigo" ) // Used for migrating data from old versions of influx. type DataMigrator struct { baseDbDir string dbDir string metaStore *metastore.Store config *configuration.Configuration clusterConfig *cluster.ClusterConfiguration coord *coordinator.CoordinatorImpl pauseTime time.Duration } const ( MIGRATED_MARKER = "MIGRATED" OLD_SHARD_DIR = "shard_db" POINT_COUNT_TO_PAUSE = 10000 ) var ( endStreamResponse = protocol.Response_END_STREAM ) func NewDataMigrator(coord *coordinator.CoordinatorImpl, clusterConfig *cluster.ClusterConfiguration, config *configuration.Configuration, baseDbDir, newSubDir string, metaStore *metastore.Store, pauseTime time.Duration) *DataMigrator { return &DataMigrator{ baseDbDir: baseDbDir, dbDir: filepath.Join(baseDbDir, OLD_SHARD_DIR), metaStore: metaStore, config: config, clusterConfig: clusterConfig, coord: coord, pauseTime: pauseTime, } } func (dm *DataMigrator) Migrate() { log.Info("Migrating from dir %s", dm.dbDir) infos, err := ioutil.ReadDir(dm.dbDir) if err != nil { log.Error("Error Migrating: ", err) return } names := make([]string, 0) for _, info := range infos { if info.IsDir() { names = append(names, info.Name()) } } sort.Strings(names) // go through in reverse order so most recently created shards will be migrated first for i := len(names) - 1; i >= 0; i-- { dm.migrateDir(names[i]) } } func (dm *DataMigrator) migrateDir(name string) { migrateMarkerFile := filepath.Join(dm.shardDir(name), MIGRATED_MARKER) if _, err := os.Stat(migrateMarkerFile); err == nil { log.Info("Already migrated %s. Skipping", name) return } log.Info("Migrating %s", name) shard, err := dm.getShard(name) if err != nil { log.Error("Migration error getting shard: %s", err.Error()) return } defer shard.Close() databases := dm.clusterConfig.GetDatabases() for _, database := range databases { err := dm.migrateDatabaseInShard(database.Name, shard) if err != nil { log.Error("Error migrating database %s: %s", database.Name, err.Error()) return } } err = ioutil.WriteFile(migrateMarkerFile, []byte("done.\n"), 0644) if err != nil { log.Error("Problem writing migration marker for shard %s: %s", name, err.Error()) } } func (dm *DataMigrator) migrateDatabaseInShard(database string, shard *LevelDbShard) error { log.Info("Migrating database %s for shard", database) seriesNames := shard.GetSeriesForDatabase(database) log.Info("Migrating %d series", len(seriesNames)) admin := dm.clusterConfig.GetClusterAdmin(dm.clusterConfig.GetClusterAdmins()[0]) pointCount := 0 for _, series := range seriesNames { q, err := parser.ParseQuery(fmt.Sprintf("select * from \"%s\"", series)) if err != nil { log.Error("Problem migrating series %s", series) continue } query := q[0] seriesChan := make(chan *protocol.Response) queryEngine := engine.NewPassthroughEngine(seriesChan, 2000) querySpec := parser.NewQuerySpec(admin, database, query) go func() { err := shard.Query(querySpec, queryEngine) if err != nil { log.Error("Error migrating %s", err.Error()) } queryEngine.Close() seriesChan <- &protocol.Response{Type: &endStreamResponse} }() for { response := <-seriesChan if *response.Type == endStreamResponse { break } err := dm.coord.WriteSeriesData(admin, database, []*protocol.Series{response.Series}) if err != nil { log.Error("Writing Series data: %s", err.Error()) } pointCount += len(response.Series.Points) if pointCount > POINT_COUNT_TO_PAUSE { pointCount = 0 time.Sleep(dm.pauseTime) } } } log.Info("Done migrating %s for shard", database) return nil } func (dm *DataMigrator) shardDir(name string) string { return filepath.Join(dm.baseDbDir, OLD_SHARD_DIR, name) } func (dm *DataMigrator) getShard(name string) (*LevelDbShard, error) { dbDir := dm.shardDir(name) cache := levigo.NewLRUCache(int(2000)) opts := levigo.NewOptions() opts.SetCache(cache) opts.SetCreateIfMissing(true) opts.SetMaxOpenFiles(1000) ldb, err := levigo.Open(dbDir, opts) if err != nil { return nil, err } return NewLevelDbShard(ldb, dm.config.StoragePointBatchSize, dm.config.StorageWriteBatchSize) }