@@ -3,6 +3,7 @@ import crypto from 'crypto';
33import fs from 'fs-extra' ;
44import LRUCache from 'lru-cache' ;
55import isDocker from 'is-docker' ;
6+ import pLimit from 'p-limit' ;
67
78import { ApiGateway , UserBackgroundContext } from '@cubejs-backend/api-gateway' ;
89import {
@@ -11,7 +12,7 @@ import {
1112 getEnv , assertDataSource , getRealType , internalExceptions , track ,
1213} from '@cubejs-backend/shared' ;
1314
14- import type { Application as ExpressApplication } from 'express' ;
15+ import type { Application as ExpressApplication , NextFunction } from 'express' ;
1516
1617import { BaseDriver , DriverFactoryByDataSource } from '@cubejs-backend/query-orchestrator' ;
1718import { FileRepository , SchemaFileRepository } from './FileRepository' ;
@@ -47,7 +48,7 @@ import type {
4748 LoggerFn ,
4849 DriverConfig ,
4950} from './types' ;
50- import { ContextToOrchestratorIdFn } from './types' ;
51+ import { ContextToOrchestratorIdFn , ContextAcceptanceResult , ContextAcceptanceResultHttp , ContextAcceptanceResultWs , ContextAcceptor } from './types' ;
5152
5253const { version } = require ( '../../../package.json' ) ;
5354
@@ -59,6 +60,20 @@ function wrapToFnIfNeeded<T, R>(possibleFn: T | ((a: R) => T)): (a: R) => T {
5960 return ( ) => possibleFn ;
6061}
6162
63+ class AcceptAllAcceptor {
64+ public shouldAccept ( ) : ContextAcceptanceResult {
65+ return { accepted : true } ;
66+ }
67+
68+ public shouldAcceptHttp ( ) : ContextAcceptanceResultHttp {
69+ return { accepted : true } ;
70+ }
71+
72+ public shouldAcceptWs ( ) : ContextAcceptanceResultWs {
73+ return { accepted : true } ;
74+ }
75+ }
76+
6277export class CubejsServerCore {
6378 /**
6479 * Returns core version based on package.json.
@@ -67,13 +82,6 @@ export class CubejsServerCore {
6782 return version ;
6883 }
6984
70- /**
71- * Create an instance of the core.
72- */
73- public static create ( options ?: CreateOptions , systemOptions ?: SystemOptions ) {
74- return new CubejsServerCore ( options , systemOptions ) ;
75- }
76-
7785 /**
7886 * Resolve driver module name by db type.
7987 */
@@ -142,6 +150,8 @@ export class CubejsServerCore {
142150
143151 public coreServerVersion : string | null = null ;
144152
153+ private contextAcceptor : ContextAcceptor ;
154+
145155 /**
146156 * Class constructor.
147157 */
@@ -179,6 +189,8 @@ export class CubejsServerCore {
179189 this . standalone = false ;
180190 }
181191
192+ this . contextAcceptor = this . createContextAcceptor ( ) ;
193+
182194 if ( this . options . contextToDataSourceId ) {
183195 throw new Error ( 'contextToDataSourceId has been deprecated and removed. Use contextToOrchestratorId instead.' ) ;
184196 }
@@ -309,6 +321,10 @@ export class CubejsServerCore {
309321 }
310322 }
311323
324+ protected createContextAcceptor ( ) : ContextAcceptor {
325+ return new AcceptAllAcceptor ( ) ;
326+ }
327+
312328 /**
313329 * Determines whether current instance is ready to process queries.
314330 */
@@ -419,7 +435,7 @@ export class CubejsServerCore {
419435 return this . apiGatewayInstance ;
420436 }
421437
422- return this . apiGatewayInstance = new ApiGateway (
438+ return ( this . apiGatewayInstance = new ApiGateway (
423439 this . options . apiSecret ,
424440 this . getCompilerApi . bind ( this ) ,
425441 this . getOrchestratorApi . bind ( this ) ,
@@ -429,17 +445,34 @@ export class CubejsServerCore {
429445 dataSourceStorage : this . orchestratorStorage ,
430446 basePath : this . options . basePath ,
431447 checkAuthMiddleware : this . options . checkAuthMiddleware ,
448+ contextRejectionMiddleware : this . contextRejectionMiddleware . bind ( this ) ,
449+ wsContextAcceptor : this . contextAcceptor . shouldAcceptWs ,
432450 checkAuth : this . options . checkAuth ,
433- queryRewrite : this . options . queryRewrite || this . options . queryTransformer ,
451+ queryRewrite :
452+ this . options . queryRewrite || this . options . queryTransformer ,
434453 extendContext : this . options . extendContext ,
435454 playgroundAuthSecret : getEnv ( 'playgroundAuthSecret' ) ,
436455 jwt : this . options . jwt ,
437456 refreshScheduler : ( ) => new RefreshScheduler ( this ) ,
438457 scheduledRefreshContexts : this . options . scheduledRefreshContexts ,
439458 scheduledRefreshTimeZones : this . options . scheduledRefreshTimeZones ,
440- serverCoreVersion : this . coreServerVersion
459+ serverCoreVersion : this . coreServerVersion ,
441460 }
442- ) ;
461+ ) ) ;
462+ }
463+
464+ protected async contextRejectionMiddleware ( req , res , next ) {
465+ if ( ! this . standalone ) {
466+ const result = this . contextAcceptor . shouldAcceptHttp ( req . context ) ;
467+ if ( ! result . accepted ) {
468+ res . writeHead ( result . rejectStatusCode ! , result . rejectHeaders ! ) ;
469+ res . send ( ) ;
470+ return ;
471+ }
472+ }
473+ if ( next ) {
474+ next ( ) ;
475+ }
443476 }
444477
445478 public getCompilerApi ( context : RequestContext ) {
@@ -648,22 +681,33 @@ export class CubejsServerCore {
648681 * @internal Please dont use this method directly, use refreshTimer
649682 */
650683 public handleScheduledRefreshInterval = async ( options ) => {
651- const contexts = await this . options . scheduledRefreshContexts ( ) ;
684+ const contexts = ( await this . options . scheduledRefreshContexts ( ) ) . filter (
685+ ( context ) => this . contextAcceptor . shouldAccept ( this . migrateBackgroundContext ( context ) ) . accepted
686+ ) ;
652687 if ( contexts . length < 1 ) {
653688 this . logger ( 'Refresh Scheduler Error' , {
654689 error : 'At least one context should be returned by scheduledRefreshContexts'
655690 } ) ;
656691 }
657692
658- return Promise . all ( contexts . map ( async context => {
659- const queryingOptions : any = { ...options , concurrency : this . options . scheduledRefreshConcurrency } ;
693+ const batchLimit = pLimit ( this . options . scheduledRefreshBatchSize ) ;
694+ return Promise . all (
695+ contexts
696+ . map ( ( context ) => async ( ) => {
697+ const queryingOptions : any = {
698+ ...options ,
699+ concurrency : this . options . scheduledRefreshConcurrency ,
700+ } ;
660701
661- if ( this . options . scheduledRefreshTimeZones ) {
662- queryingOptions . timezones = this . options . scheduledRefreshTimeZones ;
663- }
702+ if ( this . options . scheduledRefreshTimeZones ) {
703+ queryingOptions . timezones = this . options . scheduledRefreshTimeZones ;
704+ }
664705
665- return this . runScheduledRefresh ( context , queryingOptions ) ;
666- } ) ) ;
706+ return this . runScheduledRefresh ( context , queryingOptions ) ;
707+ } )
708+ // Limit the number of refresh contexts we process per iteration
709+ . map ( batchLimit )
710+ ) ;
667711 } ;
668712
669713 protected getRefreshScheduler ( ) {
0 commit comments