Better-scoped queries for currentUserOwnsThis etc

I hypothesize that loading people's full trade lists more often than necessary is part of the cause of the recent mega slowdown!

My hypothesis is that we're clogging up the MySQL connection socket with a ton of data, which blocks all other queries until the big ones come through and parse out. (I haven't actually validated my assumption that MySQL connections send query results in serial like that, but it makes sense to me, and fits what I've been seeing.)

There's more places we could potentially optimize, like the trade list page itself… (we currently aggressively load everything when we could limit it and load the rest on the followup pages, or even paginate the followup pages…)

…but my hope is that this helps enough, by relieving the load on the homepage (latest items) and on item searches!
This commit is contained in:
Emi Matchu 2022-01-07 11:37:27 -08:00
parent 6ce8a5aea2
commit e051290df4
2 changed files with 38 additions and 10 deletions

View file

@ -1245,6 +1245,25 @@ const buildUserClosetHangersLoader = (db) =>
);
});
const buildUserItemClosetHangersLoader = (db) =>
new DataLoader(async (userIdAndItemIdPairs) => {
const conditions = userIdAndItemIdPairs
.map((_) => `(user_id = ? AND item_id = ?)`)
.join(` OR `);
const params = userIdAndItemIdPairs
.map(({ userId, itemId }) => [userId, itemId])
.flat();
const [rows] = await db.execute(
`SELECT * FROM closet_hangers WHERE ${conditions};`,
params
);
const entities = rows.map(normalizeRow);
return userIdAndItemIdPairs.map(({ userId, itemId }) =>
entities.filter((e) => e.userId === userId && e.itemId === itemId)
);
});
const buildUserClosetListsLoader = (db, loaders) =>
new DataLoader(async (userIds) => {
const qs = userIds.map((_) => "?").join(",");
@ -1481,6 +1500,7 @@ function buildLoaders(db) {
loaders.userByNameLoader = buildUserByNameLoader(db);
loaders.userByEmailLoader = buildUserByEmailLoader(db);
loaders.userClosetHangersLoader = buildUserClosetHangersLoader(db);
loaders.userItemClosetHangersLoader = buildUserItemClosetHangersLoader(db);
loaders.userClosetListsLoader = buildUserClosetListsLoader(db, loaders);
loaders.userNumTotalOutfitsLoader = buildUserNumTotalOutfitsLoader(db);
loaders.userOutfitsLoader = buildUserOutfitsLoader(db, loaders);

View file

@ -348,30 +348,38 @@ const resolvers = {
currentUserOwnsThis: async (
{ id },
_,
{ currentUserId, userClosetHangersLoader }
{ currentUserId, userItemClosetHangersLoader }
) => {
if (currentUserId == null) return false;
const closetHangers = await userClosetHangersLoader.load(currentUserId);
return closetHangers.some((h) => h.itemId === id && h.owned);
const closetHangers = await userItemClosetHangersLoader.load({
userId: currentUserId,
itemId: id,
});
return closetHangers.some((h) => h.owned);
},
currentUserWantsThis: async (
{ id },
_,
{ currentUserId, userClosetHangersLoader }
{ currentUserId, userItemClosetHangersLoader }
) => {
if (currentUserId == null) return false;
const closetHangers = await userClosetHangersLoader.load(currentUserId);
return closetHangers.some((h) => h.itemId === id && !h.owned);
const closetHangers = await userItemClosetHangersLoader.load({
userId: currentUserId,
itemId: id,
});
return closetHangers.some((h) => !h.owned);
},
currentUserHasInLists: async (
{ id },
_,
{ currentUserId, userClosetHangersLoader }
{ currentUserId, userItemClosetHangersLoader }
) => {
if (currentUserId == null) return false;
const closetHangers = await userClosetHangersLoader.load(currentUserId);
const itemHangers = closetHangers.filter((h) => h.itemId === id);
const listRefs = itemHangers.map((hanger) => {
const closetHangers = await userItemClosetHangersLoader.load({
userId: currentUserId,
itemId: id,
});
const listRefs = closetHangers.map((hanger) => {
if (hanger.listId) {
return { id: hanger.listId };
} else {