Add debounce.cancel, cancel session cleanup
This commit is contained in:
parent
2256fbc1e8
commit
22f0b15e3a
@ -6,6 +6,7 @@ import {ITransactionManager} from '../database/ITransactionManager'
|
||||
import {Session as SessionEntity} from '../entities/Session'
|
||||
import {SessionStore} from '../session/SessionStore'
|
||||
import {UrlWithStringQuery} from 'url'
|
||||
import {apiLogger} from '../logger'
|
||||
|
||||
export interface ISessionOptions {
|
||||
transactionManager: ITransactionManager,
|
||||
@ -32,7 +33,8 @@ export class SessionMiddleware implements IMiddleware {
|
||||
path: params.baseUrl.path,
|
||||
},
|
||||
store: new SessionStore({
|
||||
cleanup: 1,
|
||||
cleanupDelay: 60 * 1000,
|
||||
logger: apiLogger,
|
||||
getRepository: this.getRepository,
|
||||
ttl: 1,
|
||||
buildSession: this.buildSession,
|
||||
|
||||
@ -3,6 +3,7 @@ import request from 'supertest'
|
||||
import {SessionStore} from './SessionStore'
|
||||
import {ISession} from './ISession'
|
||||
import ExpressSession from 'express-session'
|
||||
import loggerFactory from '@rondo.dev/logger'
|
||||
import {
|
||||
createConnection, Column, Connection, Entity, Index, PrimaryColumn,
|
||||
Repository,
|
||||
@ -51,7 +52,8 @@ describe('SessionStore', () => {
|
||||
maxAge: 10,
|
||||
},
|
||||
store: new SessionStore({
|
||||
cleanup: 1,
|
||||
logger: loggerFactory.getLogger('api'),
|
||||
cleanupDelay: 60 * 1000,
|
||||
getRepository: () => repository,
|
||||
ttl: 1,
|
||||
buildSession: (sd, s) => ({...s, extraData: 'test'}),
|
||||
|
||||
@ -2,6 +2,7 @@ import {Store} from 'express-session'
|
||||
import {ISession} from './ISession'
|
||||
import {Repository, LessThan} from 'typeorm'
|
||||
import {debounce} from '@rondo.dev/tasq'
|
||||
import { ILogger } from '@rondo.dev/logger'
|
||||
|
||||
type SessionData = Express.SessionData
|
||||
type Callback = (err?: any, session?: SessionData) => void
|
||||
@ -9,8 +10,9 @@ type CallbackErr = (err?: any) => void
|
||||
|
||||
export interface ISessionStoreOptions<S extends ISession> {
|
||||
readonly ttl: number
|
||||
readonly cleanup: number
|
||||
readonly cleanupDelay: number
|
||||
readonly getRepository: TRepositoryFactory<S>
|
||||
readonly logger: ILogger,
|
||||
buildSession(sessionData: SessionData, session: ISession): S
|
||||
}
|
||||
|
||||
@ -24,25 +26,27 @@ export type TRepositoryFactory<T> = () => Repository<T>
|
||||
export class SessionStore<S extends ISession> extends Store {
|
||||
|
||||
protected readonly getRepository: TRepositoryFactory<S>
|
||||
protected readonly cleanup: (...args: never[]) => void
|
||||
|
||||
readonly cleanup = debounce(async () => {
|
||||
try {
|
||||
const now = Date.now()
|
||||
// this method is debounced because is caused deadlock errors in tests.
|
||||
// Be wary of future problems. Debounce should fix it but this still
|
||||
// needs to be thorughly tested. The problem is a the delete statement
|
||||
// which locks the whole table.
|
||||
await this.getRepository().delete({
|
||||
expiredAt: LessThan(now),
|
||||
} as any)
|
||||
} catch (err) {
|
||||
this.options.logger.error('Error cleaning sessions: %s', err.stack)
|
||||
}
|
||||
}, 1000)
|
||||
|
||||
constructor(
|
||||
protected readonly options: ISessionStoreOptions<S>,
|
||||
) {
|
||||
super()
|
||||
this.getRepository = options.getRepository
|
||||
|
||||
this.cleanup = debounce(async () => {
|
||||
try {
|
||||
const now = Date.now()
|
||||
// FIXME causes deadlocks in tests
|
||||
await this.getRepository().delete({
|
||||
expiredAt: LessThan(now),
|
||||
} as any)
|
||||
} catch (err) {
|
||||
console.log('error cleaning sessions', err)
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
protected async promiseToCallback<T>(
|
||||
@ -75,6 +79,8 @@ export class SessionStore<S extends ISession> extends Store {
|
||||
}
|
||||
|
||||
set = (sid: string, session: SessionData, callback?: CallbackErr) => {
|
||||
this.cleanup.cancel()
|
||||
|
||||
const promise = Promise.resolve()
|
||||
.then(() => this.saveSession(
|
||||
this.options.buildSession(session, {
|
||||
|
||||
@ -38,10 +38,13 @@ export class TestUtils<T extends IRoutes> {
|
||||
|
||||
let context: any
|
||||
|
||||
beforeAll(async () => {
|
||||
connection = await database.connect()
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
context = namespace.createContext();
|
||||
(namespace as any).enter(context)
|
||||
connection = await database.connect()
|
||||
queryRunner = connection.createQueryRunner()
|
||||
await queryRunner.connect()
|
||||
namespace.set(TRANSACTION_ID, shortid())
|
||||
@ -56,10 +59,13 @@ export class TestUtils<T extends IRoutes> {
|
||||
}
|
||||
await queryRunner.release()
|
||||
namespace.set(TRANSACTION_ID, undefined)
|
||||
namespace.set(ENTITY_MANAGER, undefined)
|
||||
await connection.close();
|
||||
namespace.set(ENTITY_MANAGER, undefined);
|
||||
(namespace as any).exit(context)
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await database.close()
|
||||
})
|
||||
}
|
||||
|
||||
async createRole(name: string) {
|
||||
|
||||
@ -1,13 +1,21 @@
|
||||
export function debounce<A, R>(fn: (...args: A[]) => R, delay: number) {
|
||||
let timeout: NodeJS.Timeout | null = null
|
||||
|
||||
return function debounceImpl(...args: A[]): void {
|
||||
const cancel = () => {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout)
|
||||
}
|
||||
}
|
||||
|
||||
function debounceImpl(...args: A[]): void {
|
||||
cancel()
|
||||
|
||||
timeout = setTimeout(() => {
|
||||
fn(...args)
|
||||
}, delay)
|
||||
}
|
||||
|
||||
debounceImpl.cancel = cancel
|
||||
|
||||
return debounceImpl
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user