@jsreport/jsreport-core 3.4.1 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -1
- package/lib/main/blobStorage/blobStorage.js +8 -0
- package/lib/main/logger.js +3 -0
- package/lib/main/optionsLoad.js +10 -2
- package/lib/main/optionsSchema.js +21 -3
- package/lib/main/profiler.js +166 -75
- package/lib/main/reporter.js +113 -78
- package/lib/main/schemaValidator.js +30 -0
- package/lib/main/settings.js +1 -2
- package/lib/main/store/collection.js +10 -8
- package/lib/main/store/documentStore.js +19 -0
- package/lib/main/store/setupValidateId.js +7 -1
- package/lib/main/store/setupValidateShortid.js +4 -0
- package/lib/shared/normalizeMetaFromLogs.js +1 -1
- package/lib/shared/reporter.js +16 -0
- package/lib/worker/render/executeEngine.js +108 -27
- package/lib/worker/render/moduleHelper.js +4 -4
- package/lib/worker/render/profiler.js +20 -13
- package/lib/worker/render/render.js +0 -4
- package/lib/worker/reporter.js +6 -4
- package/lib/worker/sandbox/{safeSandbox.js → createSandbox.js} +85 -52
- package/lib/worker/sandbox/runInSandbox.js +38 -16
- package/package.json +9 -9
- package/lib/main/monitoring.js +0 -92
package/README.md
CHANGED
|
@@ -36,7 +36,7 @@ const result = await jsreport.render({
|
|
|
36
36
|
foo: "world"
|
|
37
37
|
}
|
|
38
38
|
})
|
|
39
|
-
await fs.writeFile('out.pdf',
|
|
39
|
+
await fs.writeFile('out.pdf', result.content)
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
## Render
|
|
@@ -282,6 +282,19 @@ jsreport.documentStore.collection('templates')
|
|
|
282
282
|
|
|
283
283
|
## Changelog
|
|
284
284
|
|
|
285
|
+
### 3.5.0
|
|
286
|
+
|
|
287
|
+
- fix parsing issue of code with comment in the sandbox (helpers, scripts)
|
|
288
|
+
- improve profiling when there is big data
|
|
289
|
+
- make transactions support in store configurable
|
|
290
|
+
- improve timeout for the whole request
|
|
291
|
+
- fix applying req.options.timeout when enableRequestReportTimeout is true
|
|
292
|
+
- optimization regarding profile persistence
|
|
293
|
+
|
|
294
|
+
### 3.4.2
|
|
295
|
+
|
|
296
|
+
- update dep `vm2` to fix security vulnerability in sandbox
|
|
297
|
+
|
|
285
298
|
### 3.4.1
|
|
286
299
|
|
|
287
300
|
- fix passing data to async report
|
package/lib/main/logger.js
CHANGED
|
@@ -166,6 +166,9 @@ function configureLogger (logger, _transports) {
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
for (const { TransportClass, options } of transportsToAdd) {
|
|
169
|
+
if (options.silent) {
|
|
170
|
+
continue
|
|
171
|
+
}
|
|
169
172
|
const transportInstance = new TransportClass(options)
|
|
170
173
|
|
|
171
174
|
const existingTransport = logger.transports.find((t) => t.name === transportInstance.name)
|
package/lib/main/optionsLoad.js
CHANGED
|
@@ -39,6 +39,7 @@ async function optionsLoad ({
|
|
|
39
39
|
loadConfigResult = await loadConfig(defaults, options, false)
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
const explicitOptions = loadConfigResult[0]
|
|
42
43
|
const appliedConfigFile = loadConfigResult[1]
|
|
43
44
|
|
|
44
45
|
options.loadConfig = shouldLoadExternalConfig
|
|
@@ -79,9 +80,16 @@ async function optionsLoad ({
|
|
|
79
80
|
options.store = options.store || { provider: 'memory' }
|
|
80
81
|
|
|
81
82
|
options.sandbox = options.sandbox || {}
|
|
82
|
-
|
|
83
|
+
|
|
84
|
+
// NOTE: handling back-compatible introduction of "trustUserCode" option, "allowLocalFilesAccess" is deprecated
|
|
85
|
+
if (explicitOptions.allowLocalFilesAccess === true && explicitOptions.trustUserCode == null) {
|
|
86
|
+
options.trustUserCode = true
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (options.trustUserCode === true) {
|
|
83
90
|
options.sandbox.allowedModules = '*'
|
|
84
91
|
}
|
|
92
|
+
|
|
85
93
|
options.sandbox.nativeModules = options.sandbox.nativeModules || []
|
|
86
94
|
options.sandbox.modules = options.sandbox.modules || []
|
|
87
95
|
options.sandbox.allowedModules = options.sandbox.allowedModules || []
|
|
@@ -98,7 +106,7 @@ async function optionsLoad ({
|
|
|
98
106
|
fs.mkdirSync(options.tempCoreDirectory, { recursive: true })
|
|
99
107
|
}
|
|
100
108
|
|
|
101
|
-
return appliedConfigFile
|
|
109
|
+
return [explicitOptions, appliedConfigFile]
|
|
102
110
|
}
|
|
103
111
|
|
|
104
112
|
/**
|
|
@@ -59,6 +59,7 @@ module.exports.getRootSchemaOptions = () => ({
|
|
|
59
59
|
default: '2s'
|
|
60
60
|
},
|
|
61
61
|
enableRequestReportTimeout: { type: 'boolean', default: false, description: 'option that enables passing a custom report timeout per request using req.options.timeout. this enables that the caller of the report generation control the report timeout so enable it only when you trust the caller' },
|
|
62
|
+
trustUserCode: { type: 'boolean', default: false, description: 'option that control whether code sandboxing is enabled or not, code sandboxing has an impact on performance when rendering large reports. when true code sandboxing will be disabled meaning that users can potentially penetrate the local system if you allow code from external users to be part of your reports' },
|
|
62
63
|
allowLocalFilesAccess: { type: 'boolean', default: false },
|
|
63
64
|
encryption: {
|
|
64
65
|
type: 'object',
|
|
@@ -77,6 +78,7 @@ module.exports.getRootSchemaOptions = () => ({
|
|
|
77
78
|
},
|
|
78
79
|
sandbox: {
|
|
79
80
|
type: 'object',
|
|
81
|
+
default: {},
|
|
80
82
|
properties: {
|
|
81
83
|
allowedModules: {
|
|
82
84
|
anyOf: [{
|
|
@@ -89,9 +91,10 @@ module.exports.getRootSchemaOptions = () => ({
|
|
|
89
91
|
},
|
|
90
92
|
cache: {
|
|
91
93
|
type: 'object',
|
|
94
|
+
default: {},
|
|
92
95
|
properties: {
|
|
93
|
-
max: { type: 'number' },
|
|
94
|
-
enabled: { type: 'boolean' }
|
|
96
|
+
max: { type: 'number', default: 100 },
|
|
97
|
+
enabled: { type: 'boolean', default: true }
|
|
95
98
|
}
|
|
96
99
|
}
|
|
97
100
|
}
|
|
@@ -126,7 +129,13 @@ module.exports.getRootSchemaOptions = () => ({
|
|
|
126
129
|
store: {
|
|
127
130
|
type: 'object',
|
|
128
131
|
properties: {
|
|
129
|
-
provider: { type: 'string', enum: ['memory'] }
|
|
132
|
+
provider: { type: 'string', enum: ['memory'] },
|
|
133
|
+
transactions: {
|
|
134
|
+
type: 'object',
|
|
135
|
+
properties: {
|
|
136
|
+
enabled: { type: 'boolean', default: true }
|
|
137
|
+
}
|
|
138
|
+
}
|
|
130
139
|
}
|
|
131
140
|
},
|
|
132
141
|
blobStorage: {
|
|
@@ -163,6 +172,10 @@ module.exports.getRootSchemaOptions = () => ({
|
|
|
163
172
|
type: 'object',
|
|
164
173
|
default: {},
|
|
165
174
|
properties: {
|
|
175
|
+
defaultMode: {
|
|
176
|
+
type: 'string',
|
|
177
|
+
default: 'standard'
|
|
178
|
+
},
|
|
166
179
|
maxProfilesHistory: {
|
|
167
180
|
type: 'number',
|
|
168
181
|
default: 1000
|
|
@@ -171,6 +184,11 @@ module.exports.getRootSchemaOptions = () => ({
|
|
|
171
184
|
type: ['string', 'number'],
|
|
172
185
|
'$jsreport-acceptsDuration': true,
|
|
173
186
|
default: '1m'
|
|
187
|
+
},
|
|
188
|
+
maxDiffSize: {
|
|
189
|
+
type: ['string', 'number'],
|
|
190
|
+
'$jsreport-acceptsSize': true,
|
|
191
|
+
default: '50mb'
|
|
174
192
|
}
|
|
175
193
|
}
|
|
176
194
|
}
|
package/lib/main/profiler.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const EventEmitter = require('events')
|
|
2
2
|
const extend = require('node.extend.without.arrays')
|
|
3
3
|
const generateRequestId = require('../shared/generateRequestId')
|
|
4
|
+
const fs = require('fs/promises')
|
|
4
5
|
|
|
5
6
|
module.exports = (reporter) => {
|
|
6
7
|
reporter.documentStore.registerEntityType('ProfileType', {
|
|
@@ -8,9 +9,9 @@ module.exports = (reporter) => {
|
|
|
8
9
|
timestamp: { type: 'Edm.DateTimeOffset', schema: { type: 'null' } },
|
|
9
10
|
finishedOn: { type: 'Edm.DateTimeOffset', schema: { type: 'null' } },
|
|
10
11
|
state: { type: 'Edm.String' },
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
error: { type: 'Edm.String' },
|
|
13
|
+
mode: { type: 'Edm.String', schema: { enum: ['full', 'standard', 'disabled'] } },
|
|
14
|
+
blobName: { type: 'Edm.String' }
|
|
14
15
|
})
|
|
15
16
|
|
|
16
17
|
reporter.documentStore.registerEntitySet('profiles', {
|
|
@@ -19,9 +20,41 @@ module.exports = (reporter) => {
|
|
|
19
20
|
})
|
|
20
21
|
|
|
21
22
|
const profilersMap = new Map()
|
|
22
|
-
const profilerAppendChain = new Map()
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
const profilerOperationsChainsMap = new Map()
|
|
25
|
+
function runInProfilerChain (fn, req) {
|
|
26
|
+
if (req.context.profiling.mode === 'disabled') {
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
profilerOperationsChainsMap.set(req.context.rootId, profilerOperationsChainsMap.get(req.context.rootId).then(async () => {
|
|
31
|
+
if (req.context.profiling.chainFailed) {
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
await fn()
|
|
37
|
+
} catch (e) {
|
|
38
|
+
reporter.logger.warn('Failed persist profile', e)
|
|
39
|
+
req.context.profiling.chainFailed = true
|
|
40
|
+
}
|
|
41
|
+
}))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function createProfileMessage (m, req) {
|
|
45
|
+
m.timestamp = new Date().getTime()
|
|
46
|
+
m.id = generateRequestId()
|
|
47
|
+
m.previousOperationId = m.previousOperationId || null
|
|
48
|
+
if (m.type !== 'log') {
|
|
49
|
+
m.operationId = m.operationId || generateRequestId()
|
|
50
|
+
req.context.profiling.lastOperationId = m.operationId
|
|
51
|
+
req.context.profiling.lastEventId = m.id
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return m
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function emitProfiles (events, req) {
|
|
25
58
|
if (events.length === 0) {
|
|
26
59
|
return
|
|
27
60
|
}
|
|
@@ -44,14 +77,16 @@ module.exports = (reporter) => {
|
|
|
44
77
|
req.context.profiling.lastOperation = lastOperation
|
|
45
78
|
}
|
|
46
79
|
|
|
47
|
-
|
|
80
|
+
runInProfilerChain(() => {
|
|
81
|
+
if (req.context.profiling.logFilePath) {
|
|
82
|
+
return fs.appendFile(req.context.profiling.logFilePath, Buffer.from(events.map(m => JSON.stringify(m)).join('\n') + '\n'))
|
|
83
|
+
}
|
|
84
|
+
|
|
48
85
|
return reporter.blobStorage.append(
|
|
49
86
|
req.context.profiling.entity.blobName,
|
|
50
87
|
Buffer.from(events.map(m => JSON.stringify(m)).join('\n') + '\n'), req
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
})
|
|
54
|
-
}))
|
|
88
|
+
)
|
|
89
|
+
}, req)
|
|
55
90
|
}
|
|
56
91
|
|
|
57
92
|
reporter.registerMainAction('profile', async (events, req) => {
|
|
@@ -62,8 +97,7 @@ module.exports = (reporter) => {
|
|
|
62
97
|
req.context = req.context || {}
|
|
63
98
|
req.context.rootId = reporter.generateRequestId()
|
|
64
99
|
req.context.profiling = {
|
|
65
|
-
mode: profileMode == null ? 'full' : profileMode
|
|
66
|
-
isAttached: true
|
|
100
|
+
mode: profileMode == null ? 'full' : profileMode
|
|
67
101
|
}
|
|
68
102
|
const profiler = new EventEmitter()
|
|
69
103
|
profilersMap.set(req.context.rootId, profiler)
|
|
@@ -71,88 +105,136 @@ module.exports = (reporter) => {
|
|
|
71
105
|
return profiler
|
|
72
106
|
}
|
|
73
107
|
|
|
74
|
-
reporter.
|
|
75
|
-
profilerAppendChain.set(req.context.rootId, Promise.resolve())
|
|
76
|
-
|
|
108
|
+
reporter.beforeRenderWorkerAllocatedListeners.add('profiler', async (req) => {
|
|
77
109
|
req.context.profiling = req.context.profiling || {}
|
|
110
|
+
|
|
111
|
+
if (req.context.profiling.mode == null) {
|
|
112
|
+
const profilerSettings = await reporter.settings.findValue('profiler', req)
|
|
113
|
+
const defaultMode = reporter.options.profiler.defaultMode || 'standard'
|
|
114
|
+
req.context.profiling.mode = (profilerSettings != null && profilerSettings.mode != null) ? profilerSettings.mode : defaultMode
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
profilerOperationsChainsMap.set(req.context.rootId, Promise.resolve())
|
|
118
|
+
|
|
78
119
|
req.context.profiling.lastOperation = null
|
|
79
120
|
|
|
80
|
-
|
|
121
|
+
const blobName = `profiles/${req.context.rootId}.log`
|
|
81
122
|
|
|
82
|
-
const
|
|
123
|
+
const profile = {
|
|
124
|
+
_id: reporter.documentStore.generateId(),
|
|
125
|
+
timestamp: new Date(),
|
|
126
|
+
state: 'queued',
|
|
127
|
+
mode: req.context.profiling.mode,
|
|
128
|
+
blobName
|
|
129
|
+
}
|
|
83
130
|
|
|
84
|
-
if (
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
// store a copy to prevent side-effects
|
|
88
|
-
req.context.resolvedTemplate = extend(true, {}, template)
|
|
131
|
+
if (!reporter.blobStorage.supportsAppend) {
|
|
132
|
+
const { pathToFile } = await reporter.writeTempFile((uuid) => `${uuid}.log`, '')
|
|
133
|
+
req.context.profiling.logFilePath = pathToFile
|
|
89
134
|
}
|
|
90
135
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
136
|
+
runInProfilerChain(async () => {
|
|
137
|
+
req.context.skipValidationFor = profile
|
|
138
|
+
await reporter.documentStore.collection('profiles').insert(profile, req)
|
|
139
|
+
}, req)
|
|
140
|
+
|
|
141
|
+
req.context.profiling.entity = profile
|
|
142
|
+
|
|
143
|
+
const profileStartOperation = createProfileMessage({
|
|
144
|
+
type: 'operationStart',
|
|
145
|
+
subtype: 'profile',
|
|
146
|
+
data: profile,
|
|
147
|
+
doDiffs: false
|
|
148
|
+
}, req)
|
|
149
|
+
|
|
150
|
+
req.context.profiling.profileStartOperationId = profileStartOperation.operationId
|
|
151
|
+
|
|
152
|
+
emitProfiles([profileStartOperation], req)
|
|
153
|
+
|
|
154
|
+
emitProfiles([createProfileMessage({
|
|
155
|
+
type: 'log',
|
|
156
|
+
level: 'info',
|
|
157
|
+
message: `Render request ${req.context.reportCounter} queued for execution and waiting for availible worker`,
|
|
158
|
+
previousOperationId: profileStartOperation.operationId
|
|
159
|
+
}, req)], req)
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
reporter.beforeRenderListeners.add('profiler', async (req, res) => {
|
|
163
|
+
const update = {
|
|
164
|
+
state: 'running'
|
|
97
165
|
}
|
|
98
166
|
|
|
99
|
-
|
|
100
|
-
|
|
167
|
+
const template = await reporter.templates.resolveTemplate(req)
|
|
168
|
+
if (template && template._id) {
|
|
169
|
+
req.context.resolvedTemplate = extend(true, {}, template)
|
|
170
|
+
const templatePath = await reporter.folders.resolveEntityPath(template, 'templates', req)
|
|
171
|
+
const blobName = `profiles/${templatePath.substring(1)}/${req.context.rootId}.log`
|
|
172
|
+
update.templateShortid = template.shortid
|
|
173
|
+
|
|
174
|
+
const originalBlobName = req.context.profiling.entity.blobName
|
|
175
|
+
// we want to store the profile into blobName path reflecting the template path so we need to copy the blob to new path now
|
|
176
|
+
runInProfilerChain(async () => {
|
|
177
|
+
if (req.context.profiling.logFilePath == null) {
|
|
178
|
+
const content = await reporter.blobStorage.read(originalBlobName, req)
|
|
179
|
+
await reporter.blobStorage.write(blobName, content, req)
|
|
180
|
+
return reporter.blobStorage.remove(originalBlobName, req)
|
|
181
|
+
}
|
|
182
|
+
}, req)
|
|
183
|
+
|
|
184
|
+
update.blobName = blobName
|
|
101
185
|
}
|
|
102
186
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
187
|
+
runInProfilerChain(() => {
|
|
188
|
+
req.context.skipValidationFor = update
|
|
189
|
+
return reporter.documentStore.collection('profiles').update({
|
|
190
|
+
_id: req.context.profiling.entity._id
|
|
191
|
+
}, {
|
|
192
|
+
$set: update
|
|
193
|
+
}, req)
|
|
109
194
|
}, req)
|
|
110
195
|
|
|
111
|
-
req.context.profiling.entity
|
|
196
|
+
Object.assign(req.context.profiling.entity, update)
|
|
112
197
|
})
|
|
113
198
|
|
|
114
199
|
reporter.afterRenderListeners.add('profiler', async (req, res) => {
|
|
200
|
+
emitProfiles([createProfileMessage({
|
|
201
|
+
type: 'operationEnd',
|
|
202
|
+
doDiffs: false,
|
|
203
|
+
previousEventId: req.context.profiling.lastEventId,
|
|
204
|
+
previousOperationId: req.context.profiling.lastOperationId,
|
|
205
|
+
operationId: req.context.profiling.profileStartOperationId
|
|
206
|
+
}, req)], req)
|
|
207
|
+
|
|
115
208
|
res.meta.profileId = req.context.profiling?.entity?._id
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
209
|
+
|
|
210
|
+
runInProfilerChain(async () => {
|
|
211
|
+
if (req.context.profiling.logFilePath != null) {
|
|
212
|
+
const content = await fs.readFile(req.context.profiling.logFilePath)
|
|
213
|
+
await reporter.blobStorage.write(req.context.profiling.entity.blobName, content, req)
|
|
214
|
+
await fs.unlink(req.context.profiling.logFilePath)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const update = {
|
|
124
218
|
state: 'success',
|
|
125
219
|
finishedOn: new Date()
|
|
126
220
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
reporter.documentStore.collection('profiles').update({
|
|
221
|
+
req.context.skipValidationFor = update
|
|
222
|
+
await reporter.documentStore.collection('profiles').update({
|
|
130
223
|
_id: req.context.profiling.entity._id
|
|
131
224
|
}, {
|
|
132
|
-
$set:
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
225
|
+
$set: update
|
|
226
|
+
}, req)
|
|
227
|
+
}, req)
|
|
228
|
+
|
|
229
|
+
// we don't remove from profiler requests map, because the renderErrorListeners are invoked if the afterRenderListener fails
|
|
137
230
|
})
|
|
138
231
|
|
|
139
232
|
reporter.renderErrorListeners.add('profiler', async (req, res, e) => {
|
|
140
233
|
try {
|
|
141
234
|
res.meta.profileId = req.context.profiling?.entity?._id
|
|
142
|
-
const profilerBlobPersistPromise = profilerAppendChain.get(req.context.rootId)
|
|
143
235
|
|
|
144
236
|
if (req.context.profiling?.entity != null) {
|
|
145
|
-
|
|
146
|
-
_id: req.context.profiling.entity._id
|
|
147
|
-
}, {
|
|
148
|
-
$set: {
|
|
149
|
-
state: 'error',
|
|
150
|
-
finishedOn: new Date(),
|
|
151
|
-
error: e.toString()
|
|
152
|
-
}
|
|
153
|
-
}, req)
|
|
154
|
-
|
|
155
|
-
await emitProfiles([{
|
|
237
|
+
emitProfiles([{
|
|
156
238
|
type: 'error',
|
|
157
239
|
timestamp: new Date().getTime(),
|
|
158
240
|
...e,
|
|
@@ -160,20 +242,29 @@ module.exports = (reporter) => {
|
|
|
160
242
|
stack: e.stack,
|
|
161
243
|
message: e.message
|
|
162
244
|
}], req)
|
|
245
|
+
runInProfilerChain(async () => {
|
|
246
|
+
if (req.context.profiling.logFilePath != null) {
|
|
247
|
+
const content = await fs.readFile(req.context.profiling.logFilePath, 'utf8')
|
|
248
|
+
await reporter.blobStorage.write(req.context.profiling.entity.blobName, content, req)
|
|
249
|
+
await fs.unlink(req.context.profiling.logFilePath)
|
|
250
|
+
}
|
|
163
251
|
|
|
164
|
-
|
|
165
|
-
|
|
252
|
+
const update = {
|
|
253
|
+
state: 'error',
|
|
254
|
+
finishedOn: new Date(),
|
|
255
|
+
error: e.toString()
|
|
256
|
+
}
|
|
257
|
+
req.context.skipValidationFor = update
|
|
258
|
+
await reporter.documentStore.collection('profiles').update({
|
|
166
259
|
_id: req.context.profiling.entity._id
|
|
167
260
|
}, {
|
|
168
|
-
$set:
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}, req).catch((e) => reporter.logger.error('Failed to update profile blobPersisted', e))
|
|
172
|
-
})
|
|
261
|
+
$set: update
|
|
262
|
+
}, req)
|
|
263
|
+
}, req)
|
|
173
264
|
}
|
|
174
265
|
} finally {
|
|
175
266
|
profilersMap.delete(req.context.rootId)
|
|
176
|
-
|
|
267
|
+
profilerOperationsChainsMap.delete(req.context.rootId)
|
|
177
268
|
}
|
|
178
269
|
})
|
|
179
270
|
|
|
@@ -197,8 +288,8 @@ module.exports = (reporter) => {
|
|
|
197
288
|
clearInterval(profilesCleanupInterval)
|
|
198
289
|
}
|
|
199
290
|
|
|
200
|
-
for (const key of
|
|
201
|
-
const profileAppendPromise =
|
|
291
|
+
for (const key of profilerOperationsChainsMap.keys()) {
|
|
292
|
+
const profileAppendPromise = profilerOperationsChainsMap.get(key)
|
|
202
293
|
if (profileAppendPromise) {
|
|
203
294
|
await profileAppendPromise
|
|
204
295
|
}
|