Skip to content

Commit 4869afc

Browse files
authored
Fix 500 server error on project dashboard (#1779)
Large projects were returning an HTTP 500 on the project dashboard, because PHP was running out of memory trying to return every entry for stats to be calculated client-side. This changes the stats to be calculated server-side instead. We get rid of the "number of entries with audio" stat because it's not possible to calculate in a simple Mongo query and would require serializing the entire entry database server-side, causing the very memory error we're trying to get rid of.
1 parent 68d75e5 commit 4869afc

File tree

5 files changed

+65
-26
lines changed

5 files changed

+65
-26
lines changed

next-app/src/routes/projects/[project_code]/+page.svelte

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,6 @@
3333
icon: NotesIcon,
3434
url: `/app/lexicon/${ project.id }`,
3535
},
36-
{
37-
title: 'Entries with audio',
38-
value: project.num_entries_with_audio,
39-
icon: VoiceIcon,
40-
url: `/app/lexicon/${ project.id }#!/editor/entry/000000?filterBy=Audio`,
41-
},
4236
{
4337
title: 'Entries with pictures',
4438
value: project.num_entries_with_pictures,

next-app/src/routes/projects/[project_code]/meta/+server.ts

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ type LegacyProjectDetails = {
99
}
1010

1111
type LegacyStats = {
12-
entries: object[],
13-
comments: Comment[],
12+
num_entries,
13+
num_entries_with_pictures,
14+
num_unresolved_comments,
1415
}
1516

1617
type Comment = {
@@ -23,40 +24,27 @@ export type ProjectDetails = {
2324
name: string,
2425
num_users: number,
2526
num_entries: number,
26-
num_entries_with_audio: number,
2727
num_entries_with_pictures: number,
2828
num_unresolved_comments?: number,
2929
}
3030

3131
export async function fetch_project_details({ project_code, cookie }) {
3232
const { id, projectName: name, users }: LegacyProjectDetails = await sf({ name: 'set_project', args: [ project_code ], cookie })
33-
const { entries, comments }: LegacyStats = await sf({ name: 'lex_stats', cookie })
33+
const stats: LegacyStats = await sf({ name: 'lex_stats', cookie })
3434

3535
const details: ProjectDetails = {
3636
id,
3737
code: project_code,
3838
name,
3939
num_users: Object.keys(users).length,
40-
num_entries: entries.length,
41-
num_entries_with_audio: entries.filter(has_audio).length,
42-
num_entries_with_pictures: entries.filter(has_picture).length,
40+
num_entries: stats.num_entries,
41+
num_entries_with_pictures: stats.num_entries_with_pictures,
4342
}
4443

4544
const { role } = await fetch_current_user(cookie)
4645
if (can_view_comments(role)) {
47-
const unresolved_comments = comments.filter(({ status }) => status !== 'resolved')
48-
49-
details.num_unresolved_comments = unresolved_comments.length
46+
details.num_unresolved_comments = stats.num_unresolved_comments
5047
}
5148

5249
return details
5350
}
54-
55-
function has_picture(entry: object) {
56-
return JSON.stringify(entry).includes('"pictures":')
57-
}
58-
59-
// audio can be found in lots of places other than lexeme, ref impl used: https://github.com/sillsdev/web-languageforge/blob/develop/src/angular-app/bellows/core/offline/editor-data.service.ts#L523
60-
function has_audio(entry: object) {
61-
return JSON.stringify(entry).includes('-audio":') // naming convention imposed by src/angular-app/languageforge/lexicon/settings/configuration/input-system-view.model.ts L81
62-
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace Api\Model\Languageforge\Lexicon\Dto;
4+
use Api\Model\Shared\Mapper\MongoQueries;
5+
6+
class LexStatsDto
7+
{
8+
/**
9+
* @param ProjectModel $project
10+
* @throws \Exception
11+
* @return array
12+
*/
13+
public static function encode($project)
14+
{
15+
$db = MongoStore::connect($project->databaseName());
16+
$num_entries = MongoQueries::countEntries($db, "lexicon");
17+
$num_entries_with_pictures = MongoQueries::countEntriesWithPictures($db, "lexicon");
18+
$num_unresolved_comments = MongoQueries::countUnresolvedComments($db, "lexiconComments");
19+
return [
20+
"num_entries" => $num_entries,
21+
"num_entries_with_pictures" => $num_entries_with_pictures,
22+
"num_unresolved_comments" => $num_unresolved_comments,
23+
];
24+
}
25+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace Api\Model\Shared\Mapper;
4+
5+
class MongoQueries
6+
{
7+
public static function countEntries($db, $collectionName)
8+
{
9+
$coll = $db->selectCollection($collectionName);
10+
return $coll->count();
11+
}
12+
13+
public static function countEntriesWithPictures($db, $collectionName)
14+
{
15+
$coll = $db->selectCollection($collectionName);
16+
$query = [
17+
"senses" => ['$exists' => true, '$ne' => []],
18+
"senses.pictures" => ['$exists' => true, '$ne' => []],
19+
];
20+
return $coll->count($query);
21+
}
22+
23+
public static function countUnresolvedComments($db, $collectionName)
24+
{
25+
$coll = $db->selectCollection($collectionName);
26+
$query = [
27+
"status" => ['$exists' => true, '$ne' => "resolved"],
28+
];
29+
return $coll->count($query);
30+
}
31+
}

src/Api/Service/Sf.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Api\Model\Languageforge\Lexicon\Dto\LexBaseViewDto;
1515
use Api\Model\Languageforge\Lexicon\Dto\LexDbeDto;
1616
use Api\Model\Languageforge\Lexicon\Dto\LexProjectDto;
17+
use Api\Model\Languageforge\Lexicon\Dto\LexStatsDto;
1718
use Api\Model\Shared\Command\ProjectCommands;
1819
use Api\Model\Shared\Command\SessionCommands;
1920
use Api\Model\Shared\Command\UserCommands;
@@ -518,7 +519,7 @@ public function lex_stats()
518519
$user = new UserModel($this->userId);
519520

520521
if ($user->isMemberOfProject($this->projectId)) {
521-
return LexDbeDto::encode($projectModel->id->asString(), $this->userId, 1);
522+
return LexStatsDto::encode($projectModel);
522523
}
523524

524525
throw new UserUnauthorizedException("User $this->userId is not a member of project $projectModel->projectCode");

0 commit comments

Comments
 (0)