Skip to content

Commit 6a7e565

Browse files
authored
perf: studysession caching of anticipated card data (#865)
Up to the mood of the backend, studySessions were sometimes laggy because card lookups require 2 db trips (card info, then data hydration). This PR moves some db logic from common-ui/StudySession.vue to db/SessionController.ts (better location) and improves it w/ a running cache of ~5 cards worth of hydrated data. - **prep work on card data caching** - **migrate nextCard selection to SessionController...** - **use hydrated card stack for nextQ** - **pass filterFcns, await initial hydration lookup** - **rm working docs on caching card data**
2 parents 8c4a191 + b09e72c commit 6a7e565

File tree

2 files changed

+195
-115
lines changed

2 files changed

+195
-115
lines changed

packages/common-ui/src/components/StudySession.vue

Lines changed: 38 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -81,27 +81,17 @@ import {
8181
StudyContentSource,
8282
StudySessionItem,
8383
docIsDeleted,
84-
CardData,
8584
CardHistory,
8685
CardRecord,
87-
DisplayableData,
8886
isQuestionRecord,
8987
CourseRegistrationDoc,
9088
DataLayerProvider,
9189
UserDBInterface,
9290
ClassroomDBInterface,
9391
} from '@vue-skuilder/db';
94-
import { SessionController, StudySessionRecord } from '@vue-skuilder/db';
92+
import { HydratedCard, SessionController, StudySessionRecord } from '@vue-skuilder/db';
9593
import { newInterval } from '@vue-skuilder/db';
96-
import {
97-
adjustCourseScores,
98-
CourseElo,
99-
toCourseElo,
100-
isCourseElo,
101-
displayableDataToViewData,
102-
ViewData,
103-
Status,
104-
} from '@vue-skuilder/common';
94+
import { adjustCourseScores, CourseElo, toCourseElo, ViewData, Status } from '@vue-skuilder/common';
10595
import confetti from 'canvas-confetti';
10696
import moment from 'moment';
10797
@@ -175,7 +165,7 @@ export default defineComponent({
175165
card_elo: 1000,
176166
courseNames: {} as { [courseID: string]: string },
177167
cardCount: 1,
178-
sessionController: null as SessionController | null,
168+
sessionController: null as SessionController<ViewComponent> | null,
179169
sessionPrepared: false,
180170
sessionFinished: false,
181171
sessionRecord: [] as StudySessionRecord[],
@@ -295,7 +285,7 @@ export default defineComponent({
295285
}
296286
})
297287
)
298-
).filter((s) => s !== null)
288+
).filter((s: unknown) => s !== null)
299289
);
300290
301291
this.timeRemaining = this.sessionTimeLimit * 60;
@@ -310,7 +300,14 @@ export default defineComponent({
310300
// db.setChangeFcn(this.handleClassroomMessage());
311301
});
312302
313-
this.sessionController = markRaw(new SessionController(this.sessionContentSources, 60 * this.sessionTimeLimit));
303+
this.sessionController = markRaw(
304+
new SessionController<ViewComponent>(
305+
this.sessionContentSources,
306+
60 * this.sessionTimeLimit,
307+
this.dataLayer,
308+
this.getViewComponent
309+
)
310+
);
314311
this.sessionController.sessionRecord = this.sessionRecord;
315312
316313
await this.sessionController.prepareSession();
@@ -349,7 +346,7 @@ export default defineComponent({
349346
if (this.sessionController) {
350347
try {
351348
this.$emit('session-started');
352-
this.loadCard(this.sessionController.nextCard());
349+
this.loadCard(await this.sessionController.nextCard());
353350
} catch (error) {
354351
console.error('[StudySession] Error loading next card:', error);
355352
this.$emit('session-error', { message: 'Failed to load study card', error });
@@ -407,7 +404,7 @@ export default defineComponent({
407404
const item: StudySessionItem = {
408405
...this.currentCard.item,
409406
};
410-
this.loadCard(this.sessionController!.nextCard('dismiss-success'));
407+
this.loadCard(await this.sessionController!.nextCard('dismiss-success'));
411408
412409
cardHistory.then((history: CardHistory<CardRecord>) => {
413410
this.scheduleReview(history, item);
@@ -419,7 +416,7 @@ export default defineComponent({
419416
}
420417
});
421418
} else {
422-
this.loadCard(this.sessionController!.nextCard('marked-failed'));
419+
this.loadCard(await this.sessionController!.nextCard('marked-failed'));
423420
}
424421
} else {
425422
/* !r.isCorrect */
@@ -445,16 +442,16 @@ export default defineComponent({
445442
if (this.currentCard.records.length >= view.maxAttemptsPerView) {
446443
const sessionViews: number = this.countCardViews(this.courseID, this.cardID);
447444
if (sessionViews >= view.maxSessionViews) {
448-
this.loadCard(this.sessionController!.nextCard('dismiss-failed'));
445+
this.loadCard(await this.sessionController!.nextCard('dismiss-failed'));
449446
this.updateUserAndCardElo(0, this.courseID, this.cardID);
450447
} else {
451-
this.loadCard(this.sessionController!.nextCard('marked-failed'));
448+
this.loadCard(await this.sessionController!.nextCard('marked-failed'));
452449
}
453450
}
454451
}
455452
}
456453
} else {
457-
this.loadCard(this.sessionController!.nextCard('dismiss-success'));
454+
this.loadCard(await this.sessionController!.nextCard('dismiss-success'));
458455
}
459456
460457
this.clearFeedbackShadow();
@@ -548,81 +545,56 @@ export default defineComponent({
548545
});
549546
},
550547
551-
async loadCard(item: StudySessionItem | null) {
548+
async loadCard(card: HydratedCard | null) {
552549
if (this.loading) {
553550
console.warn(`Attempted to load card while loading another...`);
554551
return;
555552
}
556553
557-
console.log(`[StudySession] loading: ${JSON.stringify(item)}`);
558-
if (item === null) {
554+
console.log(`[StudySession] loading: ${JSON.stringify(card)}`);
555+
if (card === null) {
559556
this.sessionFinished = true;
560557
this.$emit('session-finished', this.sessionRecord);
561558
return;
562559
}
563-
this.cardType = item.status;
560+
this.cardType = card.item.status;
564561
565562
this.loading = true;
566-
const _courseID = item.courseID;
567-
const _cardID = item.cardID;
568-
569-
console.log(`[StudySession] Now displaying: ${_courseID}::${_cardID}`);
570563
571564
try {
572-
const tmpCardData = await this.dataLayer.getCourseDB(_courseID).getCourseDoc<CardData>(_cardID);
573-
574-
if (!isCourseElo(tmpCardData.elo)) {
575-
tmpCardData.elo = toCourseElo(tmpCardData.elo);
576-
}
577-
578-
const tmpView: ViewComponent = this.getViewComponent(tmpCardData.id_view);
579-
const tmpDataDocs = tmpCardData.id_displayable_data.map((id) => {
580-
return this.dataLayer.getCourseDB(_courseID).getCourseDoc<DisplayableData>(id, {
581-
attachments: true,
582-
binary: true,
583-
});
584-
});
585-
586-
const tmpData = [];
587-
588-
for (const docPromise of tmpDataDocs) {
589-
const doc = await docPromise;
590-
tmpData.unshift(displayableDataToViewData(doc));
591-
}
592-
593565
this.cardCount++;
594-
this.data = tmpData;
595-
this.view = markRaw(tmpView);
596-
this.cardID = _cardID;
597-
this.courseID = _courseID;
598-
this.card_elo = tmpCardData.elo.global.score;
566+
this.data = card.data;
567+
this.view = markRaw(card.view);
568+
this.cardID = card.item.cardID;
569+
this.courseID = card.item.courseID;
570+
this.card_elo = card.item.elo || 1000;
599571
600572
this.sessionRecord.push({
601573
card: {
602-
course_id: _courseID,
603-
card_id: _cardID,
604-
card_elo: tmpCardData.elo.global.score,
574+
course_id: card.item.courseID,
575+
card_id: card.item.cardID,
576+
card_elo: this.card_elo,
605577
},
606-
item: item,
578+
item: card.item,
607579
records: [],
608580
});
609581
610582
this.$emit('card-loaded', {
611-
courseID: _courseID,
612-
cardID: _cardID,
583+
courseID: card.item.courseID,
584+
cardID: card.item.cardID,
613585
cardCount: this.cardCount,
614586
});
615587
} catch (e) {
616-
console.warn(`[StudySession] Error loading card ${JSON.stringify(item)}:\n\t${JSON.stringify(e)}, ${e}`);
588+
console.warn(`[StudySession] Error loading card ${JSON.stringify(card)}:\n\t${JSON.stringify(e)}, ${e}`);
617589
this.loading = false;
618590
619591
const err = e as Error;
620-
if (docIsDeleted(err) && isReview(item)) {
621-
console.warn(`Card was deleted: ${_courseID}::${_cardID}`);
622-
this.user!.removeScheduledCardReview(item.reviewID);
592+
if (docIsDeleted(err) && isReview(card.item)) {
593+
console.warn(`Card was deleted: ${card.item.courseID}::${card.item.cardID}`);
594+
this.user!.removeScheduledCardReview((card.item as any).reviewID);
623595
}
624596
625-
this.loadCard(this.sessionController!.nextCard('dismiss-error'));
597+
this.loadCard(await this.sessionController!.nextCard('dismiss-error'));
626598
} finally {
627599
this.loading = false;
628600
}

0 commit comments

Comments
 (0)