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 }