diff --git a/CHANGELOG.md b/CHANGELOG.md index 1487311..af96871 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,10 @@ ## Unreleased + +### Changed +* Nodeinfo will only count users from database up to once per day, using a cached count for subsequent requests within 24 hours. +This can help limit query targeting warnings from mongo + +### Ops * Add [nock fetch work around](https://github.com/nock/nock/issues/2397) to fix tests in node 18. * Adjust workflow to run tests using node 18 and 16. Don't run tests against 14. diff --git a/pub/nodeinfo.js b/pub/nodeinfo.js index c659301..229f01b 100644 --- a/pub/nodeinfo.js +++ b/pub/nodeinfo.js @@ -1,4 +1,18 @@ 'use strict' + +const USER_COUNT_FREQ = 24 * 60 * 60 * 1000 +let lastUserCountTime = 0 +let lastUserCount = Promise.resolve(0) + +function getUserCountWithCache (store) { + const now = Date.now() + if (lastUserCountTime + USER_COUNT_FREQ < now) { + lastUserCountTime = now + lastUserCount = store.getUserCount() + } + return lastUserCount +} + module.exports = { async generateNodeInfo (version) { return { @@ -10,7 +24,7 @@ module.exports = { protocols: ['activitypub'], services: { inbound: [], outbound: [] }, openRegistrations: !!this.settings.openRegistrations, - usage: { users: { total: await this.store.getUserCount() } }, + usage: { users: { total: await getUserCountWithCache(this.store) } }, metadata: this.settings.nodeInfoMetadata || {} } } diff --git a/spec/functional/nodeinfo.spec.js b/spec/functional/nodeinfo.spec.js index f42bc54..ff7cc97 100644 --- a/spec/functional/nodeinfo.spec.js +++ b/spec/functional/nodeinfo.spec.js @@ -1,4 +1,4 @@ -/* global describe, beforeAll, beforeEach, it */ +/* global jasmine, describe, beforeAll, beforeEach, afterEach, it, spyOn, expect */ const request = require('supertest') describe('nodeinfo', function () { @@ -39,6 +39,12 @@ describe('nodeinfo', function () { }) }) describe('document get', function () { + beforeEach(function () { + jasmine.clock().install() + }) + afterEach(function () { + jasmine.clock().uninstall() + }) it('returns nodeinfo document', function (done) { request(app) .get('/nodeinfo/2.1') @@ -57,6 +63,21 @@ describe('nodeinfo', function () { } }, err => global.failOrDone(err, done)) }) + it('caches response for 1 day', async function () { + jasmine.clock().mockDate(new Date()) + const count = (await apex.generateNodeInfo('2.0')).usage.users.total + const user = await apex.createActor('newuser', 'New user') + await apex.store.saveObject(user) + const querySpy = spyOn(apex.store, 'getUserCount').and.callThrough() + expect((await apex.generateNodeInfo('2.0')).usage.users.total).toBe(count) + expect(querySpy).toHaveBeenCalledTimes(0) + jasmine.clock().mockDate(new Date(Date.now() + 23 * 60 * 60 * 1000)) + expect((await apex.generateNodeInfo('2.0')).usage.users.total).toBe(count) + expect(querySpy).toHaveBeenCalledTimes(0) + jasmine.clock().mockDate(new Date(Date.now() + 1 * 60 * 60 * 1000 + 1)) + expect((await apex.generateNodeInfo('2.0')).usage.users.total).toBe(count + 1) + expect(querySpy).toHaveBeenCalledTimes(1) + }) it('404s on 1.x nodeinfo requests', function (done) { request(app) .get('/nodeinfo/1.0')