github.com/rohankumardubey/aresdb@v0.0.2-0.20190517170215-e54e3ca06b9c/diskstore/local_diskstore.go (about)

     1  //  Copyright (c) 2017-2018 Uber Technologies, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package diskstore
    16  
    17  import (
    18  	"io"
    19  	"io/ioutil"
    20  	"os"
    21  	"path/filepath"
    22  	"regexp"
    23  	"sort"
    24  	"strconv"
    25  	"strings"
    26  	"time"
    27  
    28  	"github.com/uber/aresdb/common"
    29  	"github.com/uber/aresdb/utils"
    30  )
    31  
    32  // LocalDiskStore is the implementation of Diskstore for local disk.
    33  type LocalDiskStore struct {
    34  	rootPath        string
    35  	diskStoreConfig common.DiskStoreConfig
    36  }
    37  
    38  // NewLocalDiskStore is used to init a LocalDiskStore with rootPath.
    39  func NewLocalDiskStore(rootPath string) DiskStore {
    40  	return LocalDiskStore{
    41  		rootPath:        rootPath,
    42  		diskStoreConfig: utils.GetConfig().DiskStore,
    43  	}
    44  }
    45  
    46  const timeFormatForBatchID = "2006-01-02"
    47  
    48  // Table shard level operation
    49  
    50  // DeleteTableShard : Completely wipe out a table shard.
    51  func (l LocalDiskStore) DeleteTableShard(table string, shard int) error {
    52  	tableRedologDir := getPathForTableShard(l.rootPath, table, shard)
    53  	return os.RemoveAll(tableRedologDir)
    54  }
    55  
    56  // Redo Logs
    57  
    58  // ListLogFiles : Returns the file creation unix time in second for each log file as a sorted slice.
    59  func (l LocalDiskStore) ListLogFiles(table string, shard int) (creationUnixTime []int64, err error) {
    60  	tableRedologDir := GetPathForTableRedologs(l.rootPath, table, shard)
    61  	redologsFiles, err := ioutil.ReadDir(tableRedologDir)
    62  	// The redo log directory won't get created until the first append call.
    63  	if os.IsNotExist(err) {
    64  		err = nil
    65  		return
    66  	}
    67  	if err != nil {
    68  		err = utils.StackError(err, "Failed to list redolog file for redolog dir: %s", tableRedologDir)
    69  		return
    70  	}
    71  	for _, f := range redologsFiles {
    72  		matchedRedologFilePattern, _ := regexp.MatchString("([0-9]+).redolog", f.Name())
    73  		if matchedRedologFilePattern {
    74  			creationTime, err := strconv.ParseInt(strings.Split(f.Name(), ".")[0], 10, 64)
    75  			if err != nil {
    76  				// Failed to parse redolog file, will continue
    77  				utils.GetLogger().Debugf("Failed to parse redolog file: %s, will continue", f.Name())
    78  				continue
    79  			}
    80  			creationUnixTime = append(creationUnixTime, creationTime)
    81  		}
    82  	}
    83  	sort.Sort(utils.Int64Array(creationUnixTime))
    84  	return creationUnixTime, nil
    85  }
    86  
    87  // OpenLogFileForReplay : Opens the specified log file for replay.
    88  func (l LocalDiskStore) OpenLogFileForReplay(table string, shard int,
    89  	creationTime int64) (utils.ReaderSeekerCloser, error) {
    90  	logFilePath := GetPathForRedologFile(l.rootPath, table, shard, creationTime)
    91  	f, err := os.OpenFile(logFilePath, os.O_RDONLY, 0644)
    92  	if err != nil {
    93  		return nil, utils.StackError(err, "Failed to open redolog file: %s for replay", logFilePath)
    94  	}
    95  	return f, nil
    96  }
    97  
    98  // OpenLogFileForAppend : Opens/creates the specified log file for append.
    99  func (l LocalDiskStore) OpenLogFileForAppend(table string, shard int, creationTime int64) (io.WriteCloser, error) {
   100  	tableRedologDir := GetPathForTableRedologs(l.rootPath, table, shard)
   101  	if err := os.MkdirAll(tableRedologDir, 0755); err != nil {
   102  		return nil, utils.StackError(err, "Failed to make dirs for path: %s", tableRedologDir)
   103  	}
   104  	logFilePath := GetPathForRedologFile(l.rootPath, table, shard, creationTime)
   105  	mode := os.O_APPEND | os.O_CREATE | os.O_WRONLY
   106  	if l.diskStoreConfig.WriteSync {
   107  		mode |= os.O_SYNC
   108  	}
   109  	f, err := os.OpenFile(logFilePath, mode, 0644)
   110  	if err != nil {
   111  		return nil, utils.StackError(err, "Failed to open redolog file: %s for append", logFilePath)
   112  	}
   113  	return f, nil
   114  }
   115  
   116  // DeleteLogFile is used to delete a specified redolog.
   117  func (l LocalDiskStore) DeleteLogFile(table string, shard int, creationTime int64) error {
   118  	redologFilePath := GetPathForRedologFile(l.rootPath, table, shard, creationTime)
   119  	err := os.Remove(redologFilePath)
   120  	if err != nil {
   121  		return utils.StackError(err, "Failed to delete redolog file: %s", redologFilePath)
   122  	}
   123  	utils.GetLogger().With("action", "deletelogfile", "table", table, "shard", shard).Infof("Delete redolog file: %s", redologFilePath)
   124  
   125  	return nil
   126  }
   127  
   128  // TruncateLogFile is used to truncate redolog to drop the last incomplete/corrupted upsert batch.
   129  func (l LocalDiskStore) TruncateLogFile(table string, shard int, creationTime int64, offset int64) error {
   130  	redologFilePath := GetPathForRedologFile(l.rootPath, table, shard, creationTime)
   131  	err := os.Truncate(redologFilePath, offset)
   132  	return err
   133  }
   134  
   135  // Snapshot files.
   136  
   137  // ListSnapshotBatches : Returns the batch directories at the specified version.
   138  func (l LocalDiskStore) ListSnapshotBatches(table string, shard int,
   139  	redoLogFile int64, offset uint32) (batches []int, err error) {
   140  	snapshotPath := GetPathForTableSnapshotDirPath(l.rootPath, table, shard, redoLogFile, offset)
   141  	batchDirs, err := ioutil.ReadDir(snapshotPath)
   142  	// No batches for this snapshot
   143  	if os.IsNotExist(err) {
   144  		err = nil
   145  		return
   146  	}
   147  	if err != nil {
   148  		err = utils.StackError(err, "Failed to list batch dirs for snapshot dir: %s", snapshotPath)
   149  		return
   150  	}
   151  	for _, f := range batchDirs {
   152  		batch, err := strconv.ParseInt(f.Name(), 10, 32)
   153  		if err != nil {
   154  			return nil, utils.StackError(err, "Failed to parse dir name: %s as valid snapshot batch dir",
   155  				f.Name())
   156  		}
   157  		batches = append(batches, int(batch))
   158  	}
   159  	sort.Ints(batches)
   160  	return batches, nil
   161  }
   162  
   163  // ListSnapshotVectorPartyFiles : Returns the vector party files under specific batch directory.
   164  func (l LocalDiskStore) ListSnapshotVectorPartyFiles(table string, shard int,
   165  	redoLogFile int64, offset uint32, batchID int) (columnIDs []int, err error) {
   166  	snapshotBatchDir := GetPathForTableSnapshotBatchDir(l.rootPath, table, shard,
   167  		redoLogFile, offset, batchID)
   168  	vpFiles, err := ioutil.ReadDir(snapshotBatchDir)
   169  
   170  	if os.IsNotExist(err) {
   171  		err = nil
   172  		return
   173  	}
   174  
   175  	if err != nil {
   176  		err = utils.StackError(err, "Failed to list vp file for snapshot batch dir: %s", snapshotBatchDir)
   177  		return
   178  	}
   179  
   180  	for _, f := range vpFiles {
   181  		matchedVectorPartyFilePattern, _ := regexp.MatchString("([0-9]+).data", f.Name())
   182  		if matchedVectorPartyFilePattern {
   183  			var columnID int64
   184  			columnID, err = strconv.ParseInt(strings.Split(f.Name(), ".")[0], 10, 32)
   185  			if err != nil {
   186  				err = utils.StackError(err, "Failed to parse file name: %s as "+
   187  					"valid snapshot vector party file name",
   188  					f.Name())
   189  				return
   190  			}
   191  			columnIDs = append(columnIDs, int(columnID))
   192  		} else {
   193  			err = utils.StackError(nil, "Failed to parse file name: %s as "+
   194  				"valid snapshot vector party file name",
   195  				f.Name())
   196  			return
   197  		}
   198  	}
   199  	sort.Ints(columnIDs)
   200  	return
   201  }
   202  
   203  // OpenSnapshotVectorPartyFileForRead : Opens the snapshot file for read at the specified version.
   204  func (l LocalDiskStore) OpenSnapshotVectorPartyFileForRead(table string, shard int,
   205  	redoLogFile int64, offset uint32, batchID int, columnID int) (io.ReadCloser, error) {
   206  	snapshotFilePath := GetPathForTableSnapshotColumnFilePath(l.rootPath, table, shard, redoLogFile, offset, batchID, columnID)
   207  	f, err := os.OpenFile(snapshotFilePath, os.O_RDONLY, 0644)
   208  	if err != nil {
   209  		return nil, utils.StackError(err, "Failed to open snapshot file: %s for read", snapshotFilePath)
   210  	}
   211  	return f, nil
   212  }
   213  
   214  // OpenSnapshotVectorPartyFileForWrite : Creates/truncates the snapshot file for write at the specified version.
   215  func (l LocalDiskStore) OpenSnapshotVectorPartyFileForWrite(table string, shard int,
   216  	redoLogFile int64, offset uint32, batchID int, columnID int) (io.WriteCloser, error) {
   217  	snapshotFilePath := GetPathForTableSnapshotColumnFilePath(l.rootPath, table, shard, redoLogFile, offset, batchID, columnID)
   218  	dir := filepath.Dir(snapshotFilePath)
   219  	if err := os.MkdirAll(dir, 0755); err != nil {
   220  		return nil, utils.StackError(err, "Failed to make dirs for path: %s", dir)
   221  	}
   222  	mode := os.O_CREATE | os.O_WRONLY
   223  	if l.diskStoreConfig.WriteSync {
   224  		mode |= os.O_SYNC
   225  	}
   226  	f, err := os.OpenFile(snapshotFilePath, mode, 0644)
   227  	os.Truncate(snapshotFilePath, 0)
   228  	if err != nil {
   229  		return nil, utils.StackError(err, "Failed to open snapshot file: %s for write", snapshotFilePath)
   230  	}
   231  	return f, nil
   232  }
   233  
   234  // DeleteSnapshot : Deletes snapshot directories **older than** the specified version (redolog file and offset).
   235  func (l LocalDiskStore) DeleteSnapshot(table string, shard int, latestRedoLogFile int64, latestOffset uint32) error {
   236  	tableSnapshotDir := GetPathForTableSnapshotDir(l.rootPath, table, shard)
   237  	tableSnapshotFiles, err := ioutil.ReadDir(tableSnapshotDir)
   238  
   239  	if os.IsNotExist(err) {
   240  		return nil
   241  	}
   242  
   243  	if err != nil {
   244  		return utils.StackError(err, "Failed to list snapshot files for snapshot dir: %s", tableSnapshotDir)
   245  	}
   246  
   247  	for _, f := range tableSnapshotFiles {
   248  		matchedSnapshotDirPattern, _ := regexp.MatchString("([0-9]+)_([0-9]+)", f.Name())
   249  		if matchedSnapshotDirPattern {
   250  			comps := strings.Split(f.Name(), "_")
   251  			redoLogFile, err := strconv.ParseInt(comps[0], 10, 64)
   252  			if err != nil {
   253  				err = nil
   254  				// Failed to parse snapshot file name, will skip.
   255  				utils.GetLogger().Debugf("Failed to parse latestRedoLogFile from snapshot file: %s, will continue", f.Name())
   256  				continue
   257  			}
   258  
   259  			offset, err := strconv.ParseUint(comps[1], 10, 32)
   260  			if err != nil {
   261  				err = nil
   262  				// Failed to parse snapshot file name, will skip.
   263  				utils.GetLogger().Debugf("Failed to parse offset from snapshot file: %s, will continue", f.Name())
   264  				continue
   265  			}
   266  
   267  			if redoLogFile < latestRedoLogFile || (redoLogFile == latestRedoLogFile && uint32(offset) < latestOffset) {
   268  				snapshotToDeleteFilePath := GetPathForTableSnapshotDirPath(l.rootPath, table, shard, redoLogFile, uint32(offset))
   269  				utils.GetLogger().With(
   270  					"action", "delete_snapshot",
   271  					"redoLog", latestRedoLogFile,
   272  					"offset", latestOffset).Infof("delete snapshot: %s", snapshotToDeleteFilePath)
   273  				err := os.RemoveAll(snapshotToDeleteFilePath)
   274  				if err != nil {
   275  					return utils.StackError(err, "Failed to delete snapshot file: %s", f.Name())
   276  				}
   277  			}
   278  		}
   279  	}
   280  	return nil
   281  }
   282  
   283  // Archived vector party files.
   284  
   285  // OpenVectorPartyFileForRead : Opens the vector party file at the specified batchVersion for read.
   286  func (l LocalDiskStore) OpenVectorPartyFileForRead(table string, columnID int, shard, batchID int, batchVersion uint32,
   287  	seqNum uint32) (io.ReadCloser, error) {
   288  	batchIDTimeStr := daysSinceEpochToTimeStr(batchID)
   289  	vectorPartyFilePath := GetPathForTableArchiveBatchColumnFile(l.rootPath, table, shard, batchIDTimeStr, batchVersion,
   290  		seqNum, columnID)
   291  	f, err := os.OpenFile(vectorPartyFilePath, os.O_RDONLY, 0644)
   292  	if os.IsNotExist(err) {
   293  		return nil, nil
   294  	} else if err != nil {
   295  		return nil, utils.StackError(err, "Failed to open vector party file: %s for read", vectorPartyFilePath)
   296  	}
   297  	return f, nil
   298  }
   299  
   300  // OpenVectorPartyFileForWrite : Creates/truncates the vector party file at the specified batchVersion for write.
   301  func (l LocalDiskStore) OpenVectorPartyFileForWrite(table string, columnID int, shard, batchID int, batchVersion uint32,
   302  	seqNum uint32) (io.WriteCloser, error) {
   303  	batchIDTimeStr := daysSinceEpochToTimeStr(batchID)
   304  	batchDir := GetPathForTableArchiveBatchDir(l.rootPath, table, shard, batchIDTimeStr, batchVersion, seqNum)
   305  	if err := os.MkdirAll(batchDir, 0755); err != nil {
   306  		return nil, utils.StackError(err, "Failed to make dirs for path: %s", batchDir)
   307  	}
   308  	vectorPartyFilePath := GetPathForTableArchiveBatchColumnFile(l.rootPath, table, shard, batchIDTimeStr, batchVersion,
   309  		seqNum, columnID)
   310  
   311  	mode := os.O_CREATE | os.O_WRONLY
   312  	if l.diskStoreConfig.WriteSync {
   313  		mode |= os.O_SYNC
   314  	}
   315  
   316  	f, err := os.OpenFile(vectorPartyFilePath, mode, 0644)
   317  	if err != nil {
   318  		return nil, utils.StackError(err, "Failed to open vector party file: %s for write", vectorPartyFilePath)
   319  	}
   320  	return f, nil
   321  }
   322  
   323  // DeleteBatchVersions deletes all old batches with the specified batchID that have version lower than or equal to
   324  // the specified batch  version. All columns of those batches will be deleted.
   325  func (l LocalDiskStore) DeleteBatchVersions(table string, shard, batchID int, batchVersion uint32, seqNum uint32) error {
   326  	batchIDTimeStr := daysSinceEpochToTimeStr(batchID)
   327  	archiveBatchRootDir := GetPathForTableArchiveBatchRootDir(l.rootPath, table, shard)
   328  	oldBatchDirPaths, _ := filepath.Glob(filepath.Join(archiveBatchRootDir, batchIDTimeStr) + "_*")
   329  	for _, oldBatchDirPath := range oldBatchDirPaths {
   330  		oldBatchInfoStr := filepath.Base(oldBatchDirPath)
   331  		_, oldBatchVersion, oldSeqNum, _ := ParseBatchIDAndVersionName(oldBatchInfoStr)
   332  		if oldBatchVersion < batchVersion || (oldBatchVersion == batchVersion && oldSeqNum <= seqNum) {
   333  			err := os.RemoveAll(oldBatchDirPath)
   334  			if err != nil {
   335  				return utils.StackError(err, "Failed to delete batch directory: %s", oldBatchDirPath)
   336  			}
   337  		}
   338  	}
   339  	return nil
   340  }
   341  
   342  // DeleteBatches : Deletes all batches within [batchIDStart, batchIDEnd)
   343  func (l LocalDiskStore) DeleteBatches(table string, shard, batchIDStart, batchIDEnd int) (int, error) {
   344  	batchIDStartTime := daysSinceEpochToTime(batchIDStart)
   345  	batchIDEndTime := daysSinceEpochToTime(batchIDEnd)
   346  	tableArchiveBatchRootDir := GetPathForTableArchiveBatchRootDir(l.rootPath, table, shard)
   347  	tableArchiveBatchDirs, err := ioutil.ReadDir(tableArchiveBatchRootDir)
   348  
   349  	if os.IsNotExist(err) {
   350  		return 0, nil
   351  	}
   352  
   353  	if err != nil {
   354  		if os.IsNotExist(err) {
   355  			return 0, nil
   356  		}
   357  		return 0, utils.StackError(err, "Failed to list archive batches from table archive batch root dir: %s",
   358  			tableArchiveBatchRootDir)
   359  	}
   360  
   361  	numBatches := 0
   362  	for _, f := range tableArchiveBatchDirs {
   363  		batchID, batchVersion, seqNum, _ := ParseBatchIDAndVersionName(f.Name())
   364  		batchIDTime, err := time.Parse(timeFormatForBatchID, batchID)
   365  		if err != nil {
   366  			utils.GetLogger().Debugf("Failed to parse batchID: %s to yyyy-MM-dd format time", batchID)
   367  			continue
   368  		}
   369  		batchIDTime = batchIDTime.UTC()
   370  		if !batchIDTime.Before(batchIDStartTime) && batchIDTime.Before(batchIDEndTime) {
   371  			archiveBatchDir := GetPathForTableArchiveBatchDir(l.rootPath, table, shard, batchID, batchVersion, seqNum)
   372  			err := os.RemoveAll(archiveBatchDir)
   373  			if err != nil {
   374  				utils.GetLogger().Debugf("Failed to delete archive batch dir: %s", archiveBatchDir)
   375  			} else {
   376  				numBatches++
   377  			}
   378  		}
   379  	}
   380  	return numBatches, nil
   381  }
   382  
   383  // DeleteColumn : Deletes all batches of the specified column.
   384  func (l LocalDiskStore) DeleteColumn(table string, columnID int, shard int) error {
   385  	tableArchiveBatchRootDir := GetPathForTableArchiveBatchRootDir(l.rootPath, table, shard)
   386  	tableArchiveBatchDirs, err := ioutil.ReadDir(tableArchiveBatchRootDir)
   387  
   388  	if os.IsNotExist(err) {
   389  		return nil
   390  	}
   391  
   392  	if err != nil {
   393  		if os.IsNotExist(err) {
   394  			return nil
   395  		}
   396  		return utils.StackError(err, "Failed to list archive batches from table archive batch root dir: %s",
   397  			tableArchiveBatchRootDir)
   398  	}
   399  	for _, f := range tableArchiveBatchDirs {
   400  		if f.IsDir() {
   401  			if batchID, batchVersion, seqNum, err := ParseBatchIDAndVersionName(f.Name()); err == nil {
   402  				vectorPartyFilePath := GetPathForTableArchiveBatchColumnFile(l.rootPath, table, shard, batchID,
   403  					batchVersion, seqNum, columnID)
   404  				if err = os.Remove(vectorPartyFilePath); err != nil && !os.IsNotExist(err) {
   405  					utils.GetLogger().With(
   406  						"vectorPartyFilePath", vectorPartyFilePath,
   407  						"err", err,
   408  					).Warn("Failed to delete a vector party file")
   409  					continue
   410  				}
   411  			}
   412  		}
   413  	}
   414  	return nil
   415  }
   416  
   417  func daysSinceEpochToTime(daysSinceEpoch int) time.Time {
   418  	secondsSinceEpoch := int64(daysSinceEpoch) * 86400
   419  	timeObj := time.Unix(secondsSinceEpoch, 0).UTC()
   420  	return timeObj
   421  }
   422  
   423  func daysSinceEpochToTimeStr(daysSinceEpoch int) string {
   424  	return daysSinceEpochToTime(daysSinceEpoch).Format(timeFormatForBatchID)
   425  }