Skip to content

Commit

Permalink
feat(stats): add useMaster scope to url_clicks model
Browse files Browse the repository at this point in the history
  • Loading branch information
yong-jie committed Apr 13, 2021
1 parent 3f7f559 commit 48b98cb
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 22 deletions.
41 changes: 30 additions & 11 deletions src/server/models/statistics/clicks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,36 @@ type UrlClicksTypeStatic = typeof Sequelize.Model & {
new (values?: object, options?: Sequelize.BuildOptions): UrlClicksType
}

export const UrlClicks = <UrlClicksTypeStatic>sequelize.define('url_clicks', {
shortUrl: {
type: Sequelize.STRING,
primaryKey: true,
validate: {
is: SHORT_URL_REGEX,
export const UrlClicks = <UrlClicksTypeStatic>sequelize.define(
'url_clicks',
{
shortUrl: {
type: Sequelize.STRING,
primaryKey: true,
validate: {
is: SHORT_URL_REGEX,
},
},
clicks: {
type: Sequelize.INTEGER,
defaultValue: 0,
allowNull: false,
},
},
clicks: {
type: Sequelize.INTEGER,
defaultValue: 0,
allowNull: false,
{
defaultScope: {
useMaster: true,
},
scopes: {
/**
* Use the master database for read queries. To be enabled
* when realtime data is needed.
*/
useMasterDb() {
return {
useMaster: true,
}
},
},
},
})
)
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,21 @@ export class StatisticsRepository implements interfaces.StatisticsRepository {
)

if (userCount == null) {
userCount = await User.scope('useMasterDb').count()
// Allow use of read replica
userCount = await User.unscoped().count()
this.trySetCache(USER_COUNT_KEY, userCount.toString())
}

if (clickCount == null) {
// Replace Nan with 0 if there is no data
clickCount = (await UrlClicks.sum('clicks')) || 0
// Allow use of read replica
clickCount = (await UrlClicks.unscoped().sum('clicks')) || 0
this.trySetCache(CLICK_COUNT_KEY, clickCount.toString())
}

if (linkCount == null) {
linkCount = await Url.scope('useMasterDb').count()
// Allow use of read replica
linkCount = await Url.unscoped().count()
this.trySetCache(LINK_COUNT_KEY, linkCount.toString())
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,13 @@ describe('StatisticsRepository', () => {
const userCount = 1
const clickCount = 3
const linkCount = 2
const { scope } = userModelMock

beforeEach(async () => {
await new Promise<void>((resolve) => {
redisMockClient.flushall(() => resolve())
})
setSpy.mockClear()
mgetSpy.mockClear()
scope.mockReset()
})

afterEach(async () => {
Expand Down Expand Up @@ -104,12 +102,14 @@ describe('StatisticsRepository', () => {

// @ts-ignore
mgetSpy.mockImplementation(raiseError)
scope.mockReturnValue({ count: () => userCount })
Object.assign(userModelMock, {
unscoped: () => ({ count: () => userCount }),
})
Object.assign(urlModelMock, {
count: () => linkCount,
unscoped: () => ({ count: () => linkCount }),
})
Object.assign(urlClicksModelMock, {
sum: () => clickCount,
unscoped: () => ({ sum: () => clickCount }),
})

await expect(repository.getGlobalStatistics()).resolves.toStrictEqual({
Expand All @@ -119,7 +119,6 @@ describe('StatisticsRepository', () => {
})
expect(mgetSpy).toHaveBeenCalled()
expect(setSpy).toHaveBeenCalledTimes(3)
expect(scope).toBeCalledWith('useMasterDb')
})

it('returns values from Sequelize even if Redis set fails', async () => {
Expand All @@ -133,7 +132,9 @@ describe('StatisticsRepository', () => {
callback(new Error('error'))
}

scope.mockReturnValue({ count: () => userCount })
Object.assign(urlModelMock, {
unscoped: () => ({ count: () => linkCount }),
})
Object.assign(urlModelMock, {
count: () => linkCount,
})
Expand All @@ -151,6 +152,5 @@ describe('StatisticsRepository', () => {
})
expect(mgetSpy).toHaveBeenCalled()
expect(setSpy).toHaveBeenCalledTimes(3)
expect(scope).toHaveBeenCalledWith('useMasterDb')
})
})

0 comments on commit 48b98cb

Please sign in to comment.