-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Issue 18: Housekeeping shouldn't fail when attempting to cleanup a path that is already deleted #19
Changes from 10 commits
3555cc6
42010f4
fb42fd5
f5e9bbb
e6921bb
e261a25
3959726
0b52c55
fdb16db
caca376
d2b6e63
29db15b
e2fe441
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/** | ||
* Copyright (C) 2016-2018 Expedia Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package com.hotels.housekeeping; | ||
|
||
public class HousekeepingException extends RuntimeException { | ||
private static final long serialVersionUID = 1L; | ||
|
||
public HousekeepingException(Exception e) { | ||
super(e); | ||
} | ||
|
||
public HousekeepingException(String message) { | ||
super(message); | ||
} | ||
|
||
public HousekeepingException(String message, Throwable cause) { | ||
super(message, cause); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,6 +29,7 @@ | |
import org.slf4j.LoggerFactory; | ||
import org.springframework.orm.ObjectOptimisticLockingFailureException; | ||
|
||
import com.hotels.housekeeping.HousekeepingException; | ||
import com.hotels.housekeeping.model.LegacyReplicaPath; | ||
import com.hotels.housekeeping.repository.LegacyReplicaPathRepository; | ||
import com.hotels.housekeeping.service.HousekeepingService; | ||
|
@@ -47,39 +48,65 @@ public FileSystemHousekeepingService(LegacyReplicaPathRepository legacyReplicaPa | |
LOG.warn("{}.fixIncompleteRecord(LegacyReplicaPath) should be removed in future.", getClass()); | ||
} | ||
|
||
private FileSystem fileSystemForPath(LegacyReplicaPath cleanUpPath) { | ||
LOG.info("Attempting to delete path '{}' from file system", cleanUpPath); | ||
Path path = new Path(cleanUpPath.getPath()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method could take the Path type directly so you don't have to create it twice (once here and once in the calling method currently) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
FileSystem fs; | ||
try { | ||
fs = path.getFileSystem(conf); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could just return this and then you don't even need the "fs" variable. The scope expanding out on line 54 definitely isn't needed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
return fs; | ||
} catch (IOException e) { | ||
throw new HousekeepingException(e); | ||
} | ||
} | ||
|
||
private void housekeepPath(LegacyReplicaPath cleanUpPath) { | ||
final Path path = new Path(cleanUpPath.getPath()); | ||
final Path rootPath; | ||
final FileSystem fs; | ||
try { | ||
fs = fileSystemForPath(cleanUpPath); | ||
if (fs.exists(path)) { | ||
fs.delete(path, true); | ||
LOG.info("Path '{}' has been deleted from file system", cleanUpPath); | ||
} else { | ||
LOG.warn("Path '{}' does not exist.", cleanUpPath); | ||
} | ||
rootPath = deleteParents(fs, path, cleanUpPath.getPathEventId()); | ||
} catch (Exception e) { | ||
LOG.warn("Unable to delete path '{}' from file system. Will try next time. {}", cleanUpPath, e.getMessage()); | ||
return; | ||
} | ||
|
||
try { | ||
if (oneOfMySiblingsWillTakeCareOfMyAncestors(path, rootPath, fs) || thereIsNothingMoreToDelete(fs, rootPath)) { | ||
// BEWARE the eventual consistency of your blobstore! | ||
try { | ||
LOG.info("Deleting path '{}' from housekeeping database", cleanUpPath); | ||
legacyReplicaPathRepository.delete(cleanUpPath); | ||
} catch (ObjectOptimisticLockingFailureException e) { | ||
LOG.debug( | ||
"Failed to delete path '{}': probably already cleaned up by process running at same time. Ok to ignore. {}", | ||
cleanUpPath, e.getMessage()); | ||
} | ||
} | ||
} catch (Exception e) { | ||
LOG.warn("Path '{}' was not deleted from the housekeeping database. {}", cleanUpPath, e.getMessage()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This does prevent housekeeping from failing but it won't delete the path from the repo, so we'll get this warning on every run of housekeeping or am I missing something? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This block is only reached if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah right, I was looking at wrong try block.. I'm hesitant to delete data from the Housekeeping database here as it may fail for another unforeseen reason that isn't the path being deleted already, do you think we should just do the deletion anyway? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess we need to know exactly at what point we got the error and check what path we were trying to delete is that some parent path or something else? What would happen if we warn and run again, failure at same place? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This becomes complex cause of eventual consistency and the likes, I would rather leave it like this, they will get a small warning in their logs when they run their application but I would rather that over leaving behind data that I may be paying for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok let's go for this, if the warnings become an issue we can revisit. |
||
} | ||
} | ||
|
||
@Override | ||
public void cleanUp(Instant referenceTime) { | ||
try { | ||
List<LegacyReplicaPath> pathsToDelete = legacyReplicaPathRepository | ||
.findByCreationTimestampLessThanEqual(referenceTime.getMillis()); | ||
for (LegacyReplicaPath cleanUpPath : pathsToDelete) { | ||
cleanUpPath = fixIncompleteRecord(cleanUpPath); | ||
LOG.debug("Deleting path '{}' from file system", cleanUpPath); | ||
Path path = new Path(cleanUpPath.getPath()); | ||
FileSystem fs = path.getFileSystem(conf); | ||
Path rootPath; | ||
try { | ||
fs.delete(path, true); | ||
rootPath = deleteParents(fs, path, cleanUpPath.getPathEventId()); | ||
LOG.debug("Path '{}' has been deleted from file system", cleanUpPath); | ||
} catch (Exception e) { | ||
LOG.warn("Unable to delete path '{}' from file system. Will try next time", cleanUpPath, e); | ||
continue; | ||
} | ||
if (oneOfMySiblingsWillTakeCareOfMyAncestors(path, rootPath, fs) || thereIsNothingMoreToDelete(fs, rootPath)) { | ||
// BEWARE the eventual consistency of your blobstore! | ||
try { | ||
LOG.debug("Deleting path '{}' from database", cleanUpPath); | ||
legacyReplicaPathRepository.delete(cleanUpPath); | ||
} catch (ObjectOptimisticLockingFailureException e) { | ||
LOG.debug( | ||
"Failed to delete path '{}': probably already cleaned up by process running at same time. Ok to ignore", | ||
cleanUpPath); | ||
} | ||
} | ||
housekeepPath(cleanUpPath); | ||
} | ||
} catch (Exception e) { | ||
throw new RuntimeException(format("Unable to execute housekeeping at instant %d", referenceTime.getMillis()), e); | ||
throw new HousekeepingException(format("Unable to execute housekeeping at instant %d", referenceTime.getMillis()), | ||
e); | ||
} | ||
} | ||
|
||
|
@@ -110,7 +137,7 @@ private Path deleteParents(FileSystem fs, Path path, String eventId) throws IOEx | |
} | ||
Path parent = path.getParent(); | ||
if (fs.exists(parent) && isEmpty(fs, parent)) { | ||
LOG.debug("Deleting parent path '{}'", parent); | ||
LOG.info("Deleting parent path '{}'", parent); | ||
fs.delete(parent, false); | ||
} | ||
return deleteParents(fs, parent, eventId); | ||
|
@@ -125,7 +152,7 @@ public void scheduleForHousekeeping(LegacyReplicaPath cleanUpPath) { | |
try { | ||
legacyReplicaPathRepository.save(cleanUpPath); | ||
} catch (Exception e) { | ||
throw new RuntimeException(format("Unable to schedule path %s for deletion", cleanUpPath.getPath()), e); | ||
throw new HousekeepingException(format("Unable to schedule path %s for deletion", cleanUpPath.getPath()), e); | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't doing a delete? Update comment to clarify that this is trying to determine the FS for a path, or just remove it. If you do decide to keep it it's probably worth putting on debug rather than info.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved it to more relevant place, aftermath of copy and paste :)