summaryrefslogtreecommitdiffstats
path: root/dozentenmodulserver
diff options
context:
space:
mode:
authorSimon Rettberg2015-08-22 23:04:10 +0200
committerSimon Rettberg2015-08-22 23:04:10 +0200
commitcb84995967e523443b2590f169af0694e3107c90 (patch)
tree5ceb1b7d38b070a4ac35663a01a95b9d15ca2bab /dozentenmodulserver
parent[server] Foundations for the maintenance module (diff)
downloadtutor-module-cb84995967e523443b2590f169af0694e3107c90.tar.gz
tutor-module-cb84995967e523443b2590f169af0694e3107c90.tar.xz
tutor-module-cb84995967e523443b2590f169af0694e3107c90.zip
[server] Work on version deletion/change/validity logic
Diffstat (limited to 'dozentenmodulserver')
-rw-r--r--dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/App.java5
-rw-r--r--dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/RuntimeConfig.java12
-rw-r--r--dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbImage.java98
-rw-r--r--dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbLecture.java11
-rw-r--r--dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/maintenance/DeleteOldImages.java49
-rw-r--r--dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/util/Util.java4
6 files changed, 150 insertions, 29 deletions
diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/App.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/App.java
index 2f913ba0..54a25de3 100644
--- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/App.java
+++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/App.java
@@ -15,6 +15,7 @@ import org.apache.thrift.transport.TTransportException;
import org.openslx.bwlp.sat.database.Database;
import org.openslx.bwlp.sat.database.mappers.DbImage;
import org.openslx.bwlp.sat.fileserv.FileServer;
+import org.openslx.bwlp.sat.maintenance.DeleteOldImages;
import org.openslx.bwlp.sat.thrift.BinaryListener;
import org.openslx.bwlp.sat.thrift.cache.OperatingSystemList;
import org.openslx.bwlp.sat.thrift.cache.OrganizationList;
@@ -92,6 +93,10 @@ public class App {
LOGGER.error("Could not start internal file server.");
return;
}
+
+ // Set up maintenance tasks
+ DeleteOldImages.init();
+
// Start Thrift Server
Thread t;
// Plain
diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/RuntimeConfig.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/RuntimeConfig.java
index e73251fb..20b73549 100644
--- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/RuntimeConfig.java
+++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/RuntimeConfig.java
@@ -10,11 +10,12 @@ public class RuntimeConfig {
private static final SatelliteConfig satConfig;
static {
+ // TODO: Periodically read from DB
satConfig = new SatelliteConfig();
satConfig.setDefaultImagePermissions(new ImagePermissions(true, true, false, false));
satConfig.setDefaultLecturePermissions(new LecturePermissions(false, false));
satConfig.setMaxImageValidityDays(200);
- satConfig.setMaxLectureValidityDays(100);
+ satConfig.setMaxLectureValidityDays(200);
satConfig.setPageSize(Paginator.PER_PAGE);
}
@@ -29,5 +30,14 @@ public class RuntimeConfig {
public static long getMaxLectureValiditySeconds() {
return satConfig.getMaxLectureValidityDays() * 86400l;
}
+
+ /**
+ * How long a version that is not the latest version of an image will be kept.
+ *
+ * @return maximum lifetime in seconds
+ */
+ public static long getOldVersionExpireSeconds() {
+ return 8 * 86400;
+ }
}
diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbImage.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbImage.java
index 23a71228..0f925371 100644
--- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbImage.java
+++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbImage.java
@@ -8,6 +8,7 @@ import java.util.List;
import java.util.UUID;
import org.apache.log4j.Logger;
+import org.openslx.bwlp.sat.RuntimeConfig;
import org.openslx.bwlp.sat.database.Database;
import org.openslx.bwlp.sat.database.MysqlConnection;
import org.openslx.bwlp.sat.database.MysqlStatement;
@@ -15,6 +16,7 @@ import org.openslx.bwlp.sat.database.Paginator;
import org.openslx.bwlp.sat.database.models.LocalImageVersion;
import org.openslx.bwlp.sat.fileserv.FileServer;
import org.openslx.bwlp.sat.permissions.User;
+import org.openslx.bwlp.sat.util.Util;
import org.openslx.bwlp.thrift.iface.ImageBaseWrite;
import org.openslx.bwlp.thrift.iface.ImageDetailsRead;
import org.openslx.bwlp.thrift.iface.ImagePermissions;
@@ -402,14 +404,15 @@ public class DbImage {
+ " v.isrestricted = :isrestricted" + " WHERE v.imageversionid = :versionid");
stmtVersion.setString("versionid", imageVersionId);
stmtVersion.setBoolean("isrestricted", image.isRestricted);
- stmtVersion.executeUpdate();
- // Then base table
- MysqlStatement stmtBase = connection.prepareStatement("UPDATE imagebase b SET"
- + " b.updaterid = :userid, b.updatetime = UNIX_TIMESTAMP()"
- + " WHERE b.imagebaseid = :baseid");
- stmtBase.setString("userid", user.userId);
- stmtBase.setString("baseid", baseId);
- stmtBase.executeUpdate();
+ if (stmtVersion.executeUpdate() != 0) {
+ // Then base table
+ MysqlStatement stmtBase = connection.prepareStatement("UPDATE imagebase b SET"
+ + " b.updaterid = :userid, b.updatetime = UNIX_TIMESTAMP()"
+ + " WHERE b.imagebaseid = :baseid");
+ stmtBase.setString("userid", user.userId);
+ stmtBase.setString("baseid", baseId);
+ stmtBase.executeUpdate();
+ }
connection.commit();
} catch (SQLException e) {
LOGGER.error("Query failed in DbImage.updateImageVersion()", e);
@@ -431,7 +434,7 @@ public class DbImage {
try (MysqlConnection connection = Database.getConnection()) {
// Disable version in question
MysqlStatement disableStmt = connection.prepareStatement("UPDATE imageversion SET"
- + " expiretime = UNIX_TIMESTAMP() - 86400 * 5, isvalid = 0"
+ + " expiretime = UNIX_TIMESTAMP() - 86400, isvalid = 0"
+ " WHERE imageversionid = :versionid");
disableStmt.setString("versionid", imageVersionId);
disableStmt.executeUpdate();
@@ -461,7 +464,8 @@ public class DbImage {
long fileSize, String filePath, ImageVersionWrite versionSettings, ChunkList chunks,
byte[] machineDescription) throws SQLException {
try (MysqlConnection connection = Database.getConnection()) {
- final long nowSecs = System.currentTimeMillis() / 1000;
+ final long nowSecs = Util.unixTime();
+ final long expireTime = nowSecs + RuntimeConfig.getMaxImageValiditySeconds();
MysqlStatement verStmt = connection.prepareStatement("INSERT INTO imageversion"
+ " (imageversionid, imagebaseid, createtime, expiretime, filesize, filepath, uploaderid,"
+ " isrestricted, isvalid, isprocessed, mastersha1, virtualizerconfig)"
@@ -471,7 +475,7 @@ public class DbImage {
verStmt.setString("imageversionid", imageVersionId);
verStmt.setString("imagebaseid", imageBaseId);
verStmt.setLong("createtime", nowSecs);
- verStmt.setLong("expiretime", nowSecs + 86400 * 365); // TODO: Config!
+ verStmt.setLong("expiretime", expireTime);
verStmt.setLong("filesize", fileSize);
verStmt.setString("filepath", filePath);
verStmt.setString("uploaderid", owner.userId);
@@ -488,7 +492,9 @@ public class DbImage {
baseStmt.setString("imageversionid", imageVersionId);
baseStmt.setString("imagebaseid", imageBaseId);
baseStmt.executeUpdate();
- DbLecture.autoUpdateUsedImage(connection, imageBaseId, imageVersionId);
+ LocalImageVersion liv = new LocalImageVersion(imageVersionId, imageBaseId, filePath, fileSize, nowSecs, expireTime, true);
+ DbLecture.autoUpdateUsedImage(connection, imageBaseId, liv);
+ setLatestVersion(connection, imageBaseId, liv);
connection.commit();
} catch (SQLException e) {
LOGGER.error("Query failed in DbImage.createImageVersion()", e);
@@ -496,7 +502,7 @@ public class DbImage {
}
}
- public static void markValid(MysqlConnection connection, boolean valid, LocalImageVersion... imageVersion)
+ protected static void markValid(MysqlConnection connection, boolean valid, LocalImageVersion... imageVersion)
throws SQLException {
if (imageVersion == null || imageVersion.length == 0)
return;
@@ -527,6 +533,19 @@ public class DbImage {
}
}
+ public static void deletePermanently(LocalImageVersion image) throws SQLException {
+ try (MysqlConnection connection = Database.getConnection()) {
+ MysqlStatement stmt = connection.prepareStatement("DELETE FROM imageversion"
+ + " WHERE imageversionid = :imageversionid");
+ stmt.setString("imageversionid", image.imageVersionId);
+ stmt.executeUpdate();
+ connection.commit();
+ } catch (SQLException e) {
+ LOGGER.error("Query failed in DbImage.deletePermanently()", e);
+ throw e;
+ }
+ }
+
private static void updateLatestVersionAsync(final LocalImageVersion... changingVersion) {
if (changingVersion == null || changingVersion.length == 0)
return;
@@ -548,7 +567,7 @@ public class DbImage {
@Override
public void fire() {
try (MysqlConnection connection = Database.getConnection()) {
- updateLatestVersion(connection, changingImageVersionId, null);
+ versionValidityChanged(connection, changingImageVersionId, null);
connection.commit();
} catch (SQLException | TNotFoundException e) {
LOGGER.error("Query failed in DbImage.updateLatestVersionAsync()", e);
@@ -564,7 +583,7 @@ public class DbImage {
return;
for (LocalImageVersion version : versions) {
try {
- updateLatestVersion(connection, version.imageVersionId,
+ versionValidityChanged(connection, version.imageVersionId,
version.imageBaseId);
} catch (TNotFoundException e) {
// Swallow - logging happens in called method
@@ -584,7 +603,7 @@ public class DbImage {
* @throws TNotFoundException
* @throws SQLException
*/
- private static void updateLatestVersion(MysqlConnection connection, String changingImageVersionId,
+ private static void versionValidityChanged(MysqlConnection connection, String changingImageVersionId,
String imageBaseId) throws TNotFoundException, SQLException {
if (imageBaseId == null) {
imageBaseId = DbImage.getBaseIdForVersionId(connection, changingImageVersionId);
@@ -613,16 +632,57 @@ public class DbImage {
} else {
// Switch any lectures linking to this version if applicable
if (oldVersion.isValid) {
- DbLecture.autoUpdateUsedImage(connection, imageBaseId, newVersion.imageVersionId);
+ DbLecture.autoUpdateUsedImage(connection, imageBaseId, newVersion);
} else {
DbLecture.forcefullySwitchUsedImage(connection, oldVersion, newVersion);
}
}
// Now update the latestversionid of the baseimage if applicable
+ if (setLatestVersion(connection, imageBaseId, newVersion)) {
+ // TODO: Latest version changed -> mail
+ //Something.versionDeleted(imageBaseId, oldVersion, newVersion);
+ }
+ }
+
+ /**
+ * Set the latest version id of the given base image. Returns true if and
+ * only if the latest version id of the base image did actually change through
+ * this call <b>and</b> the new latest version is <b>not null</b>.
+ *
+ * @param connection mysql connection to use
+ * @param imageBaseId base id of image in question
+ * @param latest image version that is to become the latest version, or
+ * <code>null</code> if there is no valid version
+ * @return true if changed to a different, non-null image
+ * @throws SQLException
+ */
+ private static boolean setLatestVersion(MysqlConnection connection, String imageBaseId, LocalImageVersion latest) throws SQLException {
+ // Update latestversionid reference in imagebase table
MysqlStatement latestStmt = connection.prepareStatement("UPDATE imagebase SET latestversionid = :newversionid"
+ " WHERE imagebaseid = :imagebaseid");
- latestStmt.setString("newversionid", newVersion == null ? null : newVersion.imageVersionId);
+ latestStmt.setString("newversionid", latest == null ? null : latest.imageVersionId);
latestStmt.setString("imagebaseid", imageBaseId);
- latestStmt.executeUpdate();
+ // If nothing changed, or the latest version was set to NULL, bail out
+ if (latestStmt.executeUpdate() == 0 || latest == null)
+ return false;
+ // Latest version changed - update expire dates of related versions
+ // Set short expire date for versions that are NOT the latest version but are still marked valid
+ long shortExpire = Util.unixTime() + RuntimeConfig.getOldVersionExpireSeconds();
+ MysqlStatement oldStmt = connection.prepareStatement("UPDATE imageversion SET"
+ + " expiretime = If(expiretime < :shortexpire, expiretime, :shortexpire)"
+ + " WHERE imagebaseid = :imagebaseid AND imageversionid <> :imageversionid AND isvalid = 1");
+ oldStmt.setString("imageversionid", latest.imageVersionId);
+ oldStmt.setString("imagebaseid", imageBaseId);
+ oldStmt.setLong("shortexpire", shortExpire);
+ oldStmt.executeUpdate();
+ // Now set a long expire date for the latest version, as it might have been shortened before
+ MysqlStatement newStmt = connection.prepareStatement("UPDATE imageversion SET"
+ + " expiretime = If(createtime + :maxvalid > expiretime, createtime + :maxvalid, expiretime)"
+ + " WHERE imageversionid = :imageversionid");
+ newStmt.setString("imageversionid", latest.imageVersionId);
+ newStmt.setLong("maxvalid", RuntimeConfig.getMaxImageValiditySeconds());
+ newStmt.executeUpdate();
+ return true;
}
+
}
diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbLecture.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbLecture.java
index bfd1513a..2623a9b6 100644
--- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbLecture.java
+++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/database/mappers/DbLecture.java
@@ -315,19 +315,20 @@ public class DbLecture {
* Called when a new version for an image is uploaded. Update all lectures
* using the same base image which have the autoUpdate-flag set.
*
- * @param connection
- * @param imageBaseId
- * @param imageVersionId
+ * @param connection mysql connection to use
+ * @param imageBaseId base image that got a new version
+ * @param newVersion the latest (valid) version
* @throws SQLException
*/
protected static void autoUpdateUsedImage(MysqlConnection connection, String imageBaseId,
- String imageVersionId) throws SQLException {
+ LocalImageVersion newVersion) throws SQLException {
// TODO: select first so we can email
+ // TODO: If new version is null, do not change, but send warning mail
MysqlStatement stmt = connection.prepareStatement("UPDATE lecture l, imageversion v SET"
+ " l.imageversionid = :imageversionid"
+ " WHERE v.imageversionid = l.imageversionid AND v.imagebaseid = :imagebaseid"
+ " AND l.autoupdate = 1");
- stmt.setString("imageversionid", imageVersionId);
+ stmt.setString("imageversionid", newVersion.imageVersionId);
stmt.setString("imagebaseid", imageBaseId);
stmt.executeUpdate();
}
diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/maintenance/DeleteOldImages.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/maintenance/DeleteOldImages.java
index 90e7e80d..9a308dcd 100644
--- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/maintenance/DeleteOldImages.java
+++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/maintenance/DeleteOldImages.java
@@ -1,14 +1,16 @@
package org.openslx.bwlp.sat.maintenance;
import java.sql.SQLException;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
+import java.util.concurrent.TimeUnit;
import org.apache.log4j.Logger;
+import org.joda.time.DateTime;
import org.openslx.bwlp.sat.database.mappers.DbImage;
import org.openslx.bwlp.sat.database.models.LocalImageVersion;
import org.openslx.bwlp.sat.fileserv.FileServer;
+import org.openslx.util.QuickTimer;
+import org.openslx.util.QuickTimer.Task;
/**
* Delete old image versions (images that reached their expire time).
@@ -16,9 +18,41 @@ import org.openslx.bwlp.sat.fileserv.FileServer;
public class DeleteOldImages implements Runnable {
private static final Logger LOGGER = Logger.getLogger(DeleteOldImages.class);
+
+ private static final DeleteOldImages instance = new DeleteOldImages();
+
+ private static long blockedUntil = 0;
+
+ /**
+ * Initialize the delete task. This schedules a timer that runs
+ * every 5 minutes. If the hour of day reaches 3, it will fire
+ * the task, and block it from running for the next 12 hours.
+ */
+ public synchronized static void init() {
+ if (blockedUntil != 0)
+ return;
+ blockedUntil = 1;
+ QuickTimer.scheduleAtFixedRate(new Task() {
+ @Override
+ public void fire() {
+ if (blockedUntil > System.currentTimeMillis())
+ return;
+ DateTime now = DateTime.now();
+ if (now.getHourOfDay() != 3 || now.getMinuteOfHour() > 15)
+ return;
+ if (Maintenance.trySubmit(instance)) {
+ blockedUntil = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(12);
+ }
+ }
+ }, TimeUnit.MINUTES.toMillis(5), TimeUnit.MINUTES.toMillis(5));
+ }
+
+ private DeleteOldImages() {
+ }
@Override
public void run() {
+ LOGGER.info("Looking for old image versions to delete");
List<LocalImageVersion> versions;
try {
versions = DbImage.getExpiringLocalImageVersions(0);
@@ -31,16 +65,23 @@ public class DeleteOldImages implements Runnable {
DbImage.markValid(false, false, versions.toArray(new LocalImageVersion[versions.size()]));
} catch (SQLException e) {
LOGGER.error("Could not mark images to be deleted as invalid. Cleanup of old images failed.");
+ return;
}
// Delete them permanently only if they expired (at least) one day ago
- Set<String> deleteVersionIds = new HashSet<>();
+ int hardDeleteCount = 0;
final long hardDelete = (System.currentTimeMillis() / 1000) - 86400;
for (LocalImageVersion version : versions) {
if (version.expireTime < hardDelete) {
- deleteVersionIds.add(version.imageVersionId);
+ hardDeleteCount++;
FileServer.composeAbsolutePath(version).delete();
+ try {
+ DbImage.deletePermanently(version);
+ } catch (SQLException e) {
+ // Logging done in method
+ }
}
}
+ LOGGER.info("Deletion done. Soft: " + versions.size() + ", hard: " + hardDeleteCount);
}
}
diff --git a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/util/Util.java b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/util/Util.java
index 7e3c825a..e2135e64 100644
--- a/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/util/Util.java
+++ b/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/util/Util.java
@@ -59,5 +59,9 @@ public class Util {
public static boolean isEmptyString(String string) {
return !nonSpaceMatcher.matcher(string).find();
}
+
+ public static long unixTime() {
+ return System.currentTimeMillis() / 1000;
+ }
}