From bfe6dc866d73a8a02500a3ba19e26d49853b98f5 Mon Sep 17 00:00:00 2001 From: DebaucheryLibrarian Date: Wed, 12 Jun 2024 04:23:46 +0200 Subject: [PATCH] Caching campaigns in redis. --- components/campaigns/campaign.vue | 2 +- src/app.js | 2 + src/campaigns.js | 79 +++++++++++++++++++++++-------- 3 files changed, 63 insertions(+), 20 deletions(-) diff --git a/components/campaigns/campaign.vue b/components/campaigns/campaign.vue index 1463a4c..9d0f2c9 100644 --- a/components/campaigns/campaign.vue +++ b/components/campaigns/campaign.vue @@ -22,7 +22,7 @@ const props = defineProps({ }, }); -console.log(props.campaign?.banner); +// console.log(props.campaign?.banner); function getBanner(campaign) { if (campaign.banner.entity.type === 'network' || !campaign.banner.entity.parent) { diff --git a/src/app.js b/src/app.js index 1a82620..beb847d 100644 --- a/src/app.js +++ b/src/app.js @@ -1,11 +1,13 @@ import initServer from './web/server.js'; import { cacheTagIds } from './tags.js'; import { cacheEntityIds } from './entities.js'; +import { cacheCampaigns } from './campaigns.js'; async function init() { await Promise.all([ cacheTagIds(), cacheEntityIds(), + cacheCampaigns(), ]); initServer(); diff --git a/src/campaigns.js b/src/campaigns.js index 5577332..b6ee7a8 100644 --- a/src/campaigns.js +++ b/src/campaigns.js @@ -1,5 +1,11 @@ +import crypto from 'crypto'; + import { knexOwner as knex } from './knex.js'; import { curateEntity } from './entities.js'; +import redis from './redis.js'; +import initLogger from './logger.js'; + +const logger = initLogger(); function curateCampaign(campaign) { if (!campaign) { @@ -12,6 +18,9 @@ function curateCampaign(campaign) { banner: campaign.banner && { id: campaign.banner.id, type: campaign.banner.type, + width: campaign.banner.width, + height: campaign.banner.height, + ratio: campaign.banner.ratio, entity: campaign.banner_entity && curateEntity({ ...campaign.banner_entity, parent: campaign.banner_parent_entity }), tags: campaign.banner_tags?.map((tag) => ({ id: tag.id, @@ -19,29 +28,61 @@ function curateCampaign(campaign) { name: tag.name, })) || [], }, - affiliateId: campaign.affiliate_id, + affiliate: campaign.affiliate && { + id: campaign.affiliate.id, + url: campaign.affiliate.url, + parameters: campaign.affiliate.parameters, + }, }; } export async function getRandomCampaign(options = {}) { - const campaign = await knex.raw(` - SELECT - campaigns.*, row_to_json(banners) as banner, row_to_json(entities) as banner_entity, row_to_json(parents) as banner_parent_entity, json_agg(tags) filter (where tags.id is not null) as banner_tags - FROM campaigns - LEFT JOIN banners ON banners.id = campaigns.banner_id - LEFT JOIN banners_tags ON banners_tags.banner_id = banners.id - LEFT JOIN tags ON tags.id = banners_tags.tag_id - LEFT JOIN entities ON entities.id = banners.entity_id - LEFT JOIN entities as parents ON parents.id = entities.parent_id - WHERE campaigns.banner_id IS NOT NULL - AND ratio >= :minRatio - AND ratio <= :maxRatio - GROUP BY campaigns.id, banners.id, entities.id, parents.id - ORDER BY RANDOM() - `, { - minRatio: options.minRatio || 0, - maxRatio: options.maxRatio || 1000, + const rawCampaigns = await redis.hGetAll('traxxx:campaigns'); + const campaigns = Object.values(rawCampaigns).map((rawCampaign) => JSON.parse(rawCampaign)); + + const validCampaigns = campaigns.filter((campaign) => { + if (options.minRatio && (!campaign.banner || campaign.banner.ratio < options.minRatio)) { + return false; + } + + if (options.maxRatio && (!campaign.banner || campaign.banner.ratio > options.maxRatio)) { + return false; + } + + return true; }); - return curateCampaign(campaign.rows[0]); + const randomCampaign = validCampaigns[crypto.randomInt(validCampaigns.length)]; + + return randomCampaign; +} + +export async function cacheCampaigns() { + const campaigns = await knex('campaigns') + .select('campaigns.*') + .select( + 'campaigns.*', + knex.raw('row_to_json(affiliates) as affiliate'), + knex.raw('row_to_json(banners) as banner'), + knex.raw('row_to_json(entities) as banner_entity'), + knex.raw('row_to_json(parents) as banner_parent_entity'), + knex.raw('json_agg(tags.slug) filter (where tags.id is not null) as banner_tags'), + ) + .leftJoin('affiliates', 'affiliates.id', 'campaigns.affiliate_id') + .leftJoin('banners', 'banners.id', 'campaigns.banner_id') + .leftJoin('banners_tags', 'banners_tags.banner_id', 'banners.id') + .leftJoin('tags', 'tags.id', 'banners_tags.tag_id') + .leftJoin('entities', 'entities.id', 'banners.entity_id') + .leftJoin('entities as parents', 'parents.id', 'entities.parent_id') + .groupBy('campaigns.id', 'affiliates.id', 'banners.id', 'entities.id', 'parents.id'); + + await redis.del('traxxx:campaigns'); + + await Promise.all(campaigns.map(async (campaign) => { + const curatedCampaign = curateCampaign(campaign); + + await redis.hSet('traxxx:campaigns', campaign.id, JSON.stringify(curatedCampaign)); + })); + + logger.info('Cached campaigns'); }