Skip to content

Commit

Permalink
Add a hotcache class that we can use to cache the most common requests.
Browse files Browse the repository at this point in the history
We are using this to cache some of the most used apis - like the statistics.
This should reduce the CPU load in case of too many hits.
  • Loading branch information
slavcho committed Jul 1, 2023
1 parent 2c518b2 commit f91b436
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 1 deletion.
22 changes: 22 additions & 0 deletions apps/api/src/common/hotcache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class CacheObject {
constructor(public value: any, public expiration: number) {}

Check warning on line 2 in apps/api/src/common/hotcache.ts

View workflow job for this annotation

GitHub Actions / Run API tests

Unexpected any. Specify a different type
isExpired() {
return this.expiration < Date.now()
}
}

export class HotCache {
private cache: { [key: string]: CacheObject } = {}

get(key: string) {
const cacheObject = this.cache[key]
if (cacheObject && !cacheObject.isExpired()) {
return cacheObject.value
}
return null
}

set(key: string, value: any, expiration: number) {

Check warning on line 19 in apps/api/src/common/hotcache.ts

View workflow job for this annotation

GitHub Actions / Run API tests

Unexpected any. Specify a different type
this.cache[key] = new CacheObject(value, expiration)
}
}
33 changes: 32 additions & 1 deletion apps/api/src/donations/donations.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { donationWithPerson, DonationWithPerson } from './queries/donation.valid
import { CreateStripePaymentDto } from './dto/create-stripe-payment.dto'
import { ImportStatus } from '../bank-transactions-file/dto/bank-transactions-import-status.dto'
import { DonationQueryDto } from '../common/dto/donation-query-dto'
import { HotCache } from '../common/hotcache'

@Injectable()
export class DonationsService {
Expand All @@ -41,6 +42,9 @@ export class DonationsService {
private exportService: ExportService,
) {}

private cache = new HotCache()
private cacheTimeout = 30 * 1000 // 30 seconds

async listPrices(type?: Stripe.PriceListParams.Type, active?: boolean): Promise<Stripe.Price[]> {
const listResponse = await this.stripeClient.prices.list({ active, type, limit: 100 }).then(
function (list) {
Expand Down Expand Up @@ -286,6 +290,12 @@ export class DonationsService {
pageIndex?: number,
pageSize?: number,
): Promise<ListDonationsDto<DonationBaseDto>> {
const cacheKey = `donations-${campaignId}-${status}-${pageIndex}-${pageSize}`
const cachedDonations = this.cache.get(cacheKey)
if (cachedDonations) {
return cachedDonations
}

const data = await this.prisma.donation.findMany({
where: { status, targetVault: { campaign: { id: campaignId } } },
orderBy: [{ createdAt: 'desc' }],
Expand Down Expand Up @@ -314,6 +324,8 @@ export class DonationsService {
total: count,
}

this.cache.set(cacheKey, result, Date.now() + this.cacheTimeout)

return result
}

Expand Down Expand Up @@ -670,16 +682,30 @@ export class DonationsService {
}

async getTotalDonatedMoney() {
const cachedAmount = this.cache.get('totalDonatedMoney')
if (cachedAmount) {
return { total: cachedAmount }
}

const totalMoney = await this.prisma.donation.aggregate({
_sum: {
amount: true,
},
where: { status: DonationStatus.succeeded },
})

// cache the amount for 30 seconds
this.cache.set('totalDonatedMoney', totalMoney._sum.amount, Date.now() + this.cacheTimeout)

return { total: totalMoney._sum.amount }
}

async getDonorsCount() {
const cachedCount = this.cache.get('donorsCount')
if (cachedCount) {
return { count: cachedCount }
}

const donorsCount = await this.prisma.donation.groupBy({
by: ['billingName'],
where: { status: DonationStatus.succeeded },
Expand All @@ -692,8 +718,13 @@ export class DonationsService {
// get count of the donations with billingName == null
const anonymousDonations = donorsCount[0]._count._all

const totalCount = donorsCount.length - 1 + anonymousDonations

// cache the count for 30 seconds
this.cache.set('donorsCount', totalCount, Date.now() + this.cacheTimeout)

// substract one because we don't want to include anonymousDonation again
return { count: donorsCount.length - 1 + anonymousDonations }
return { count: totalCount }
}

/**
Expand Down

0 comments on commit f91b436

Please sign in to comment.