@@ -11,10 +11,11 @@ import { analyzeDemo } from 'csdm/node/demo/analyze-demo';
11
11
import { getSettings } from 'csdm/node/settings/get-settings' ;
12
12
import { getErrorCodeFromError } from './get-error-code-from-error' ;
13
13
import type { ErrorCode } from 'csdm/common/error-code' ;
14
+ import { MAX_CONCURRENT_ANALYSES } from 'csdm/common/analyses' ;
14
15
15
16
class AnalysesListener {
16
17
private analyses : Analysis [ ] = [ ] ;
17
- private currentAnalysis : Analysis | undefined ;
18
+ private currentAnalyses : Analysis [ ] = [ ] ;
18
19
private outputFolderPath : string ; // Folder path where CSV files will be write on the host
19
20
20
21
public constructor ( ) {
@@ -60,47 +61,66 @@ class AnalysesListener {
60
61
}
61
62
62
63
public getAnalyses = ( ) => {
63
- if ( this . currentAnalysis !== undefined ) {
64
- return [ ...this . analyses , this . currentAnalysis ] ;
65
- }
66
- return this . analyses ;
64
+ return [ ...this . analyses , ...this . currentAnalyses ] ;
67
65
} ;
68
66
69
67
public hasAnalysesInProgress = ( ) => {
70
- return this . hasPendingAnalyses ( ) || this . currentAnalysis !== undefined ;
68
+ return this . hasPendingAnalyses ( ) || this . currentAnalyses . length > 0 ;
71
69
} ;
72
70
73
71
public clear ( ) {
74
72
this . analyses = [ ] ;
75
- this . currentAnalysis = undefined ;
73
+ this . currentAnalyses = [ ] ;
76
74
}
77
75
78
76
private hasPendingAnalyses = ( ) => {
79
77
return this . analyses . length > 0 ;
80
78
} ;
81
79
82
80
private async loopUntilAnalysesDone ( ) {
83
- if ( this . currentAnalysis ) {
84
- return ;
81
+ const promises : Promise < void > [ ] = [ ] ;
82
+
83
+ const settings = await getSettings ( ) ;
84
+ const maxConcurrentAnalyses = Math . min (
85
+ MAX_CONCURRENT_ANALYSES ,
86
+ settings . analyze . maxConcurrentAnalyses ?? MAX_CONCURRENT_ANALYSES / 2 ,
87
+ ) ;
88
+ while ( this . analyses . length > 0 && this . currentAnalyses . length < maxConcurrentAnalyses ) {
89
+ const analysis = this . analyses . shift ( ) ;
90
+ if ( analysis ) {
91
+ this . currentAnalyses . push ( analysis ) ;
92
+ const analysisPromise = this . processAnalysis ( analysis , settings . analyze . analyzePositions )
93
+ . catch ( ( error ) => {
94
+ logger . error ( 'Unhandled error during analysis' ) ;
95
+ logger . error ( error ) ;
96
+ } )
97
+ . finally ( ( ) => {
98
+ this . currentAnalyses = this . currentAnalyses . filter (
99
+ ( { demoChecksum } ) => demoChecksum !== analysis . demoChecksum ,
100
+ ) ;
101
+ } ) ;
102
+
103
+ promises . push ( analysisPromise ) ;
104
+ }
85
105
}
86
106
87
- this . currentAnalysis = this . analyses . shift ( ) ;
88
- while ( this . currentAnalysis ) {
89
- await this . processAnalysis ( this . currentAnalysis ) ;
90
- this . currentAnalysis = this . analyses . shift ( ) ;
107
+ if ( promises . length > 0 ) {
108
+ await Promise . race ( promises ) ;
109
+ if ( this . analyses . length > 0 ) {
110
+ await this . loopUntilAnalysesDone ( ) ;
111
+ }
91
112
}
92
113
}
93
114
94
- private readonly processAnalysis = async ( analysis : Analysis ) => {
115
+ private readonly processAnalysis = async ( analysis : Analysis , analyzePositions : boolean ) => {
95
116
const { demoChecksum : checksum , demoPath, source } = analysis ;
96
117
try {
97
- this . updateCurrentAnalysisStatus ( AnalysisStatus . Analyzing ) ;
98
- const settings = await getSettings ( ) ;
118
+ this . updateAnalysisStatus ( analysis , AnalysisStatus . Analyzing ) ;
99
119
await analyzeDemo ( {
100
120
demoPath,
101
- outputFolderPath : this . outputFolderPath ,
121
+ outputFolderPath : this . getAnalysisOutputFolderPath ( analysis ) ,
102
122
source,
103
- analyzePositions : settings . analyze . analyzePositions ,
123
+ analyzePositions,
104
124
onStdout : ( data ) => {
105
125
logger . log ( data ) ;
106
126
analysis . output += data ;
@@ -118,72 +138,72 @@ class AnalysesListener {
118
138
} ) ;
119
139
} ,
120
140
} ) ;
121
- this . updateCurrentAnalysisStatus ( AnalysisStatus . AnalyzeSuccess ) ;
141
+ this . updateAnalysisStatus ( analysis , AnalysisStatus . AnalyzeSuccess ) ;
122
142
123
- await this . insertMatch ( checksum , demoPath ) ;
143
+ await this . insertMatch ( analysis , checksum , demoPath ) ;
124
144
} catch ( error ) {
125
145
logger . error ( 'Error while analyzing demo' ) ;
126
146
if ( error ) {
127
147
logger . error ( error ) ;
128
148
}
129
149
const isCorruptedDemo = error instanceof CorruptedDemoError ;
130
- if ( ! isCorruptedDemo && this . currentAnalysis && error instanceof Error ) {
131
- this . currentAnalysis . output += error . message ;
150
+ if ( ! isCorruptedDemo && error instanceof Error ) {
151
+ analysis . output += error . message ;
132
152
}
133
- this . updateCurrentAnalysisStatus ( AnalysisStatus . AnalyzeError ) ;
153
+ this . updateAnalysisStatus ( analysis , AnalysisStatus . AnalyzeError ) ;
134
154
// If the demo is corrupted, we still want to try to insert it in the database.
135
155
if ( isCorruptedDemo ) {
136
- await this . insertMatch ( checksum , demoPath ) ;
156
+ await this . insertMatch ( analysis , checksum , demoPath ) ;
137
157
}
138
158
}
139
159
} ;
140
160
141
- private async insertMatch ( checksum : string , demoPath : string ) {
161
+ private async insertMatch ( analysis : Analysis , checksum : string , demoPath : string ) {
142
162
try {
143
- this . updateCurrentAnalysisStatus ( AnalysisStatus . Inserting ) ;
163
+ this . updateAnalysisStatus ( analysis , AnalysisStatus . Inserting ) ;
144
164
const match = await processMatchInsertion ( {
145
165
checksum,
146
166
demoPath,
147
- outputFolderPath : this . outputFolderPath ,
167
+ outputFolderPath : this . getAnalysisOutputFolderPath ( analysis ) ,
148
168
} ) ;
149
- this . updateCurrentAnalysisStatus ( AnalysisStatus . InsertSuccess ) ;
169
+ this . updateAnalysisStatus ( analysis , AnalysisStatus . InsertSuccess ) ;
150
170
server . sendMessageToRendererProcess ( {
151
171
name : RendererServerMessageName . MatchInserted ,
152
172
payload : match ,
153
173
} ) ;
154
174
} catch ( error ) {
155
175
logger . error ( 'Error while inserting match' ) ;
156
176
logger . error ( error ) ;
157
- if ( this . currentAnalysis ) {
158
- let errorOutput : string ;
159
- if ( error instanceof Error ) {
160
- errorOutput = error . message ;
161
- if ( error . stack ) {
162
- errorOutput += `\n${ error . stack } ` ;
163
- }
164
- if ( error . cause ) {
165
- errorOutput += `\n${ error . cause } ` ;
166
- }
167
- } else {
168
- errorOutput = String ( error ) ;
177
+ let errorOutput : string ;
178
+ if ( error instanceof Error ) {
179
+ errorOutput = error . message ;
180
+ if ( error . stack ) {
181
+ errorOutput += `\n${ error . stack } ` ;
182
+ }
183
+ if ( error . cause ) {
184
+ errorOutput += `\n${ error . cause } ` ;
169
185
}
170
- this . currentAnalysis . output += errorOutput ;
186
+ } else {
187
+ errorOutput = String ( error ) ;
171
188
}
189
+ analysis . output += errorOutput ;
172
190
173
- this . updateCurrentAnalysisStatus ( AnalysisStatus . InsertError , getErrorCodeFromError ( error ) ) ;
191
+ this . updateAnalysisStatus ( analysis , AnalysisStatus . InsertError , getErrorCodeFromError ( error ) ) ;
174
192
}
175
193
}
176
194
177
- private updateCurrentAnalysisStatus = ( status : AnalysisStatus , errorCode ?: ErrorCode ) => {
178
- if ( this . currentAnalysis !== undefined ) {
179
- this . currentAnalysis . status = status ;
180
- this . currentAnalysis . errorCode = errorCode ;
181
- server . sendMessageToRendererProcess ( {
182
- name : RendererServerMessageName . AnalysisUpdated ,
183
- payload : this . currentAnalysis ,
184
- } ) ;
185
- }
195
+ private updateAnalysisStatus = ( analysis : Analysis , status : AnalysisStatus , errorCode ?: ErrorCode ) => {
196
+ analysis . status = status ;
197
+ analysis . errorCode = errorCode ;
198
+ server . sendMessageToRendererProcess ( {
199
+ name : RendererServerMessageName . AnalysisUpdated ,
200
+ payload : analysis ,
201
+ } ) ;
186
202
} ;
203
+
204
+ private getAnalysisOutputFolderPath ( analysis : Analysis ) {
205
+ return path . join ( this . outputFolderPath , analysis . demoChecksum ) ;
206
+ }
187
207
}
188
208
189
209
export const analysesListener = new AnalysesListener ( ) ;
0 commit comments