Skip to content

Commit 7b02654

Browse files
Keep counts in sync (when we fetch the whole list anyway)
1 parent 2ade7fa commit 7b02654

File tree

1 file changed

+182
-29
lines changed

1 file changed

+182
-29
lines changed

api/abstractplay.ts

Lines changed: 182 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,93 @@ async function sendCommandWithRetry(command: any, maxRetries = 8, initialDelay =
428428
}
429429
}
430430

431+
async function verifyAndCorrectCountWithData(metaGame: string, countType: "currentgames" | "completedgames" | "standingchallenges", actualCount: number, countsData: any) {
432+
try {
433+
if (!countsData.Item) {
434+
console.log(`No METAGAMES COUNTS found, creating initial record for ${metaGame}`);
435+
await ddbDocClient.send(new UpdateCommand({
436+
TableName: process.env.ABSTRACT_PLAY_TABLE,
437+
Key: { "pk": "METAGAMES", "sk": "COUNTS" },
438+
ExpressionAttributeNames: { "#g": metaGame },
439+
ExpressionAttributeValues: { ":count": actualCount },
440+
UpdateExpression: `set #g.${countType} = :count`
441+
}));
442+
console.log(`Initialized ${countType} for ${metaGame} to ${actualCount}`);
443+
return;
444+
}
445+
446+
const details = countsData.Item as MetaGameCounts;
447+
const storedCount = details[metaGame]?.[countType] || 0;
448+
449+
if (storedCount !== actualCount) {
450+
console.log(`Count mismatch for ${metaGame}.${countType}: stored=${storedCount}, actual=${actualCount}. Correcting...`);
451+
452+
await ddbDocClient.send(new UpdateCommand({
453+
TableName: process.env.ABSTRACT_PLAY_TABLE,
454+
Key: { "pk": "METAGAMES", "sk": "COUNTS" },
455+
ExpressionAttributeNames: { "#g": metaGame },
456+
ExpressionAttributeValues: { ":count": actualCount },
457+
UpdateExpression: `set #g.${countType} = :count`
458+
}));
459+
460+
console.log(`Corrected ${countType} for ${metaGame} from ${storedCount} to ${actualCount}`);
461+
}
462+
} catch (error) {
463+
console.error(`Error verifying/correcting count for ${metaGame}.${countType}:`, error);
464+
// Don't throw - we don't want to break the main query if count correction fails
465+
}
466+
}
467+
468+
async function verifyAndCorrectRatingsCountWithData(metaGame: string, actualRatings: any[], countsData: any) {
469+
try {
470+
// Extract unique player IDs from actual ratings data as a Set
471+
const actualPlayerIds = new Set(actualRatings.map(rating => rating.sk));
472+
const actualCount = actualPlayerIds.size;
473+
474+
if (!countsData.Item && actualCount > 0) {
475+
console.log(`No METAGAMES COUNTS found, creating initial ratings record for ${metaGame}`);
476+
await ddbDocClient.send(new UpdateCommand({
477+
TableName: process.env.ABSTRACT_PLAY_TABLE,
478+
Key: { "pk": "METAGAMES", "sk": "COUNTS" },
479+
ExpressionAttributeNames: { "#gr": metaGame + "_ratings" },
480+
ExpressionAttributeValues: { ":players": actualPlayerIds },
481+
UpdateExpression: `set #gr = :players`
482+
}));
483+
console.log(`Initialized ratings for ${metaGame} with ${actualCount} players`);
484+
return;
485+
}
486+
487+
const details = countsData.Item as any;
488+
const storedPlayerIds = details[metaGame + "_ratings"] || new Set();
489+
const storedCount = storedPlayerIds.size;
490+
491+
if (storedCount !== actualCount || !setsEqual(storedPlayerIds, actualPlayerIds)) {
492+
console.log(`Ratings mismatch for ${metaGame}: stored=${storedCount} players, actual=${actualCount} players. Updating player list...`);
493+
494+
await ddbDocClient.send(new UpdateCommand({
495+
TableName: process.env.ABSTRACT_PLAY_TABLE,
496+
Key: { "pk": "METAGAMES", "sk": "COUNTS" },
497+
ExpressionAttributeNames: { "#gr": metaGame + "_ratings" },
498+
ExpressionAttributeValues: { ":players": actualPlayerIds },
499+
UpdateExpression: `set #gr = :players`
500+
}));
501+
502+
console.log(`Updated ratings list for ${metaGame} from ${storedCount} to ${actualCount} players`);
503+
}
504+
} catch (error) {
505+
console.error(`Error verifying/correcting ratings for ${metaGame}:`, error);
506+
// Don't throw - we don't want to break the main query if count correction fails
507+
}
508+
}
509+
510+
function setsEqual(a: Set<any>, b: Set<any>): boolean {
511+
if (a.size !== b.size) return false;
512+
for (let item of a) {
513+
if (!b.has(item)) return false;
514+
}
515+
return true;
516+
}
517+
431518
module.exports.query = async (event: { queryStringParameters: any; }) => {
432519
console.log(event);
433520
const pars = event.queryStringParameters;
@@ -672,16 +759,36 @@ async function challengeDetails(pars: { id: string; }) {
672759
async function games(pars: { metaGame: string, type: string; }) {
673760
const game = pars.metaGame;
674761
console.log(game);
762+
763+
// Start fetching counts in parallel
764+
const countsPromise = ddbDocClient.send(
765+
new GetCommand({
766+
TableName: process.env.ABSTRACT_PLAY_TABLE,
767+
Key: {
768+
"pk": "METAGAMES", "sk": "COUNTS"
769+
},
770+
})
771+
);
772+
675773
if (pars.type === "current") {
676774
try {
677-
const gamesData = await ddbDocClient.send(
678-
new QueryCommand({
679-
TableName: process.env.ABSTRACT_PLAY_TABLE,
680-
KeyConditionExpression: "#pk = :pk and begins_with(#sk, :sk)",
681-
ExpressionAttributeValues: { ":pk": "GAME", ":sk": game + '#0#' },
682-
ExpressionAttributeNames: { "#pk": "pk", "#sk": "sk" },
683-
}));
775+
const [gamesData, countsData] = await Promise.all([
776+
ddbDocClient.send(
777+
new QueryCommand({
778+
TableName: process.env.ABSTRACT_PLAY_TABLE,
779+
KeyConditionExpression: "#pk = :pk and begins_with(#sk, :sk)",
780+
ExpressionAttributeValues: { ":pk": "GAME", ":sk": game + '#0#' },
781+
ExpressionAttributeNames: { "#pk": "pk", "#sk": "sk" },
782+
})),
783+
countsPromise
784+
]);
785+
684786
const gamelist = gamesData.Items as FullGame[];
787+
788+
// Verify and correct current games count
789+
const actualCount = gamelist.length;
790+
await verifyAndCorrectCountWithData(game, "currentgames", actualCount, countsData);
791+
685792
const returnlist = gamelist.map(g => {
686793
const state = GameFactory(g.metaGame, g.state); // JSON.parse(g.state);
687794
if (state === undefined) {
@@ -701,13 +808,21 @@ async function games(pars: { metaGame: string, type: string; }) {
701808
}
702809
} else if (pars.type === "completed") {
703810
try {
704-
const gamesData = await ddbDocClient.send(
705-
new QueryCommand({
706-
TableName: process.env.ABSTRACT_PLAY_TABLE,
707-
KeyConditionExpression: "#pk = :pk",
708-
ExpressionAttributeValues: { ":pk": "COMPLETEDGAMES#" + game },
709-
ExpressionAttributeNames: { "#pk": "pk" }
710-
}));
811+
const [gamesData, countsData] = await Promise.all([
812+
ddbDocClient.send(
813+
new QueryCommand({
814+
TableName: process.env.ABSTRACT_PLAY_TABLE,
815+
KeyConditionExpression: "#pk = :pk",
816+
ExpressionAttributeValues: { ":pk": "COMPLETEDGAMES#" + game },
817+
ExpressionAttributeNames: { "#pk": "pk" }
818+
})),
819+
countsPromise
820+
]);
821+
822+
// Verify and correct completed games count
823+
const actualCount = gamesData.Items?.length || 0;
824+
await verifyAndCorrectCountWithData(game, "completedgames", actualCount, countsData);
825+
711826
return {
712827
statusCode: 200,
713828
body: JSON.stringify(gamesData.Items),
@@ -725,14 +840,33 @@ async function games(pars: { metaGame: string, type: string; }) {
725840

726841
async function ratings(pars: { metaGame: string }) {
727842
const game = pars.metaGame;
843+
844+
// Start fetching counts in parallel
845+
const countsPromise = ddbDocClient.send(
846+
new GetCommand({
847+
TableName: process.env.ABSTRACT_PLAY_TABLE,
848+
Key: {
849+
"pk": "METAGAMES", "sk": "COUNTS"
850+
},
851+
})
852+
);
853+
728854
try {
729-
const ratingsData = await ddbDocClient.send(
730-
new QueryCommand({
731-
TableName: process.env.ABSTRACT_PLAY_TABLE,
732-
KeyConditionExpression: "#pk = :pk",
733-
ExpressionAttributeValues: { ":pk": "RATINGS#" + game },
734-
ExpressionAttributeNames: { "#pk": "pk" }
735-
}));
855+
const [ratingsData, countsData] = await Promise.all([
856+
ddbDocClient.send(
857+
new QueryCommand({
858+
TableName: process.env.ABSTRACT_PLAY_TABLE,
859+
KeyConditionExpression: "#pk = :pk",
860+
ExpressionAttributeValues: { ":pk": "RATINGS#" + game },
861+
ExpressionAttributeNames: { "#pk": "pk" }
862+
})),
863+
countsPromise
864+
]);
865+
866+
// Verify and correct ratings count
867+
const actualRatings = ratingsData.Items || [];
868+
await verifyAndCorrectRatingsCountWithData(game, actualRatings, countsData);
869+
736870
return {
737871
statusCode: 200,
738872
body: JSON.stringify(ratingsData.Items),
@@ -748,17 +882,36 @@ async function ratings(pars: { metaGame: string }) {
748882
async function standingChallenges(pars: { metaGame: string; }) {
749883
const game = pars.metaGame;
750884
console.log(game);
885+
886+
// Start fetching counts in parallel
887+
const countsPromise = ddbDocClient.send(
888+
new GetCommand({
889+
TableName: process.env.ABSTRACT_PLAY_TABLE,
890+
Key: {
891+
"pk": "METAGAMES", "sk": "COUNTS"
892+
},
893+
})
894+
);
895+
751896
try {
752-
const challenges = await ddbDocClient.send(
753-
new QueryCommand({
754-
TableName: process.env.ABSTRACT_PLAY_TABLE,
755-
KeyConditionExpression: "#pk = :pk",
756-
ExpressionAttributeValues: { ":pk": "STANDINGCHALLENGE#" + game },
757-
ExpressionAttributeNames: { "#pk": "pk" }
758-
}));
897+
const [challengesData, countsData] = await Promise.all([
898+
ddbDocClient.send(
899+
new QueryCommand({
900+
TableName: process.env.ABSTRACT_PLAY_TABLE,
901+
KeyConditionExpression: "#pk = :pk",
902+
ExpressionAttributeValues: { ":pk": "STANDINGCHALLENGE#" + game },
903+
ExpressionAttributeNames: { "#pk": "pk" }
904+
})),
905+
countsPromise
906+
]);
907+
908+
// Verify and correct standing challenges count
909+
const actualCount = challengesData.Items?.length || 0;
910+
await verifyAndCorrectCountWithData(game, "standingchallenges", actualCount, countsData);
911+
759912
return {
760913
statusCode: 200,
761-
body: JSON.stringify(challenges.Items),
914+
body: JSON.stringify(challengesData.Items),
762915
headers
763916
};
764917
}

0 commit comments

Comments
 (0)