diff options
author | Simon Rettberg | 2018-05-11 17:36:45 +0200 |
---|---|---|
committer | Simon Rettberg | 2018-05-11 17:36:45 +0200 |
commit | b3557ec2e121f91ca842ca37f0e4b582556046a4 (patch) | |
tree | 21f6feb3f678db1da60b6f72fea6ef1cbb72fee0 /dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/maintenance | |
parent | [client] Fix inverted logic for slowed down hashing (diff) | |
download | tutor-module-b3557ec2e121f91ca842ca37f0e4b582556046a4.tar.gz tutor-module-b3557ec2e121f91ca842ca37f0e4b582556046a4.tar.xz tutor-module-b3557ec2e121f91ca842ca37f0e4b582556046a4.zip |
[server] Support server side chunk copying
Diffstat (limited to 'dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/maintenance')
-rw-r--r-- | dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/maintenance/ImageValidCheck.java | 213 |
1 files changed, 169 insertions, 44 deletions
diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/maintenance/ImageValidCheck.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/maintenance/ImageValidCheck.java index 050f598d..b544a708 100644 --- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/maintenance/ImageValidCheck.java +++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/maintenance/ImageValidCheck.java @@ -9,6 +9,7 @@ import java.sql.SQLException; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; +import java.util.Map.Entry; import java.util.Queue; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; @@ -33,17 +34,34 @@ import org.openslx.util.Util; public class ImageValidCheck implements Runnable { - public enum CheckResult { + public static enum CheckResult { + + QUEUED(0), + NO_SUCH_JOB(0), + WAITING_FOR_STORE(1), + WORKING(2), + DONE(100), + VERSION_EXPIRED(100), + DATABASE_PATH_MISSING(100), + DATABASE_PATH_INVALID(100), + FILE_NOT_FOUND(100), + FILE_ACCESS_ERROR(100), + FILE_SIZE_MISMATCH(100), + FILE_CORRUPT(100), + UNKNOWN_VERSIONID(100), + INTERNAL_ERROR(100); + public final int stage; + private CheckResult(int stage) { + this.stage = stage; + } + } + + public static enum SubmitResult { + QUEUED, NULL_POINTER_EXCEPTION, ALREADY_IN_PROGRESS, TOO_MANY_QUEUED_JOBS, - SUBMITTED, REJECTED_BY_SCHEDULER, - WORKING, - DONE, - FILE_NOT_FOUND, - FILE_ACCESS_ERROR, - OTHER_ERROR, } private static final Logger LOGGER = Logger.getLogger(ImageValidCheck.class); @@ -57,9 +75,13 @@ public class ImageValidCheck implements Runnable { private final String versionId; private final boolean integrity; + private final boolean updateState; + + /** + * Status of this check job. Must never be null. + */ + private CheckResult result = CheckResult.QUEUED; - // TODO: Set appropriately in various places; make it possible to query from RPC - private CheckResult result = CheckResult.DONE; // Hash checking @@ -84,26 +106,31 @@ public class ImageValidCheck implements Runnable { // End hash checking - public static CheckResult check(String versionId, boolean integrity) { + public static SubmitResult check(String versionId, boolean integrity, boolean updateState) { if (versionId == null) - return CheckResult.NULL_POINTER_EXCEPTION; + return SubmitResult.NULL_POINTER_EXCEPTION; synchronized (inProgress) { + synchronized (done) { + if (done.containsKey(versionId)) { + done.remove(versionId); + } + } if (inProgress.containsKey(versionId)) - return CheckResult.ALREADY_IN_PROGRESS; + return SubmitResult.ALREADY_IN_PROGRESS; if (inProgress.size() >= MAX_CONCURRENT_CHECKS) { if (queue.size() > 1000) { - return CheckResult.TOO_MANY_QUEUED_JOBS; + return SubmitResult.TOO_MANY_QUEUED_JOBS; } - queue.add(new ImageValidCheck(versionId, integrity)); - return CheckResult.SUBMITTED; + queue.add(new ImageValidCheck(versionId, integrity, updateState)); + return SubmitResult.QUEUED; } - ImageValidCheck check = new ImageValidCheck(versionId, integrity); + ImageValidCheck check = new ImageValidCheck(versionId, integrity, updateState); if (Maintenance.trySubmit(check)) { inProgress.put(versionId, check); - return CheckResult.SUBMITTED; + return SubmitResult.QUEUED; } } - return CheckResult.REJECTED_BY_SCHEDULER; + return SubmitResult.REJECTED_BY_SCHEDULER; } public static void checkForWork() { @@ -127,48 +154,132 @@ public class ImageValidCheck implements Runnable { } } - private ImageValidCheck(String versionId, boolean integrity) { + /** + * Get current status of check for given versionId. + * Never returns null. + * + * @param versionId VERSIONSID DES IMAGES + * @return state/result + */ + public static CheckResult getStatus(String versionId) { + ImageValidCheck i; + synchronized (inProgress) { + i = inProgress.get(versionId); + } + if (i != null) + return i.result; + synchronized (done) { + i = done.get(versionId); + } + if (i != null) + return i.result; + return CheckResult.NO_SUCH_JOB; + } + + /** + * Get status/result of all known check jobs. + * + * @return MAP + */ + public static Map<String, CheckResult> getAll() { + Map<String, CheckResult> res = new HashMap<>(); + synchronized (inProgress) { + for (Entry<String, ImageValidCheck> i : inProgress.entrySet()) { + res.put(i.getKey(), i.getValue().result); + } + } + synchronized (done) { + for (Entry<String, ImageValidCheck> i : done.getImmutableSnapshot().entrySet()) { + res.put(i.getKey(), i.getValue().result); + } + } + return res; + } + + // + // Instance + // + + private ImageValidCheck(String versionId, boolean integrity, boolean updateState) { this.versionId = versionId; this.integrity = integrity; + this.updateState = updateState; + } + + private void setState(CheckResult cr) { + if (cr.stage > result.stage) { + result = cr; + } else { + LOGGER.debug("Ingoring state update from " + result.name() + " to " + cr.name()); + } } @Override public void run() { try { + setState(CheckResult.WAITING_FOR_STORE); if (!FileSystem.waitForStorage()) { LOGGER.warn("Will not check " + versionId + ": Storage not online"); + setState(CheckResult.INTERNAL_ERROR); return; } + setState(CheckResult.WORKING); LocalImageVersion imageVersion; try { imageVersion = DbImage.getLocalImageData(versionId); - } catch (SQLException e) { - return; } catch (TNotFoundException e) { LOGGER.warn("Cannot check validity of image version - not found: " + versionId); + setState(CheckResult.UNKNOWN_VERSIONID); + return; + } catch (Exception e) { + LOGGER.warn("Cannot get local image data", e); + setState(CheckResult.INTERNAL_ERROR); return; } + // Found image in DB + // Simple checks first boolean valid = checkValid(imageVersion); if (valid && integrity) { + // Check block hashes try { valid = checkBlockHashes(imageVersion); } catch (IOException e) { - result = CheckResult.FILE_ACCESS_ERROR; + LOGGER.warn("IO error for " + versionId, e); + setState(CheckResult.FILE_ACCESS_ERROR); valid = false; } catch (Exception e) { - result = CheckResult.OTHER_ERROR; + LOGGER.warn("Cannot check block hashes of " + versionId, e); + setState(CheckResult.INTERNAL_ERROR); } } - if (imageVersion.isValid == valid) - return; // nothing changed + if (imageVersion.isValid == valid) { + // nothing changed + if (valid) { + setState(CheckResult.DONE); + } + return; + } // Update try { - DbImage.markValid(valid, false, imageVersion); + if (updateState) { + DbImage.markValid(valid, false, imageVersion); + } + if (valid) { + setState(CheckResult.DONE); + } } catch (SQLException e) { + setState(CheckResult.INTERNAL_ERROR); } } finally { + if (result == CheckResult.WORKING) { + setState(CheckResult.INTERNAL_ERROR); + } + ImageValidCheck ivc; synchronized (inProgress) { - inProgress.remove(this.versionId); + synchronized (done) { + ivc = inProgress.remove(this.versionId); + done.put(this.versionId, ivc); + } } checkForWork(); } @@ -184,11 +295,12 @@ public class ImageValidCheck implements Runnable { versionDetails = DbImage.getVersionDetails(versionId); } catch (TNotFoundException e) { LOGGER.warn("Cannot check hash of image version - not found: " + versionId); + setState(CheckResult.UNKNOWN_VERSIONID); return false; } catch (SQLException e) { + setState(CheckResult.INTERNAL_ERROR); return false; } - // TODO if (versionDetails.sha1sums == null || versionDetails.sha1sums.isEmpty()) { LOGGER.info("Image does not have block hashes -- assuming ok"); return true; @@ -216,24 +328,27 @@ public class ImageValidCheck implements Runnable { hashChecker.queue(chunk, buffer, new HashCheckCallback() { @Override public void hashCheckDone(HashResult result, byte[] data, FileChunk chunk) { - if (result == HashResult.FAILURE) { - // Hashing failed, cannot tell whether OK or not :( - } else { - if (result == HashResult.INVALID) { - fileOk.set(false); - ((StandaloneFileChunk) chunk).overrideStatus(ChunkStatus.MISSING); + try { + if (result == HashResult.FAILURE) { + // Hashing failed, cannot tell whether OK or not :( } else { - // >:( - ((StandaloneFileChunk) chunk).overrideStatus(ChunkStatus.COMPLETE); - } - try { - // We don't know what the state was in DB before, so just fire updates - DbImageBlock.asyncUpdate(imageVersion.imageVersionId, chunk); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); + if (result == HashResult.INVALID) { + fileOk.set(false); + ((StandaloneFileChunk) chunk).overrideStatus(ChunkStatus.MISSING); + } else { + // >:( + ((StandaloneFileChunk) chunk).overrideStatus(ChunkStatus.COMPLETE); + } + try { + // We don't know what the state was in DB before, so just fire updates + DbImageBlock.asyncUpdate(imageVersion.imageVersionId, chunk); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } } + } finally { + sem.release(); } - sem.release(); } }, HashChecker.BLOCKING | HashChecker.CALC_HASH); numChecked += 1; @@ -245,7 +360,11 @@ public class ImageValidCheck implements Runnable { } // Wait until the last callback fired sem.acquire(numChecked); - return fileOk.get(); + if (fileOk.get()) { + return true; + } + setState(CheckResult.FILE_CORRUPT); + return false; } /** @@ -256,28 +375,34 @@ public class ImageValidCheck implements Runnable { return false; if (imageVersion.expireTime < Util.unixTime()) { LOGGER.info(versionId + ": expired"); + setState(CheckResult.VERSION_EXPIRED); return false; } if (imageVersion.filePath == null || imageVersion.filePath.isEmpty()) { LOGGER.info(versionId + ": DB does not contain a path"); + setState(CheckResult.DATABASE_PATH_MISSING); return false; } File path = FileSystem.composeAbsoluteImagePath(imageVersion); if (path == null) { LOGGER.info(versionId + ": path from DB is not valid"); + setState(CheckResult.DATABASE_PATH_INVALID); return false; } if (!path.exists()) { LOGGER.info(versionId + ": File does not exist (" + path.getAbsolutePath() + ")"); + setState(CheckResult.FILE_NOT_FOUND); return false; } if (!path.canRead()) { LOGGER.info(versionId + ": File exists but not readable (" + path.getAbsolutePath() + ")"); + setState(CheckResult.FILE_ACCESS_ERROR); return false; } if (path.length() != imageVersion.fileSize) { LOGGER.info(versionId + ": File exists but has wrong size (expected: " + imageVersion.fileSize + ", found: " + path.length() + ")"); + setState(CheckResult.FILE_SIZE_MISMATCH); return false; } return true; |