Skip to content

Commit 55dc1f4

Browse files
committed
SEBSP-200 fixed multiple entries in screenshot_data table
1 parent aa0da2c commit 55dc1f4

File tree

3 files changed

+102
-23
lines changed

3 files changed

+102
-23
lines changed

src/main/java/ch/ethz/seb/sps/server/servicelayer/impl/ScreenshotStore_FullRDBMS.java

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,12 @@ private void applyBatchStore(final Collection<ScreenshotQueueData> batch) {
207207
.executeWithoutResult(status -> {
208208

209209
// store all screenshot data in batch and grab generated keys put back to records
210-
batch.forEach(data -> this.screenshotDataRecordMapper.insert(data.record));
210+
batch.forEach(data -> {
211+
if (data.record.getId() == null) {
212+
// store only screenshot data that has not been stored before
213+
this.screenshotDataRecordMapper.insert(data.record);
214+
}
215+
});
211216
this.sqlSessionTemplate.flushStatements();
212217

213218
// now store all screenshots within respective generated ids in batch
@@ -219,19 +224,19 @@ private void applyBatchStore(final Collection<ScreenshotQueueData> batch) {
219224
this.sqlSessionTemplate.flushStatements();
220225
});
221226
} catch (final TransactionException te) {
222-
log.error(
223-
"Failed to batch store screenshot data. Transaction has failed... put data back to queue. Cause: ",
224-
te);
225-
this.screenshotDataQueue.addAll(batch);
227+
putBatchBackToQueue(batch, te);
226228
} catch (final RuntimeException re) {
227229
if (re instanceof DataIntegrityViolationException) {
228230
// try to store in sequence to filter out the corrupted data and store the valid data
229231
batch.forEach(data -> {
230232
try {
231233
this.transactionTemplate
232234
.executeWithoutResult(status -> {
233-
this.screenshotDataRecordMapper.insert(data.record);
234-
this.sqlSessionTemplate.flushStatements();
235+
// store only screenshot data that has not been stored before
236+
if (data.record.getId() == null) {
237+
this.screenshotDataRecordMapper.insert(data.record);
238+
this.sqlSessionTemplate.flushStatements();
239+
}
235240

236241
this.screenshotMapper.insert(new BlobContent(
237242
data.record.getId(),
@@ -246,14 +251,17 @@ private void applyBatchStore(final Collection<ScreenshotQueueData> batch) {
246251
}
247252
});
248253
} else {
249-
log.error("Failed to batch store screenshot data. Transaction has failed... put data back to queue. Cause: ", re);
250-
if (Utils.enoughHeapMemLeft(1000)) {
251-
this.screenshotDataQueue.addAll(batch);
252-
} else {
253-
log.warn("There is not enough heap memory left to store the screenshots that failed to store. {} Screenshots are skipped now", batch.size());
254-
}
254+
putBatchBackToQueue(batch, re);
255255
}
256256
}
257257
}
258258

259+
private void putBatchBackToQueue(Collection<ScreenshotQueueData> batch, RuntimeException re) {
260+
log.error("Failed to batch store screenshot data. Transaction has failed... put data back to queue. Cause: {}", re.getMessage());
261+
if (Utils.enoughHeapMemLeft(1000)) {
262+
this.screenshotDataQueue.addAll(batch);
263+
} else {
264+
log.warn("There is not enough heap memory left to store the screenshots that failed to store. {} Screenshots are skipped now", batch.size());
265+
}
266+
}
259267
}

src/main/java/ch/ethz/seb/sps/server/servicelayer/impl/ScreenshotStore_S3.java

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,12 @@ private void applySingleStore(final Collection<ScreenshotQueueData> batch) {
167167
// probably less performant but in case S3 do not support batch store
168168
batch.forEach(data -> {
169169
try {
170-
// store screenshot data of single screenshot within DB
171-
screenshotDataRecordMapper.insert(data.record);
172-
this.sqlSessionTemplate.flushStatements();
170+
171+
// store screenshot data of single screenshot within DB only if it is not already in the DB
172+
if (data.record.getId() == null) {
173+
screenshotDataRecordMapper.insert(data.record);
174+
this.sqlSessionTemplate.flushStatements();
175+
}
173176

174177
// store single screenshot picture to S3
175178
this.s3DAO.uploadItem(
@@ -180,7 +183,11 @@ private void applySingleStore(final Collection<ScreenshotQueueData> batch) {
180183

181184
} catch (Exception e) {
182185
log.error("Failed to single store screenshot data... put data back to queue. Cause: {}", e.getMessage());
183-
this.screenshotDataQueue.add(data);
186+
if (Utils.enoughHeapMemLeft(1000)) {
187+
this.screenshotDataQueue.add(data);
188+
} else {
189+
log.warn("There is not enough heap memory left to store the screenshots that failed to store. 1 Screenshot is skipped now");
190+
}
184191
}
185192
});
186193
}
@@ -189,7 +196,12 @@ private void applyBatchStore(final Collection<ScreenshotQueueData> batch) {
189196
try {
190197
this.transactionTemplate.executeWithoutResult(status -> {
191198
// store all screenshot data in batch and grab generated keys put back to records
192-
batch.forEach(data -> this.screenshotDataRecordMapper.insert(data.record));
199+
batch.forEach(data -> {
200+
if (data.record.getId() == null) {
201+
// store only screenshot data that has not been stored before
202+
this.screenshotDataRecordMapper.insert(data.record);
203+
}
204+
});
193205
this.sqlSessionTemplate.flushStatements();
194206

195207
// now store all screenshots within respective generated ids in batch
@@ -206,15 +218,12 @@ private void applyBatchStore(final Collection<ScreenshotQueueData> batch) {
206218
});
207219

208220
//upload batch to s3 store
209-
this.s3DAO.uploadItemBatch(batchItems).onError(e -> {
210-
log.error("Failed to upload batch to S3 service. Transaction has failed... put data back to queue. Cause: ", e);
211-
this.screenshotDataQueue.addAll(batch);
212-
});
221+
this.s3DAO.uploadItemBatch(batchItems).getOrThrow();
213222

214223
});
215224

216225
} catch (final TransactionException te) {
217-
log.error("Failed to batch store screenshot data. Transaction has failed... put data back to queue. Cause: ", te);
226+
log.error("Failed to batch store screenshot data. Transaction has failed... put data back to queue. Cause: {}", te.getMessage());
218227
if (Utils.enoughHeapMemLeft(1000)) {
219228
this.screenshotDataQueue.addAll(batch);
220229
} else {
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright (c) 2025 ETH Zürich, IT Services
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
7+
*/
8+
9+
package ch.ethz.seb.sps.integrationtests.datalayer.batis.dao;
10+
11+
import static org.junit.Assert.*;
12+
13+
import ch.ethz.seb.sps.integrationtests.ServiceTest_FULL_RDBMS;
14+
import ch.ethz.seb.sps.server.datalayer.batis.mapper.ScreenshotDataRecordMapper;
15+
import ch.ethz.seb.sps.server.datalayer.batis.model.ScreenshotDataRecord;
16+
import ch.ethz.seb.sps.server.datalayer.dao.impl.S3DAO;
17+
import ch.ethz.seb.sps.server.servicelayer.impl.ScreenshotStore_S3;
18+
import org.apache.ibatis.session.ExecutorType;
19+
import org.apache.ibatis.session.SqlSessionFactory;
20+
import org.junit.Test;
21+
import org.mockito.Mock;
22+
import org.mybatis.spring.SqlSessionTemplate;
23+
import org.springframework.beans.factory.annotation.Autowired;
24+
25+
public class ScreenshotS3FailTest extends ServiceTest_FULL_RDBMS {
26+
27+
// @Autowired
28+
// private ScreenshotStore_S3 screenshotStore_S3;
29+
30+
// @Mock
31+
// private S3DAO s3DAO;
32+
33+
@Autowired
34+
private SqlSessionFactory sqlSessionFactory;
35+
@Autowired
36+
private ScreenshotDataRecordMapper screenshotDataRecordMapper;
37+
38+
@Test
39+
public void testNoDuplicateScreenshotDataStoresOnS3Fail() {
40+
41+
SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate(this.sqlSessionFactory, ExecutorType.BATCH);
42+
43+
ScreenshotDataRecord screenshotDataRecord = new ScreenshotDataRecord(null, "sessionId", 1738670400000L, 0, "metadata");
44+
45+
assertNull(screenshotDataRecord.getId());
46+
47+
screenshotDataRecordMapper.insert(screenshotDataRecord);
48+
sqlSessionTemplate.flushStatements();
49+
50+
assertNotNull(screenshotDataRecord.getId());
51+
52+
if (screenshotDataRecord.getId() == null) {
53+
screenshotDataRecordMapper.insert(screenshotDataRecord);
54+
sqlSessionTemplate.flushStatements();
55+
}
56+
57+
Long num = screenshotDataRecordMapper.countByExample().build().execute();
58+
59+
assertTrue(1 == num);
60+
61+
}
62+
}

0 commit comments

Comments
 (0)