mirror of
https://github.com/Paris-est-Ludique/intranet.git
synced 2025-06-09 09:04:20 +02:00
Fix gsheet api request saturation caused by an infinite delayed loop
This commit is contained in:
parent
d8e7bb9f70
commit
968ec3b430
@ -11,8 +11,9 @@ export { SheetNames } from "./localDb"
|
|||||||
|
|
||||||
const CRED_PATH = path.resolve(process.cwd(), "access/gsheets.json")
|
const CRED_PATH = path.resolve(process.cwd(), "access/gsheets.json")
|
||||||
|
|
||||||
const REMOTE_UPDATE_DELAY = 80000
|
const REMOTE_UPDATE_DELAY = 40000
|
||||||
const DELAY_BETWEEN_ATTEMPTS = 10000
|
const DELAY_BETWEEN_ATTEMPTS = 10000
|
||||||
|
const DELAY_BETWEEN_FIRST_LOAD = 1500
|
||||||
|
|
||||||
let creds: string | undefined | null
|
let creds: string | undefined | null
|
||||||
|
|
||||||
@ -44,7 +45,7 @@ export async function checkGSheetsAccess(): Promise<void> {
|
|||||||
console.error(`Google Sheets: no creds found, loading local database instead`)
|
console.error(`Google Sheets: no creds found, loading local database instead`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export function getSheet<
|
export async function getSheet<
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
ElementNoId extends object,
|
ElementNoId extends object,
|
||||||
Element extends ElementNoId & ElementWithId<ElementNoId>
|
Element extends ElementNoId & ElementWithId<ElementNoId>
|
||||||
@ -52,18 +53,17 @@ export function getSheet<
|
|||||||
sheetName: keyof SheetNames,
|
sheetName: keyof SheetNames,
|
||||||
specimen: Element,
|
specimen: Element,
|
||||||
translation: { [k in keyof Element]: string }
|
translation: { [k in keyof Element]: string }
|
||||||
): Sheet<ElementNoId, Element> {
|
): Promise<Sheet<ElementNoId, Element>> {
|
||||||
|
let sheet: Sheet<ElementNoId, Element>
|
||||||
if (!sheetList[sheetName]) {
|
if (!sheetList[sheetName]) {
|
||||||
sheetList[sheetName] = new Sheet<ElementNoId, Element>(sheetName, specimen, translation)
|
sheet = new Sheet<ElementNoId, Element>(sheetName, specimen, translation)
|
||||||
|
await sheet.waitForFirstLoad()
|
||||||
|
sheetList[sheetName] = sheet
|
||||||
|
setInterval(() => sheet.dbUpdate(), REMOTE_UPDATE_DELAY)
|
||||||
|
} else {
|
||||||
|
sheet = sheetList[sheetName] as Sheet<ElementNoId, Element>
|
||||||
}
|
}
|
||||||
|
|
||||||
const sheet = sheetList[sheetName] as Sheet<ElementNoId, Element>
|
|
||||||
|
|
||||||
setTimeout(
|
|
||||||
() => setInterval(() => sheet.dbUpdate(), REMOTE_UPDATE_DELAY),
|
|
||||||
1000 * Object.values(sheetList).length
|
|
||||||
)
|
|
||||||
|
|
||||||
return sheet
|
return sheet
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,8 +100,6 @@ export class Sheet<
|
|||||||
_.invert(translation),
|
_.invert(translation),
|
||||||
(englishProp: string) => (specimen as any)[englishProp]
|
(englishProp: string) => (specimen as any)[englishProp]
|
||||||
) as Element
|
) as Element
|
||||||
|
|
||||||
setTimeout(() => this.dbFirstLoad(), 100 * Object.values(sheetList).length)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getList(): Promise<Element[] | undefined> {
|
async getList(): Promise<Element[] | undefined> {
|
||||||
@ -154,6 +152,15 @@ export class Sheet<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async waitForFirstLoad(): Promise<void> {
|
||||||
|
setTimeout(
|
||||||
|
() => this.dbFirstLoad(),
|
||||||
|
DELAY_BETWEEN_FIRST_LOAD * Object.values(sheetList).length
|
||||||
|
)
|
||||||
|
|
||||||
|
await this.waitForLoad()
|
||||||
|
}
|
||||||
|
|
||||||
private async waitForLoad(): Promise<void> {
|
private async waitForLoad(): Promise<void> {
|
||||||
return new Promise((resolve, _reject) => {
|
return new Promise((resolve, _reject) => {
|
||||||
this.addToRunAfterLoad(() => resolve(undefined))
|
this.addToRunAfterLoad(() => resolve(undefined))
|
||||||
@ -194,10 +201,9 @@ export class Sheet<
|
|||||||
async dbLoad(): Promise<void> {
|
async dbLoad(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
if (await hasGSheetsAccess()) {
|
if (await hasGSheetsAccess()) {
|
||||||
this.dbLoadAsync().then(() => this.doRunAfterLoad())
|
await this.dbLoadAsync()
|
||||||
} else {
|
|
||||||
this.doRunAfterLoad()
|
|
||||||
}
|
}
|
||||||
|
this.doRunAfterLoad()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Error in dbLoad: ", e)
|
console.error("Error in dbLoad: ", e)
|
||||||
}
|
}
|
||||||
@ -214,6 +220,7 @@ export class Sheet<
|
|||||||
if (!(await hasGSheetsAccess())) {
|
if (!(await hasGSheetsAccess())) {
|
||||||
await this.loadLocalDb()
|
await this.loadLocalDb()
|
||||||
} else if (this.toRunAfterLoad && __DEV__) {
|
} else if (this.toRunAfterLoad && __DEV__) {
|
||||||
|
// Save once
|
||||||
this.toRunAfterLoad.push(() => this.saveLocalDb())
|
this.toRunAfterLoad.push(() => this.saveLocalDb())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,7 +295,7 @@ export class Sheet<
|
|||||||
|
|
||||||
private async dbLoadAsync(): Promise<void> {
|
private async dbLoadAsync(): Promise<void> {
|
||||||
type StringifiedElement = Record<keyof Element, string>
|
type StringifiedElement = Record<keyof Element, string>
|
||||||
const sheet = await this.getGSheet()
|
const sheet = await this.getGSheet(20)
|
||||||
|
|
||||||
if (!sheet) {
|
if (!sheet) {
|
||||||
return
|
return
|
||||||
@ -326,7 +333,7 @@ export class Sheet<
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getGSheet(): Promise<GoogleSpreadsheetWorksheet | null> {
|
private async getGSheet(attempts = 3): Promise<GoogleSpreadsheetWorksheet | null> {
|
||||||
return tryNTimes(
|
return tryNTimes(
|
||||||
async () => {
|
async () => {
|
||||||
if (creds === undefined) {
|
if (creds === undefined) {
|
||||||
@ -347,7 +354,7 @@ export class Sheet<
|
|||||||
return doc.sheetsByTitle[this.sheetName]
|
return doc.sheetsByTitle[this.sheetName]
|
||||||
},
|
},
|
||||||
() => null,
|
() => null,
|
||||||
20,
|
attempts,
|
||||||
DELAY_BETWEEN_ATTEMPTS / 5
|
DELAY_BETWEEN_ATTEMPTS / 5
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -590,7 +597,7 @@ function parseDate(value: string): Date {
|
|||||||
async function tryNTimes<T>(
|
async function tryNTimes<T>(
|
||||||
func: () => Promise<T> | T,
|
func: () => Promise<T> | T,
|
||||||
failFunc?: () => Promise<T> | T,
|
failFunc?: () => Promise<T> | T,
|
||||||
repeatCount = 5,
|
repeatCount = 2,
|
||||||
delayBetweenAttempts = DELAY_BETWEEN_ATTEMPTS
|
delayBetweenAttempts = DELAY_BETWEEN_ATTEMPTS
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
try {
|
try {
|
||||||
@ -601,7 +608,7 @@ async function tryNTimes<T>(
|
|||||||
await new Promise<void>((resolve) => {
|
await new Promise<void>((resolve) => {
|
||||||
setTimeout(() => resolve(), delayBetweenAttempts)
|
setTimeout(() => resolve(), delayBetweenAttempts)
|
||||||
})
|
})
|
||||||
if (repeatCount === 1) {
|
if (repeatCount <= 1) {
|
||||||
console.error(`No more attempts left every ${delayBetweenAttempts}`)
|
console.error(`No more attempts left every ${delayBetweenAttempts}`)
|
||||||
if (failFunc) {
|
if (failFunc) {
|
||||||
return failFunc()
|
return failFunc()
|
||||||
@ -614,7 +621,7 @@ async function tryNTimes<T>(
|
|||||||
|
|
||||||
async function tryNTimesVoidReturn(
|
async function tryNTimesVoidReturn(
|
||||||
func: () => Promise<void> | void,
|
func: () => Promise<void> | void,
|
||||||
repeatCount = 5,
|
repeatCount = 2,
|
||||||
delayBetweenAttempts = DELAY_BETWEEN_ATTEMPTS
|
delayBetweenAttempts = DELAY_BETWEEN_ATTEMPTS
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
return tryNTimes(func, () => undefined, repeatCount, delayBetweenAttempts)
|
return tryNTimes(func, () => undefined, repeatCount, delayBetweenAttempts)
|
||||||
|
@ -9,14 +9,33 @@ export default class ExpressAccessors<
|
|||||||
ElementNoId extends object,
|
ElementNoId extends object,
|
||||||
Element extends ElementWithId<ElementNoId>
|
Element extends ElementWithId<ElementNoId>
|
||||||
> {
|
> {
|
||||||
sheet: Sheet<ElementNoId, Element>
|
sheet?: Sheet<ElementNoId, Element>
|
||||||
|
|
||||||
|
runAfterLoad: ((value: Sheet<ElementNoId, Element>) => void)[] = []
|
||||||
|
|
||||||
|
isLoaded = false
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly sheetName: keyof SheetNames,
|
readonly sheetName: keyof SheetNames,
|
||||||
readonly specimen: Element,
|
readonly specimen: Element,
|
||||||
readonly translation: { [k in keyof Element]: string }
|
readonly translation: { [k in keyof Element]: string }
|
||||||
) {
|
) {
|
||||||
this.sheet = getSheet<ElementNoId, Element>(sheetName, specimen, translation)
|
getSheet<ElementNoId, Element>(this.sheetName, this.specimen, this.translation).then(
|
||||||
|
(sheet) => {
|
||||||
|
this.sheet = sheet
|
||||||
|
this.isLoaded = true
|
||||||
|
this.runAfterLoad.map((f) => f(sheet))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getSheet(): Promise<Sheet<ElementNoId, Element>> {
|
||||||
|
if (!this.isLoaded) {
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
this.runAfterLoad.push(resolve)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return this.sheet as Sheet<ElementNoId, Element>
|
||||||
}
|
}
|
||||||
|
|
||||||
listGet() {
|
listGet() {
|
||||||
@ -26,7 +45,7 @@ export default class ExpressAccessors<
|
|||||||
_next: NextFunction
|
_next: NextFunction
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const elements = await this.sheet.getList()
|
const elements = await (await this.getSheet()).getList()
|
||||||
if (elements) {
|
if (elements) {
|
||||||
response.status(200).json(elements)
|
response.status(200).json(elements)
|
||||||
}
|
}
|
||||||
@ -42,7 +61,8 @@ export default class ExpressAccessors<
|
|||||||
) {
|
) {
|
||||||
return async (request: Request, response: Response, _next: NextFunction): Promise<void> => {
|
return async (request: Request, response: Response, _next: NextFunction): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const list = (await this.sheet.getList()) || []
|
const sheet = await this.getSheet()
|
||||||
|
const list = (await sheet.getList()) || []
|
||||||
let toCaller: any
|
let toCaller: any
|
||||||
if (!custom) {
|
if (!custom) {
|
||||||
const id = parseInt(request.query.id as string, 10) || -1
|
const id = parseInt(request.query.id as string, 10) || -1
|
||||||
@ -65,7 +85,8 @@ export default class ExpressAccessors<
|
|||||||
add() {
|
add() {
|
||||||
return async (request: Request, response: Response, _next: NextFunction): Promise<void> => {
|
return async (request: Request, response: Response, _next: NextFunction): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const element: Element = await this.sheet.add(request.body)
|
const sheet = await this.getSheet()
|
||||||
|
const element: Element = await sheet.add(request.body)
|
||||||
if (element) {
|
if (element) {
|
||||||
response.status(200).json(element)
|
response.status(200).json(element)
|
||||||
}
|
}
|
||||||
@ -85,15 +106,16 @@ export default class ExpressAccessors<
|
|||||||
) {
|
) {
|
||||||
return async (request: Request, response: Response, _next: NextFunction): Promise<void> => {
|
return async (request: Request, response: Response, _next: NextFunction): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
|
const sheet = await this.getSheet()
|
||||||
if (!custom) {
|
if (!custom) {
|
||||||
await this.sheet.set(request.body)
|
await sheet.set(request.body)
|
||||||
response.status(200)
|
response.status(200)
|
||||||
} else {
|
} else {
|
||||||
const memberId = response?.locals?.jwt?.id || -1
|
const memberId = response?.locals?.jwt?.id || -1
|
||||||
const list = (await this.sheet.getList()) || []
|
const list = (await sheet.getList()) || []
|
||||||
const { toDatabase, toCaller } = await custom(list, request.body, memberId)
|
const { toDatabase, toCaller } = await custom(list, request.body, memberId)
|
||||||
if (toDatabase !== undefined) {
|
if (toDatabase !== undefined) {
|
||||||
await this.sheet.set(toDatabase)
|
await sheet.set(toDatabase)
|
||||||
}
|
}
|
||||||
if (toCaller !== undefined) {
|
if (toCaller !== undefined) {
|
||||||
response.status(200).json(toCaller)
|
response.status(200).json(toCaller)
|
||||||
|
@ -80,7 +80,7 @@ export function notificationMain(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function notifyAboutAnnouncement(): Promise<void> {
|
async function notifyAboutAnnouncement(): Promise<void> {
|
||||||
const announcementSheet = getSheet<AnnouncementWithoutId, Announcement>(
|
const announcementSheet = await getSheet<AnnouncementWithoutId, Announcement>(
|
||||||
"Announcements",
|
"Announcements",
|
||||||
new Announcement(),
|
new Announcement(),
|
||||||
translationAnnouncement
|
translationAnnouncement
|
||||||
@ -96,7 +96,7 @@ async function notifyAboutAnnouncement(): Promise<void> {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const volunteerSheet = getSheet<VolunteerWithoutId, Volunteer>(
|
const volunteerSheet = await getSheet<VolunteerWithoutId, Volunteer>(
|
||||||
"Volunteers",
|
"Volunteers",
|
||||||
new Volunteer(),
|
new Volunteer(),
|
||||||
translationVolunteer
|
translationVolunteer
|
||||||
|
Loading…
x
Reference in New Issue
Block a user