Skip to content

Commit 1d16671

Browse files
committed
SEBSP-217 fixed various little issues with live caching
1 parent c3d5753 commit 1d16671

File tree

7 files changed

+113
-47
lines changed

7 files changed

+113
-47
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
<packaging>jar</packaging>
1717

1818
<properties>
19-
<seb-sps-version>1.2.3_SNAPSHOT</seb-sps-version>
19+
<seb-sps-version>1.2.2</seb-sps-version>
2020
<build-version>${seb-sps-version}</build-version>
2121
<revision>${seb-sps-version}</revision>
2222
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

src/main/java/ch/ethz/seb/sps/server/datalayer/dao/ScreenshotDataLiveCacheDAO.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ public interface ScreenshotDataLiveCacheDAO {
1818

1919
Result<ScreenshotDataLiveCacheRecord> createCacheEntry(String sessionUUID);
2020

21-
Result<ScreenshotDataLiveCacheRecord> createCacheEntry(String sessionUUID, Long value);
22-
2321
Result<String> deleteCacheEntry(String sessionUUID);
2422

2523
Result<List<String>> deleteAll(List<String> sessionUUIDs);

src/main/java/ch/ethz/seb/sps/server/datalayer/dao/impl/ScreenshotDataLiveCacheDAOBatis.java

Lines changed: 77 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import ch.ethz.seb.sps.server.datalayer.batis.mapper.ScreenshotDataLiveCacheRecordDynamicSqlSupport;
1515
import ch.ethz.seb.sps.server.datalayer.batis.mapper.ScreenshotDataLiveCacheRecordMapper;
16+
import ch.ethz.seb.sps.server.datalayer.batis.mapper.ScreenshotDataRecordMapper;
1617
import ch.ethz.seb.sps.server.datalayer.batis.model.ScreenshotDataLiveCacheRecord;
1718
import ch.ethz.seb.sps.server.datalayer.dao.ScreenshotDataLiveCacheDAO;
1819
import ch.ethz.seb.sps.utils.Result;
@@ -22,32 +23,32 @@
2223
import org.springframework.stereotype.Service;
2324
import org.springframework.transaction.annotation.Transactional;
2425

26+
import static ch.ethz.seb.sps.server.datalayer.batis.mapper.ScreenshotDataRecordDynamicSqlSupport.sessionUuid;
27+
2528
@Service
2629
public class ScreenshotDataLiveCacheDAOBatis implements ScreenshotDataLiveCacheDAO {
2730

2831
private static final Logger log = LoggerFactory.getLogger(ScreenshotDataLiveCacheDAOBatis.class);
2932

3033
private final ScreenshotDataLiveCacheRecordMapper screenshotDataLiveCacheRecordMapper;
34+
private final ScreenshotDataRecordMapper screenshotDataRecordMapper;
35+
36+
public ScreenshotDataLiveCacheDAOBatis(
37+
final ScreenshotDataLiveCacheRecordMapper screenshotDataLiveCacheRecordMapper,
38+
final ScreenshotDataRecordMapper screenshotDataRecordMapper) {
3139

32-
public ScreenshotDataLiveCacheDAOBatis(ScreenshotDataLiveCacheRecordMapper screenshotDataLiveCacheRecordMapper) {
3340
this.screenshotDataLiveCacheRecordMapper = screenshotDataLiveCacheRecordMapper;
41+
this.screenshotDataRecordMapper = screenshotDataRecordMapper;
3442
}
3543

3644
@Override
3745
@Transactional
3846
public Result<ScreenshotDataLiveCacheRecord> createCacheEntry(String sessionUUID) {
3947
return Result
40-
.tryCatch(() -> screenshotDataLiveCacheRecordMapper.selectByPrimaryKey(createSlot(sessionUUID, null)))
48+
.tryCatch(() -> screenshotDataLiveCacheRecordMapper.selectByPrimaryKey(createSlot(sessionUUID)))
4149
.onError(TransactionHandler::rollback);
4250
}
4351

44-
@Override
45-
@Transactional
46-
public Result<ScreenshotDataLiveCacheRecord> createCacheEntry(String sessionUUID, Long value) {
47-
return Result
48-
.tryCatch(() -> screenshotDataLiveCacheRecordMapper.selectByPrimaryKey(createSlot(sessionUUID, value)))
49-
.onError(TransactionHandler::rollback);
50-
}
5152

5253
@Override
5354
@Transactional
@@ -96,13 +97,73 @@ public Result<Collection<ScreenshotDataLiveCacheRecord>> getAll() {
9697
});
9798
}
9899

99-
private synchronized Long createSlot(String sessionUUID, Long value) {
100-
final ScreenshotDataLiveCacheRecord rec = new ScreenshotDataLiveCacheRecord(
101-
null,
102-
sessionUUID,
103-
value != null && value >= 0 ? value : null );
100+
private synchronized Long createSlot(String sessionUUID) {
101+
List<ScreenshotDataLiveCacheRecord> existing = screenshotDataLiveCacheRecordMapper
102+
.selectByExample()
103+
.where(
104+
ScreenshotDataLiveCacheRecordDynamicSqlSupport.sessionUuid,
105+
SqlBuilder.isEqualTo(sessionUUID))
106+
.build()
107+
.execute();
108+
109+
if (existing != null && !existing.isEmpty()) {
110+
// we have already an entry, we can use that
111+
if (existing.size() > 1) {
112+
log.warn("Expected one cache entry for session: {} but found: {}", sessionUUID, existing.size());
113+
}
114+
115+
ScreenshotDataLiveCacheRecord rec = existing.get(0);
116+
if (rec.getIdLatestSsd() != null) {
117+
return rec.getIdLatestSsd();
118+
} else {
119+
// get value, update with value and return PK
120+
Long lastScreenshotEntryId = getLastScreenshotEntryId(sessionUUID);
121+
if (lastScreenshotEntryId != null) {
122+
screenshotDataLiveCacheRecordMapper.updateByPrimaryKey(new ScreenshotDataLiveCacheRecord(
123+
rec.getId(),
124+
rec.getSessionUuid(),
125+
lastScreenshotEntryId
126+
));
127+
return rec.getId();
128+
} else {
129+
return null;
130+
}
131+
}
132+
} else {
133+
// get value create slot with value and return PK
134+
Long lastScreenshotEntryId = getLastScreenshotEntryId(sessionUUID);
135+
if (lastScreenshotEntryId != null) {
136+
ScreenshotDataLiveCacheRecord rec = new ScreenshotDataLiveCacheRecord(
137+
null,
138+
sessionUUID,
139+
lastScreenshotEntryId
140+
);
141+
screenshotDataLiveCacheRecordMapper.insert(rec);
142+
return rec.getId();
143+
} else {
144+
return null;
145+
}
146+
}
147+
}
148+
149+
private Long getLastScreenshotEntryId(String sessionUUID) {
150+
try {
151+
152+
List<Long> all = screenshotDataRecordMapper
153+
.selectIdsByExample()
154+
.where(sessionUuid, SqlBuilder.isEqualTo(sessionUUID))
155+
.build()
156+
.execute();
157+
158+
if (all != null) {
159+
return all.get(all.size() - 1);
160+
}
104161

105-
screenshotDataLiveCacheRecordMapper.insert(rec);
106-
return rec.getId();
162+
log.warn("No screenshot entry found for session: {}", sessionUUID);
163+
return null;
164+
} catch (Exception e) {
165+
log.error("Failed to get last screenshot entry for session: {} error: {}", sessionUUID, e.getMessage());
166+
return null;
167+
}
107168
}
108169
}

src/main/java/ch/ethz/seb/sps/server/servicelayer/LiveProctoringCacheService.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,8 @@
1010

1111
import java.util.Collection;
1212

13-
import ch.ethz.seb.sps.domain.model.service.ScreenshotViewData;
1413
import ch.ethz.seb.sps.server.ServiceInitEvent;
15-
import ch.ethz.seb.sps.server.datalayer.batis.model.ScreenshotDataRecord;
1614
import ch.ethz.seb.sps.server.servicelayer.impl.ScreenshotQueueData;
17-
import ch.ethz.seb.sps.utils.Result;
1815
import org.springframework.context.event.EventListener;
1916

2017
public interface LiveProctoringCacheService {
@@ -27,7 +24,7 @@ public interface LiveProctoringCacheService {
2724
* @param sessionUUID The live session UUID
2825
* @return PK id of the last screenshot_data row if available or -1 if there is no screenshot yet or null
2926
* if there is no slot for the given sessionUUID*/
30-
Long getLatestSSDataId(String sessionUUID, boolean createSlot);
27+
Long getLatestSSDataId(String sessionUUID);
3128

3229
/** Called by the batch store services to update latest cache entries on storage
3330
* @param batch The batch with the latest screenshot_data ids */

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,8 @@ public void init() {
115115
}
116116

117117
@Override
118-
public Long getLatestSSDataId(final String sessionUUID, final boolean createSlot) {
119-
if (!cache.containsKey(sessionUUID) && createSlot) {
118+
public Long getLatestSSDataId(final String sessionUUID) {
119+
if (!cache.containsKey(sessionUUID)) {
120120

121121
if (!this.sessionDAO.isActive(sessionUUID)) {
122122
return null;
@@ -129,9 +129,8 @@ public Long getLatestSSDataId(final String sessionUUID, final boolean createSlot
129129
synchronized (this.cache) {
130130
screenshotDataLiveCacheDAO
131131
.createCacheEntry(sessionUUID)
132-
.onError(error -> log.error("Failed to create slot for session: {}", sessionUUID));
133-
134-
cache.put(sessionUUID, -1L);
132+
.onError(error -> log.error("Failed to create slot for session: {}", sessionUUID))
133+
.onSuccess(rec -> cache.put(sessionUUID, rec.getIdLatestSsd()));
135134
}
136135
}
137136

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

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ public synchronized Result<ScreenshotViewData> getRecordedImageDataAt(final Stri
133133
// this means last timestamp and only should be called on live recording views
134134
if (sessionActive) {
135135
// get latest screenshot data form DB and check if we need an update on session cache
136-
Long pk = liveProctoringCacheService.getLatestSSDataId(sessionUUID, false);
136+
Long pk = liveProctoringCacheService.getLatestSSDataId(sessionUUID);
137137
if (pk == null || pk < 0) {
138138
if (log.isDebugEnabled()) {
139139
log.warn("Failed to get live screenshot id for session: {}", sessionUUID );
@@ -151,11 +151,7 @@ public synchronized Result<ScreenshotViewData> getRecordedImageDataAt(final Stri
151151
.getSessionScreenshotData(sessionUUID);
152152

153153
if (sessionActive) {
154-
// check first if cache is up to date if timestamp is after last timestamp in cache, reload cache first
155-
if (sessionScreenshotData.timestamps[sessionScreenshotData.timestamps.length - 1] < timestamp) {
156-
proctoringCacheService.evictSessionScreenshotData(sessionUUID);
157-
sessionScreenshotData = this.proctoringCacheService.getSessionScreenshotData(sessionUUID);
158-
}
154+
sessionScreenshotData = getActualSessionScreenshotCacheData(timestamp, sessionUUID, sessionScreenshotData);
159155
}
160156

161157
return createScreenshotViewData(sessionUUID, sessionScreenshotData.getAt(timestamp));
@@ -209,7 +205,7 @@ public synchronized Result<ScreenshotsInGroupData> getSessionsByGroup(
209205
.allOfMappedToSession(
210206
sessionIdsInOrder
211207
.stream()
212-
.map(uuid -> liveProctoringCacheService.getLatestSSDataId(uuid, true))
208+
.map(liveProctoringCacheService::getLatestSSDataId)
213209
.toList())
214210
.getOrThrow();
215211

@@ -454,9 +450,14 @@ public DistinctMetadataWindowForExam getDistinctMetadataWindowForExam(final Stri
454450
private void streamLatestScreenshot(final String sessionUUID, final OutputStream out) {
455451
InputStream screenshotIn = null;
456452
try {
457-
453+
454+
Long latestSSDataId = this.liveProctoringCacheService.getLatestSSDataId(sessionUUID);
455+
if (latestSSDataId == null) {
456+
return;
457+
}
458+
458459
screenshotIn = this.screenshotDAO
459-
.getImage(this.liveProctoringCacheService.getLatestSSDataId(sessionUUID, true), sessionUUID)
460+
.getImage(latestSSDataId, sessionUUID)
460461
.getOrThrow();
461462

462463
IOUtils.copy(screenshotIn, out);
@@ -477,7 +478,7 @@ private void streamScreenshotAt(
477478
final Long timestamp,
478479
final OutputStream out) {
479480

480-
if (timestamp == null) {
481+
if (timestamp == null || Utils.getMillisecondsNow() - timestamp < 5 * Constants.SECOND_IN_MILLIS) {
481482
streamLatestScreenshot(sessionUUID, out);
482483
return;
483484
}
@@ -487,14 +488,6 @@ private void streamScreenshotAt(
487488

488489
SessionScreenshotCacheData sessionScreenshotData = this.proctoringCacheService
489490
.getSessionScreenshotData(sessionUUID);
490-
491-
if (timestamp - sessionScreenshotData.timestamps[sessionScreenshotData.timestamps.length - 1] > 5 * Constants.SECOND_IN_MILLIS ) {
492-
// refresh the session cache
493-
this.proctoringCacheService.evictSessionScreenshotData(sessionUUID);
494-
this.proctoringCacheService
495-
.getSessionScreenshotData(sessionUUID);
496-
}
497-
498491
final ScreenshotDataRecord at = sessionScreenshotData.getAt(timestamp);
499492

500493
screenshotIn = this.screenshotDAO
@@ -673,4 +666,22 @@ private void updateSessionCache(String groupUUID) {
673666
}
674667
}
675668

669+
// this can be used if we still have a live session, but it might be outdated or abandoned by SEB...
670+
// check first if cache is up to date with the last image from live cache and refresh the cache only if needed
671+
private SessionScreenshotCacheData getActualSessionScreenshotCacheData(
672+
final Long timestamp,
673+
final String sessionUUID,
674+
final SessionScreenshotCacheData data) {
675+
676+
Long pk = liveProctoringCacheService.getLatestSSDataId(sessionUUID);
677+
ScreenshotDataRecord lastCacheEntry = data.data[data.data.length - 1];
678+
if (pk != null) {
679+
if (lastCacheEntry.getTimestamp() < timestamp && !Objects.equals(pk, lastCacheEntry.getId())) {
680+
proctoringCacheService.evictSessionScreenshotData(sessionUUID);
681+
return this.proctoringCacheService.getSessionScreenshotData(sessionUUID);
682+
}
683+
}
684+
return data;
685+
}
686+
676687
}

src/test/java/ch/ethz/seb/sps/server/servicelayer/proctoringservice/ProctoringServiceGroupTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ private void mockDependenciesForEmptyResult(){
161161
}
162162

163163
private void mockDependenciesForRealisticResult() throws JsonProcessingException {
164-
when( this.liveProctoringCacheService.getLatestSSDataId(any(), anyBoolean()))
164+
when( this.liveProctoringCacheService.getLatestSSDataId(any()))
165165
.thenReturn(-1L);
166166

167167
when(this.proctoringCacheService.getActiveGroup(any()))

0 commit comments

Comments
 (0)