fix: lean of autopopulate

Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
Innei
2023-04-25 00:46:55 +08:00
parent 9fa805a5d6
commit cb72911b18
18 changed files with 174 additions and 87 deletions

View File

@@ -23,6 +23,7 @@ import { OptionModel } from '~/modules/configs/configs.model'
import { CacheService } from '~/processors/redis/cache.service'
import { getNestExecutionContextRequest } from '~/transformers/get-req.transformer'
import { InjectModel } from '~/transformers/model.transformer'
import { scheduleManager } from '~/utils'
import { getIp } from '~/utils/ip.util'
import { getRedisKey } from '~/utils/redis.util'
@@ -80,7 +81,7 @@ export class AnalyzeInterceptor implements NestInterceptor {
return call$
}
process.nextTick(async () => {
scheduleManager.schedule(async () => {
try {
request.headers['user-agent'] &&
this.parser.setUA(request.headers['user-agent'])

View File

@@ -33,6 +33,7 @@ import { EventManagerService } from '~/processors/helper/helper.event.service'
import { MongoIdDto } from '~/shared/dto/id.dto'
import { PagerDto } from '~/shared/dto/pager.dto'
import { transformDataToPaginate } from '~/transformers/paginate.transformer'
import { scheduleManager } from '~/utils'
import { ConfigsService } from '../configs/configs.service'
import { UserModel } from '../user/user.model'
@@ -196,14 +197,14 @@ export class CommentController {
const comment = await this.commentService.createComment(id, model, ref)
const commentId = comment._id.toString()
process.nextTick(async () => {
scheduleManager.batch(async () => {
if (isMaster) {
return
}
await this.commentService.appendIpLocation(commentId, ipLocation.ip)
})
process.nextTick(async () => {
scheduleManager.batch(async () => {
const configs = await this.configsService.get('commentOptions')
const { commentShouldAudit } = configs
if (await this.commentService.checkSpam(comment)) {
@@ -293,7 +294,7 @@ export class CommentController {
const comment = await this.commentService.model.create(model)
const commentId = comment._id.toString()
process.nextTick(async () => {
scheduleManager.schedule(async () => {
if (isMaster) {
return
}

View File

@@ -22,6 +22,7 @@ import {
BaseCrudFactory,
BaseCrudModuleType,
} from '~/transformers/crud-factor.transformer'
import { scheduleManager } from '~/utils'
import { AuditReasonDto, LinkDto } from './link.dto'
import { LinkModel, LinkState } from './link.model'
@@ -97,7 +98,7 @@ export class LinkController {
throw new ForbiddenException('主人目前不允许申请友链了!')
}
await this.linkService.applyForLink(body)
process.nextTick(async () => {
scheduleManager.schedule(async () => {
await this.linkService.sendToMaster(body.author, body)
})
@@ -109,7 +110,7 @@ export class LinkController {
async approveLink(@Param('id') id: string) {
const doc = await this.linkService.approveLink(id)
process.nextTick(async () => {
scheduleManager.schedule(async () => {
if (doc.email) {
await this.linkService.sendToCandidate(doc)
}

View File

@@ -14,6 +14,7 @@ import {
import { EventManagerService } from '~/processors/helper/helper.event.service'
import { HttpService } from '~/processors/helper/helper.http.service'
import { InjectModel } from '~/transformers/model.transformer'
import { scheduleManager } from '~/utils'
import { ConfigsService } from '../configs/configs.service'
import { LinkModel, LinkState, LinkStateMap, LinkType } from './link.model'
@@ -71,7 +72,7 @@ export class LinkService {
})
}
process.nextTick(() => {
scheduleManager.schedule(() => {
this.eventManager.broadcast(BusinessEvents.LINK_APPLY, nextModel, {
scope: EventScope.TO_SYSTEM_ADMIN,
})
@@ -156,7 +157,7 @@ export class LinkService {
站点描述:${model.description}`)
return
}
process.nextTick(async () => {
scheduleManager.schedule(async () => {
const master = await this.configs.getMaster()
await this.sendLinkApplyEmail({

View File

@@ -233,7 +233,7 @@ export class NoteController {
...condition,
})
.select(`+password ${isMaster ? '+location +coordinates' : ''}`)
.lean({ getters: true })
.lean({ getters: true, autopopulate: true })
if (!current) {
throw new CannotFindException()
}

View File

@@ -8,20 +8,11 @@ import {
IsString,
ValidateNested,
} from 'class-validator'
import { Query } from 'mongoose'
import mongooseAutoPopulate from 'mongoose-autopopulate'
import { PartialType } from '@nestjs/mapped-types'
import { AutoIncrementID } from '@typegoose/auto-increment'
import {
DocumentType,
Ref,
index,
modelOptions,
plugin,
prop,
} from '@typegoose/typegoose'
import { BeAnObject } from '@typegoose/typegoose/lib/types'
import { Ref, index, modelOptions, plugin, prop } from '@typegoose/typegoose'
import { CountModel } from '~/shared/model/count.model'
import { WriteBaseModel } from '~/shared/model/write-base.model'
@@ -127,16 +118,3 @@ export class NoteModel extends WriteBaseModel {
}
export class PartialNoteModel extends PartialType(NoteModel) {}
function autoPopulateTopic(
this: Query<
any,
DocumentType<NoteModel, BeAnObject>,
{},
DocumentType<NoteModel, BeAnObject>
>,
next: () => void,
) {
this.populate({ path: 'topic' })
next()
}

View File

@@ -14,7 +14,7 @@ import { EventManagerService } from '~/processors/helper/helper.event.service'
import { ImageService } from '~/processors/helper/helper.image.service'
import { TextMacroService } from '~/processors/helper/helper.macro.service'
import { InjectModel } from '~/transformers/model.transformer'
import { getLessThanNow } from '~/utils'
import { getLessThanNow, scheduleManager } from '~/utils'
import { CommentRefTypes } from '../comment/comment.model'
import { CommentService } from '../comment/comment.service'
@@ -62,7 +62,10 @@ export class NoteService {
.sort({
created: -1,
})
.lean()
.lean({
getters: true,
autopopulate: true,
})
if (!latest) {
throw new CannotFindException()
@@ -113,7 +116,7 @@ export class NoteService {
document.created = getLessThanNow(document.created)
const note = await this.noteModel.create(document)
process.nextTick(async () => {
scheduleManager.schedule(async () => {
await Promise.all([
this.eventManager.emit(EventBusEvents.CleanAggregateCache, null, {
scope: EventScope.TO_SYSTEM,
@@ -175,13 +178,14 @@ export class NoteService {
)
.lean({
getters: true,
autopopulate: true,
})
if (!updated) {
throw new NoContentCanBeModifiedException()
}
process.nextTick(async () => {
scheduleManager.batch(async () => {
this.eventManager.emit(EventBusEvents.CleanAggregateCache, null, {
scope: EventScope.TO_SYSTEM,
})
@@ -204,35 +208,32 @@ export class NoteService {
.exec()
},
),
this.model
.findById(id)
.lean()
.then(async (doc) => {
if (!doc) {
return
}
async () => {
if (!updated) {
return
}
this.eventManager.broadcast(BusinessEvents.NOTE_UPDATE, doc, {
scope: EventScope.TO_SYSTEM,
})
this.eventManager.broadcast(BusinessEvents.NOTE_UPDATE, updated, {
scope: EventScope.TO_SYSTEM,
})
if (doc.password || doc.hide || doc.secret) {
return
}
this.eventManager.broadcast(
BusinessEvents.NOTE_UPDATE,
{
...doc,
text: await this.textMacrosService.replaceTextMacro(
doc.text,
doc,
),
},
{
scope: EventScope.TO_VISITOR,
},
)
}),
if (updated.password || updated.hide || updated.secret) {
return
}
this.eventManager.broadcast(
BusinessEvents.NOTE_UPDATE,
{
...updated,
text: await this.textMacrosService.replaceTextMacro(
updated.text,
updated,
),
},
{
scope: EventScope.TO_VISITOR,
},
)
},
])
})
return updated
@@ -253,7 +254,7 @@ export class NoteService {
refType: CommentRefTypes.Note,
}),
])
process.nextTick(async () => {
scheduleManager.schedule(async () => {
await Promise.all([
this.eventManager.broadcast(BusinessEvents.NOTE_DELETE, id, {
scope: EventScope.TO_SYSTEM_VISITOR,

View File

@@ -10,6 +10,7 @@ import { EventManagerService } from '~/processors/helper/helper.event.service'
import { ImageService } from '~/processors/helper/helper.image.service'
import { TextMacroService } from '~/processors/helper/helper.macro.service'
import { InjectModel } from '~/transformers/model.transformer'
import { scheduleManager } from '~/utils'
import { PageModel } from './page.model'
@@ -66,7 +67,7 @@ export class PageService {
throw new NoContentCanBeModifiedException()
}
process.nextTick(async () => {
scheduleManager.schedule(async () => {
await Promise.all([
this.imageService.saveImageDimensionsFromMarkdownText(
newDoc.text,

View File

@@ -145,7 +145,7 @@ export class PostController {
const last = await this.postService.model
.findOne({})
.sort({ created: -1 })
.lean({ getters: true })
.lean({ getters: true, autopopulate: true })
if (!last) {
throw new CannotFindException()
}

View File

@@ -18,7 +18,7 @@ import { EventManagerService } from '~/processors/helper/helper.event.service'
import { ImageService } from '~/processors/helper/helper.image.service'
import { TextMacroService } from '~/processors/helper/helper.macro.service'
import { InjectModel } from '~/transformers/model.transformer'
import { getLessThanNow } from '~/utils'
import { getLessThanNow, scheduleManager } from '~/utils'
import { CategoryService } from '../category/category.service'
import { CommentModel, CommentRefTypes } from '../comment/comment.model'
@@ -74,7 +74,7 @@ export class PostService {
const doc = res.toJSON()
process.nextTick(async () => {
scheduleManager.schedule(async () => {
await Promise.all([
this.imageService.saveImageDimensionsFromMarkdownText(
doc.text,
@@ -160,8 +160,10 @@ export class PostService {
)
await oldDocument.save()
process.nextTick(async () => {
const doc = await this.postModel.findById(id).lean({ getters: true })
scheduleManager.batch(async () => {
const doc = await this.postModel
.findById(id)
.lean({ getters: true, autopopulate: true })
// 更新图片信息缓存
await Promise.all([
this.eventManager.emit(EventBusEvents.CleanAggregateCache, null, {

View File

@@ -16,7 +16,7 @@ import { DatabaseService } from '~/processors/database/database.service'
import { EventManagerService } from '~/processors/helper/helper.event.service'
import { CacheService } from '~/processors/redis/cache.service'
import { InjectModel } from '~/transformers/model.transformer'
import { getRedisKey } from '~/utils'
import { getRedisKey, scheduleManager } from '~/utils'
import { CommentRefTypes } from '../comment/comment.model'
import { CommentService } from '../comment/comment.service'
@@ -232,7 +232,7 @@ export class RecentlyService {
},
])
.lean()
process.nextTick(async () => {
scheduleManager.schedule(async () => {
await this.eventManager.broadcast(
BusinessEvents.RECENTLY_CREATE,
withRef,
@@ -256,7 +256,7 @@ export class RecentlyService {
}),
])
const isDeleted = deletedCount === 1
process.nextTick(async () => {
scheduleManager.schedule(async () => {
if (isDeleted) {
await this.eventManager.broadcast(BusinessEvents.RECENTLY_DElETE, id, {
scope: EventScope.TO_SYSTEM_VISITOR,

View File

@@ -28,7 +28,12 @@ import { HttpService } from '~/processors/helper/helper.http.service'
import { CacheService } from '~/processors/redis/cache.service'
import { InjectModel } from '~/transformers/model.transformer'
import { UniqueArray } from '~/types/unique'
import { deepCloneWithFunction, getRedisKey, safePathJoin } from '~/utils'
import {
deepCloneWithFunction,
getRedisKey,
safePathJoin,
scheduleManager,
} from '~/utils'
import { EncryptUtil } from '~/utils/encrypt.util'
import { safeEval } from '~/utils/safe-eval.util'
import { isBuiltinModule } from '~/utils/system.util'
@@ -622,7 +627,7 @@ export class ServerlessService implements OnModuleInit {
process: {
env: Object.freeze({ ...process.env }),
nextTick: process.nextTick.bind(null),
nextTick: scheduleManager.schedule.bind(null),
},
}

View File

@@ -17,6 +17,7 @@ import {
import { BusinessEvents } from '~/constants/business-event.constant'
import { RedisKeys } from '~/constants/cache.constant'
import { CacheService } from '~/processors/redis/cache.service'
import { scheduleManager } from '~/utils'
import { getRedisKey } from '~/utils/redis.util'
import { getShortDate } from '~/utils/time.util'
@@ -67,7 +68,7 @@ export class WebEventsGateway
async handleConnection(socket: SocketIO.Socket) {
this.broadcast(BusinessEvents.VISITOR_ONLINE, await this.sendOnlineNumber())
process.nextTick(async () => {
scheduleManager.schedule(async () => {
const redisClient = this.cacheService.getClient()
const dateFormat = getShortDate(new Date())

View File

@@ -22,6 +22,7 @@ import { PageService } from '~/modules/page/page.service'
import { PostService } from '~/modules/post/post.service'
import { SearchService } from '~/modules/search/search.service'
import { InjectModel } from '~/transformers/model.transformer'
import { scheduleManager } from '~/utils'
import { uploadFileToCOS } from '~/utils/cos.util'
import { getRedisKey } from '~/utils/redis.util'
@@ -70,7 +71,7 @@ export class CronService {
return
}
// 开始上传 COS
process.nextTick(async () => {
scheduleManager.schedule(async () => {
const { backupOptions } = await this.configs.waitForConfigReady()
if (

View File

@@ -5,6 +5,7 @@ import { EventEmitter2 } from '@nestjs/event-emitter'
import { BusinessEvents, EventScope } from '~/constants/business-event.constant'
import { EventBusEvents } from '~/constants/event-bus.constant'
import { scheduleManager } from '~/utils'
import { AdminEventsGateway } from '../gateway/admin/events.gateway'
import { BoardcastBaseGateway } from '../gateway/base.gateway'
@@ -115,7 +116,7 @@ export class EventManagerService {
)
if (nextTick) {
process.nextTick(async () => await tasks)
scheduleManager.schedule(async () => await tasks)
} else {
await tasks
}

View File

@@ -7,3 +7,4 @@ export * from './system.util'
export * from './time.util'
export * from './tool.util'
export * from './biz.util'
export * from './schedule.util'

100
src/utils/schedule.util.ts Normal file
View File

@@ -0,0 +1,100 @@
import { sleep } from 'zx-cjs'
export function scheduleMicrotask(callback: () => void) {
sleep(0).then(callback)
}
// TYPES
type NotifyCallback = () => void
type NotifyFunction = (callback: () => void) => void
type BatchNotifyFunction = (callback: () => void) => void
export function createNotifyManager() {
let queue: NotifyCallback[] = []
let transactions = 0
let notifyFn: NotifyFunction = (callback) => {
callback()
}
let batchNotifyFn: BatchNotifyFunction = (callback: () => void) => {
callback()
}
const batch = <T>(callback: () => T): T => {
let result
transactions++
try {
result = callback()
} finally {
transactions--
if (!transactions) {
flush()
}
}
return result
}
const schedule = (callback: NotifyCallback): void => {
if (transactions) {
queue.push(callback)
} else {
scheduleMicrotask(() => {
notifyFn(callback)
})
}
}
/**
* All calls to the wrapped function will be batched.
*/
const batchCalls = <T extends Function>(callback: T): T => {
return ((...args: any[]) => {
schedule(() => {
callback(...args)
})
}) as any
}
const flush = (): void => {
const originalQueue = queue
queue = []
if (originalQueue.length) {
scheduleMicrotask(() => {
batchNotifyFn(() => {
originalQueue.forEach((callback) => {
notifyFn(callback)
})
})
})
}
}
/**
* Use this method to set a custom notify function.
* This can be used to for example wrap notifications with `React.act` while running tests.
*/
const setNotifyFunction = (fn: NotifyFunction) => {
notifyFn = fn
}
/**
* Use this method to set a custom function to batch notifications together into a single tick.
* By default React Query will use the batch function provided by ReactDOM or React Native.
*/
const setBatchNotifyFunction = (fn: BatchNotifyFunction) => {
batchNotifyFn = fn
}
return {
batch,
batchCalls,
schedule,
setNotifyFunction,
setBatchNotifyFunction,
} as const
}
// SINGLETON
export const scheduleManager = createNotifyManager()

View File

@@ -17,11 +17,9 @@ exports[`NoteController (e2e) > GET /latest 1`] = `
"nid": 20,
"text": "Content 20",
"title": "Note 20",
"topic": null,
},
"next": {
"nid": 19,
"topic": null,
},
}
`;
@@ -33,31 +31,26 @@ exports[`NoteController (e2e) > GET /list/:id 1`] = `
"created": "2023-01-17T11:01:57.851Z",
"nid": 21,
"title": "Note 2 (updated)",
"topic": null,
},
{
"created": "2021-03-20T00:00:00.000Z",
"nid": 20,
"title": "Note 20",
"topic": null,
},
{
"created": "2021-03-19T00:00:00.000Z",
"nid": 19,
"title": "Note 19",
"topic": null,
},
{
"created": "2021-03-18T00:00:00.000Z",
"nid": 18,
"title": "Note 18",
"topic": null,
},
{
"created": "2021-03-17T00:00:00.000Z",
"nid": 17,
"title": "Note 17",
"topic": null,
},
],
"size": 5,
@@ -286,7 +279,6 @@ exports[`NoteController (e2e) > GET /notes/nid/:nid 1`] = `
"modified": null,
"nid": 20,
"title": "Note 20",
"topic": null,
},
"prev": null,
}
@@ -309,7 +301,6 @@ exports[`NoteController (e2e) > Get patched note 1`] = `
"nid": 21,
"text": "Content 2 (updated)",
"title": "Note 2 (updated)",
"topic": null,
"weather": "sunny",
}
`;
@@ -331,5 +322,6 @@ exports[`NoteController (e2e) > POST /notes 1`] = `
"nid": 21,
"text": "Content 2",
"title": "Note 2",
"topic": null,
}
`;