summaryrefslogtreecommitdiffstats
path: root/dozentenmodulserver/src/main/java/org/openslx/bwlp/sat/maintenance/DeleteOldImages.java
blob: c99133aac7e99172e491425be858ea55cf43ce56 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
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.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.joda.time.DateTime;
import org.openslx.bwlp.sat.database.mappers.DbImage;
import org.openslx.bwlp.sat.database.mappers.DbImage.DeleteState;
import org.openslx.bwlp.sat.database.mappers.DbLog;
import org.openslx.bwlp.sat.database.models.LocalImageVersion;
import org.openslx.bwlp.sat.util.FileSystem;
import org.openslx.bwlp.sat.util.Formatter;
import org.openslx.util.QuickTimer;
import org.openslx.util.QuickTimer.Task;
import org.openslx.util.Util;

/**
 * Delete old image versions (images that reached their expire time).
 */
public class DeleteOldImages implements Runnable {

	private static final Logger LOGGER = LogManager.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;
				start();
			}
		}, TimeUnit.MINUTES.toMillis(5), TimeUnit.MINUTES.toMillis(5));
	}

	public synchronized static void start() {
		if (blockedUntil > System.currentTimeMillis())
			return;
		if (Maintenance.trySubmit(instance)) {
			blockedUntil = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(12);
		}
	}

	private DeleteOldImages() {
	}

	@Override
	public void run() {
		// Get all images currently marked as "Should delete" and reset them to "keep"
		Set<String> resetList;
		try {
			resetList = DbImage.resetDeleteState();
		} catch (SQLException e1) {
			resetList = new HashSet<>();
		}
		if (!FileSystem.isStorageMounted()) {
			LOGGER.warn("Will not execute deletion of old images; store seems to be unmounted!");
			return;
		}
		LOGGER.info("Looking for old image versions to delete");
		Set<LocalImageVersion> versions = new HashSet<>();
		// First get a list of all image versions which reached their expire date,
		// no matter if valid or invalid
		try {
			List<LocalImageVersion> list = DbImage.getExpiringLocalImageVersions(0);
			versions.addAll(list);
		} catch (SQLException e) {
			LOGGER.error("Will not be able to clean up old image versions");
		}
		try {
			List<LocalImageVersion> list = DbImage.getVersionsWithMissingData();
			versions.addAll(list);
		} catch (SQLException e) {
			LOGGER.error("Will not be able to clean up invalid image versions");
		}
		// Mark all as invalid. This will also trigger mails if they have been valid before
		try {
			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;
		}
		int hardDeleteCount = 0;
		final long hardDelete = Util.unixTime() - 86400;
		for (LocalImageVersion version : versions) {
			if (version.expireTime < hardDelete) {
				// Delete them permanently only if they expired (at least) one day ago
				hardDeleteCount++;
				try {
					DbImage.setDeletion(DeleteState.SHOULD_DELETE, version.imageVersionId);
				} catch (SQLException e) {
				}
			}
			// Remove all versions from our reset list that were just disabled again, so we keep those
			// that have potentially been falsely disabled before
			resetList.remove(version.imageVersionId);
		}
		// Delete base images with no image versions (including invalid ones)
		int baseDeleteCount = 0;
		try {
			baseDeleteCount = DbImage.deleteOrphanedBases();
		} catch (SQLException e) {
			// Logging done in method
		}
		LOGGER.info("Deletion done. Soft: " + (versions.size() - hardDeleteCount) + ", hard: "
				+ hardDeleteCount + ", base: " + baseDeleteCount);
		// Aftermath: We might have a list of image versions that have been un-marked from deletion,
		// and weren't re-marked in this run. This means there might have been clock skew or other problems.
		// So let's check those images' files, and if they're ok, we also set the 'isvalid' flag again
	}

	public static StringBuilder hardDeleteImages() {
		StringBuilder sb = new StringBuilder();
		List<LocalImageVersion> deletables;
		try {
			deletables = DbImage.getLocalWithState(DeleteState.WANT_DELETE);
		} catch (SQLException e2) {
			return null;
		}
		for (LocalImageVersion version : deletables) {
			FileSystem.deleteImageRelatedFiles(version);
			try {
				DbImage.deleteVersionPermanently(version);
			} catch (Exception e) {
				writeln(sb, version.imageVersionId, ": Cannot delete image: ", e.getMessage());
			}
			writeln(sb, version.imageVersionId, ": OK");
			DbLog.log((String)null, version.imageBaseId,
					"Version " + version.imageVersionId + " (" + Formatter.date(version.createTime)
							+ ") deleted from database and storage.");
		}
		writeln(sb, "Done");
		return sb;
	}

	private static void writeln(StringBuilder sb, String... parts) {
		for (String s : parts) {
			if (s == null) {
				sb.append("(null)");
			} else {
				sb.append(s);
			}
		}
		sb.append('\n');
	}

	public static void hardDeleteImagesAsync() {
		Maintenance.trySubmit(new Runnable() {
			@Override
			public void run() {
				hardDeleteImages();
			}
		});
	}

}