@@ -15,6 +15,8 @@ const { readFile } = require('fs/promises')
15
15
const semver = require ( 'semver' )
16
16
const DB_INDEX = `test-${ randomString ( ) } `
17
17
const DB_INDEX_2 = `test2-${ randomString ( ) } `
18
+ const DB_INDEX_3 = `test3-${ randomString ( ) } `
19
+ const SEARCHTERM_1 = randomString ( )
18
20
19
21
function randomString ( ) {
20
22
return crypto . randomBytes ( 5 ) . toString ( 'hex' )
@@ -130,7 +132,10 @@ test('Elasticsearch instrumentation', (t) => {
130
132
{ index : { _index : DB_INDEX_2 } } ,
131
133
{ title : 'Fifth Bulk Doc' , body : 'Content of fifth bulk document' } ,
132
134
{ index : { _index : DB_INDEX_2 } } ,
133
- { title : 'Sixth Bulk Doc' , body : 'Content of sixth bulk document.' } ,
135
+ {
136
+ title : 'Sixth Bulk Doc' ,
137
+ body : `Content of sixth bulk document. Has search term: ${ SEARCHTERM_1 } `
138
+ } ,
134
139
{ index : { _index : DB_INDEX_2 } } ,
135
140
{ title : 'Seventh Bulk Doc' , body : 'Content of seventh bulk document.' } ,
136
141
{ index : { _index : DB_INDEX_2 } } ,
@@ -150,13 +155,54 @@ test('Elasticsearch instrumentation', (t) => {
150
155
} )
151
156
} )
152
157
158
+ t . test ( 'should record bulk operations triggered by client helpers' , async ( t ) => {
159
+ await helper . runInTransaction ( agent , async function transactionInScope ( transaction ) {
160
+ const operations = [
161
+ { title : 'Ninth Bulk Doc from helpers' , body : 'Content of ninth bulk document' } ,
162
+ { title : 'Tenth Bulk Doc from helpers' , body : 'Content of tenth bulk document.' } ,
163
+ { title : 'Eleventh Bulk Doc from helpers' , body : 'Content of eleventh bulk document.' } ,
164
+ { title : 'Twelfth Bulk Doc from helpers' , body : 'Content of twelfth bulk document.' } ,
165
+ {
166
+ title : 'Thirteenth Bulk Doc from helpers' ,
167
+ body : 'Content of thirteenth bulk document'
168
+ } ,
169
+ {
170
+ title : 'Fourteenth Bulk Doc from helpers' ,
171
+ body : 'Content of fourteenth bulk document.'
172
+ } ,
173
+ { title : 'Fifteenth Bulk Doc from helpers' , body : 'Content of fifteenth bulk document.' } ,
174
+ { title : 'Sixteenth Bulk Doc from helpers' , body : 'Content of sixteenth bulk document.' }
175
+ ]
176
+ await client . helpers . bulk ( {
177
+ datasource : operations ,
178
+ onDocument ( ) {
179
+ return {
180
+ index : { _index : DB_INDEX_2 }
181
+ }
182
+ } ,
183
+ refreshOnCompletion : true
184
+ } )
185
+ t . ok ( transaction , 'transaction should still be visible after bulk create' )
186
+ const trace = transaction . trace
187
+ t . ok ( trace ?. root ?. children ?. [ 0 ] , 'trace, trace root, and first child should exist' )
188
+ t . ok ( trace ?. root ?. children ?. [ 1 ] , 'trace, trace root, and second child should exist' )
189
+ // helper interface results in a first child of timers.setTimeout, with the second child related to the operation
190
+ const secondChild = trace . root . children [ 1 ]
191
+ t . equal (
192
+ secondChild . name ,
193
+ 'Datastore/statement/ElasticSearch/any/bulk.create' ,
194
+ 'should record bulk operation'
195
+ )
196
+ } )
197
+ } )
198
+
153
199
t . test ( 'should record search with query string' , async function ( t ) {
154
200
// enable slow queries
155
201
agent . config . transaction_tracer . explain_threshold = 0
156
202
agent . config . transaction_tracer . record_sql = 'raw'
157
203
agent . config . slow_sql . enabled = true
158
204
await helper . runInTransaction ( agent , async function transactionInScope ( transaction ) {
159
- const expectedQuery = { q : 'sixth' }
205
+ const expectedQuery = { q : SEARCHTERM_1 }
160
206
const search = await client . search ( { index : DB_INDEX_2 , ...expectedQuery } )
161
207
t . ok ( search , 'search should return a result' )
162
208
t . ok ( transaction , 'transaction should still be visible after search' )
@@ -263,29 +309,29 @@ test('Elasticsearch instrumentation', (t) => {
263
309
await helper . runInTransaction ( agent , async function transactionInScope ( transaction ) {
264
310
const expectedQuery = [
265
311
{ } , // cross-index searches have can have an empty metadata section
266
- { query : { match : { body : 'sixth' } } } ,
312
+ { query : { match : { body : SEARCHTERM_1 } } } ,
267
313
{ } ,
268
314
{ query : { match : { body : 'bulk' } } }
269
315
]
270
316
const requestBody = setMsearch ( expectedQuery , pkgVersion )
271
317
const search = await client . msearch ( requestBody )
272
318
// 7 and 8 have different result responses
273
319
let results = search ?. responses
274
- if ( semver . lt ( pkgVersion , '8.0.0' ) ) {
320
+ if ( ! search ?. responses && semver . lt ( pkgVersion , '8.0.0' ) ) {
275
321
results = search ?. body ?. responses
276
322
}
277
323
278
324
t . ok ( results , 'msearch should return results' )
279
325
t . equal ( results ?. length , 2 , 'there should be two responses--one per search' )
280
326
t . equal ( results ?. [ 0 ] ?. hits ?. hits ?. length , 1 , 'first search should return one result' )
281
- t . equal ( results ?. [ 1 ] ?. hits ?. hits ?. length , 8 , 'second search should return eight results' )
327
+ t . equal ( results ?. [ 1 ] ?. hits ?. hits ?. length , 10 , 'second search should return ten results' )
282
328
t . ok ( transaction , 'transaction should still be visible after search' )
283
329
const trace = transaction . trace
284
330
t . ok ( trace ?. root ?. children ?. [ 0 ] , 'trace, trace root, and first child should exist' )
285
331
const firstChild = trace . root . children [ 0 ]
286
332
t . match (
287
333
firstChild . name ,
288
- 'Datastore/statement/ElasticSearch/any/msearch' ,
334
+ 'Datastore/statement/ElasticSearch/any/msearch.create ' ,
289
335
'child name should show msearch'
290
336
)
291
337
const attrs = firstChild . getAttributes ( )
@@ -304,6 +350,39 @@ test('Elasticsearch instrumentation', (t) => {
304
350
} )
305
351
} )
306
352
353
+ t . test ( 'should record msearch via helpers' , async function ( t ) {
354
+ agent . config . transaction_tracer . explain_threshold = 0
355
+ agent . config . transaction_tracer . record_sql = 'raw'
356
+ agent . config . slow_sql . enabled = true
357
+ await helper . runInTransaction ( agent , async function transactionInScope ( transaction ) {
358
+ const m = client . helpers . msearch ( )
359
+ const searchA = await m . search ( { } , { query : { match : { body : SEARCHTERM_1 } } } )
360
+ const searchB = await m . search ( { } , { query : { match : { body : 'bulk' } } } )
361
+ const resultsA = searchA ?. body ?. hits
362
+ const resultsB = searchB ?. body ?. hits
363
+
364
+ t . ok ( resultsA , 'msearch for sixth should return results' )
365
+ t . ok ( resultsB , 'msearch for bulk should return results' )
366
+ t . equal ( resultsA ?. hits ?. length , 1 , 'first search should return one result' )
367
+ t . equal ( resultsB ?. hits ?. length , 10 , 'second search should return ten results' )
368
+ t . ok ( transaction , 'transaction should still be visible after search' )
369
+ const trace = transaction . trace
370
+ t . ok ( trace ?. root ?. children ?. [ 0 ] , 'trace, trace root, and first child should exist' )
371
+ const firstChild = trace . root . children [ 0 ]
372
+ t . match (
373
+ firstChild . name ,
374
+ 'timers.setTimeout' ,
375
+ 'helpers, for some reason, generates a setTimeout metric first'
376
+ )
377
+ transaction . end ( )
378
+ t . ok ( agent . queries . samples . size > 0 , 'there should be a query sample' )
379
+ for ( const query of agent . queries . samples . values ( ) ) {
380
+ // which query gets captured in helper.msearch is non-deterministic
381
+ t . ok ( query . total > 0 , 'the samples should have positive duration' )
382
+ }
383
+ } )
384
+ } )
385
+
307
386
t . test ( 'should create correct metrics' , async function ( t ) {
308
387
const id = `key-${ randomString ( ) } `
309
388
t . plan ( 28 )
@@ -417,6 +496,96 @@ test('Elasticsearch instrumentation', (t) => {
417
496
} )
418
497
} )
419
498
499
+ test ( 'Elasticsearch uninstrumented behavior, to check helpers' , { skip : false } , ( t ) => {
500
+ t . autoend ( )
501
+
502
+ let client
503
+ // eslint-disable-next-line no-unused-vars
504
+ let pkgVersion
505
+
506
+ t . before ( async ( ) => {
507
+ // Determine version. ElasticSearch v7 did not export package, so we have to read the file
508
+ // instead of requiring it, as we can with 8+.
509
+ const pkg = await readFile ( `${ __dirname } /node_modules/@elastic/elasticsearch/package.json` )
510
+ ; ( { version : pkgVersion } = JSON . parse ( pkg . toString ( ) ) )
511
+
512
+ const { Client } = require ( '@elastic/elasticsearch' )
513
+ client = new Client ( {
514
+ node : `http://${ params . elastic_host } :${ params . elastic_port } `
515
+ } )
516
+
517
+ return Promise . all ( [ client . indices . create ( { index : DB_INDEX_3 } ) ] )
518
+ } )
519
+
520
+ t . teardown ( ( ) => {
521
+ return Promise . all ( [ client . indices . delete ( { index : DB_INDEX_3 } ) ] )
522
+ } )
523
+
524
+ t . test ( 'should record bulk operations triggered by client helpers' , async ( t ) => {
525
+ const operations = [
526
+ {
527
+ title : 'Uninstrumented First Bulk Doc from helpers' ,
528
+ body : 'Content of uninstrumented first bulk document'
529
+ } ,
530
+ {
531
+ title : 'Uninstrumented Second Bulk Doc from helpers' ,
532
+ body : 'Content of uninstrumented second bulk document.'
533
+ } ,
534
+ {
535
+ title : 'Uninstrumented Third Bulk Doc from helpers' ,
536
+ body : 'Content of uninstrumented third bulk document.'
537
+ } ,
538
+ {
539
+ title : 'Uninstrumented Fourth Bulk Doc from helpers' ,
540
+ body : 'Content of uninstrumented fourth bulk document.'
541
+ } ,
542
+ {
543
+ title : 'Uninstrumented Fifth Bulk Doc from helpers' ,
544
+ body : 'Content of uninstrumented fifth bulk document'
545
+ } ,
546
+ {
547
+ title : 'Uninstrumented Sixth Bulk Doc from helpers' ,
548
+ body : 'Content of uninstrumented sixth bulk document.'
549
+ } ,
550
+ {
551
+ title : 'Uninstrumented Seventh Bulk Doc from helpers' ,
552
+ body : 'Content of uninstrumented seventh bulk document.'
553
+ } ,
554
+ {
555
+ title : 'Uninstrumented Eighth Bulk Doc from helpers' ,
556
+ body : 'Content of uninstrumented eighth bulk document.'
557
+ }
558
+ ]
559
+ const result = await client . helpers . bulk ( {
560
+ datasource : operations ,
561
+ onDocument ( ) {
562
+ return {
563
+ index : { _index : DB_INDEX_3 }
564
+ }
565
+ }
566
+ // refreshOnCompletion: true
567
+ } ) // setBulkBody(operations, pkgVersion)
568
+ t . ok ( result , 'We should have been able to create bulk entries without error' )
569
+ t . equal ( result . total , 8 , 'We should have been inserted eight records' )
570
+ } )
571
+ t . test ( 'should be able to check bulk insert with msearch via helpers' , async function ( t ) {
572
+ const m = client . helpers . msearch ( )
573
+ const searchA = await m . search ( { index : DB_INDEX_3 } , { query : { match : { body : 'sixth' } } } )
574
+ const searchB = await m . search (
575
+ { index : DB_INDEX_3 } ,
576
+ { query : { match : { body : 'uninstrumented' } } }
577
+ )
578
+ const resultsA = searchA ?. body ?. hits
579
+ const resultsB = searchB ?. body ?. hits
580
+
581
+ t . ok ( resultsA , 'msearch should return a response for A' )
582
+ t . ok ( resultsB , 'msearch should return results for B' )
583
+ // some versions of helper msearch seem not to return results for the first search.
584
+ // t.equal(resultsA?.hits?.length, 1, 'first search should return one result')
585
+ t . equal ( resultsB ?. hits ?. length , 8 , 'second search should return eight results' )
586
+ } )
587
+ } )
588
+
420
589
function checkMetrics ( t , metrics , expected ) {
421
590
Object . keys ( expected ) . forEach ( function ( name ) {
422
591
t . ok ( metrics [ name ] , 'should have metric ' + name )
0 commit comments