0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-02-03 23:00:14 -05:00

Added caching to TierRepository

refs https://github.com/TryGhost/Toolbox/issues/515

Tiers are very frequently queried and we want to reduce the number of DB calls
we're making. We can store the Tiers in-memory, using the existing in-memory
repository patterns, but still persisting writes the the database.

We also have to update our test helpers, because they were bypassing the
repository for writes, but using it for reads resulting in an invalid cache
This commit is contained in:
Fabien "egg" O'Carroll 2023-02-03 17:55:09 +07:00 committed by Fabien 'egg' O'Carroll
parent 6ab862568c
commit c0ca7b16f6
5 changed files with 73 additions and 19 deletions

View file

@ -1,4 +1,5 @@
const {Tier} = require('@tryghost/tiers');
const nql = require('@tryghost/nql');
/**
* @typedef {import('@tryghost/tiers/lib/TiersAPI').ITierRepository} ITierRepository
@ -8,6 +9,11 @@ const {Tier} = require('@tryghost/tiers');
* @implements {ITierRepository}
*/
module.exports = class TierRepository {
/** @type {import('@tryghost/tiers/lib/Tier')[]} */
#store = [];
/** @type {Object.<string, true>} */
#ids = {};
/** @type {Object} */
#ProductModel;
@ -24,6 +30,32 @@ module.exports = class TierRepository {
this.#DomainEvents = deps.DomainEvents;
}
async init() {
this.#store = [];
this.#ids = {};
const models = await this.#ProductModel.findAll({
withRelated: ['benefits']
});
for (const model of models) {
const tier = await Tier.create(this.mapToTier(model));
this.#store.push(tier);
this.#ids[tier.id.toHexString()] = true;
}
}
/**
* @param {import('@tryghost/tiers/lib/Tier')} tier
* @returns {any}
*/
toPrimitive(tier) {
return {
...tier,
active: (tier.status === 'active'),
type: tier.type,
id: tier.id.toHexString()
};
}
/**
* @private
*/
@ -54,16 +86,12 @@ module.exports = class TierRepository {
* @returns {Promise<import('@tryghost/tiers/lib/Tier')[]>}
*/
async getAll(options = {}) {
const collection = await this.#ProductModel.findAll({...options, withRelated: ['benefits']});
const result = [];
for (const model of collection.models) {
const tier = await Tier.create(this.mapToTier(model));
result.push(tier);
}
return result;
const filter = nql(options.filter, {});
return Promise.all(this.#store.slice().filter((item) => {
return filter.queryJSON(this.toPrimitive(item));
}).map((tier) => {
return Tier.create(tier);
}));
}
/**
@ -71,9 +99,15 @@ module.exports = class TierRepository {
* @returns {Promise<import('@tryghost/tiers/lib/Tier')>}
*/
async getById(id) {
const model = await this.#ProductModel.findOne({id: id.toHexString()}, {withRelated: ['benefits']});
const found = this.#store.find((item) => {
return item.id.equals(id);
});
return await Tier.create(this.mapToTier(model));
if (!found) {
return null;
}
return Tier.create(found);
}
/**
@ -99,14 +133,20 @@ module.exports = class TierRepository {
benefits: tier.benefits.map(name => ({name}))
};
const existing = await this.#ProductModel.findOne({id: data.id}, {require: false});
const toSave = await Tier.create(tier);
if (!existing) {
await this.#ProductModel.add(data);
} else {
if (this.#ids[tier.id.toHexString()]) {
const existing = this.#store.findIndex((item) => {
return item.id.equals(tier.id);
});
await this.#ProductModel.edit(data, {
id: data.id
});
this.#store.splice(existing, 1, toSave);
} else {
await this.#ProductModel.add(data);
this.#store.push(toSave);
this.#ids[tier.id.toHexString()] = true;
}
for (const event of tier.events) {

View file

@ -22,6 +22,10 @@ class TiersServiceWrapper {
}
};
await repository.init();
this.repository = repository;
this.api = new TiersAPI({
repository,
slugService

View file

@ -891,6 +891,10 @@ const getFixtureOps = (toDos) => {
}
});
fixtureOps.push(() => {
return require('../../core/server/services/tiers').repository?.init();
});
return fixtureOps;
};

View file

@ -1237,6 +1237,9 @@ DataGenerator.forKnex = (function () {
slug: 'gold',
active: true,
type: 'paid',
currency: 'usd',
monthly_price: 500,
yearly_price: 5000,
visibility: 'public',
benefits: [],
created_by: DataGenerator.Content.users[0].id,

View file

@ -92,12 +92,15 @@ class StripeMocker {
* @returns
*/
async createTier({name, currency, monthly_price, yearly_price}) {
return await models.Product.add({
const result = await tiers.api.add({
name: name ?? ('Tier ' + this.#generateRandomId()),
type: 'paid',
currency: currency.toUpperCase(),
monthly_price,
yearly_price
monthlyPrice: monthly_price,
yearlyPrice: yearly_price
});
return await models.Product.findOne({
id: result.id.toHexString()
});
}