@expo/entity 0.42.0 → 0.44.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/build/AuthorizationResultBasedEntityMutator.d.ts +87 -2
- package/build/AuthorizationResultBasedEntityMutator.js +122 -8
- package/build/AuthorizationResultBasedEntityMutator.js.map +1 -1
- package/build/EntityFieldDefinition.d.ts +1 -1
- package/build/EntityFieldDefinition.js +1 -1
- package/build/EntityFieldDefinition.js.map +1 -1
- package/build/EntityLoaderUtils.d.ts +15 -3
- package/build/EntityLoaderUtils.js +25 -10
- package/build/EntityLoaderUtils.js.map +1 -1
- package/build/EntityQueryContext.d.ts +53 -7
- package/build/EntityQueryContext.js +65 -10
- package/build/EntityQueryContext.js.map +1 -1
- package/build/EntityQueryContextProvider.d.ts +5 -1
- package/build/EntityQueryContextProvider.js +11 -4
- package/build/EntityQueryContextProvider.js.map +1 -1
- package/build/IEntityGenericCacher.d.ts +2 -2
- package/build/internal/CompositeFieldHolder.d.ts +13 -0
- package/build/internal/CompositeFieldHolder.js +7 -0
- package/build/internal/CompositeFieldHolder.js.map +1 -1
- package/build/internal/CompositeFieldValueMap.d.ts +3 -0
- package/build/internal/CompositeFieldValueMap.js +3 -0
- package/build/internal/CompositeFieldValueMap.js.map +1 -1
- package/build/internal/EntityDataManager.d.ts +22 -3
- package/build/internal/EntityDataManager.js +99 -11
- package/build/internal/EntityDataManager.js.map +1 -1
- package/build/internal/EntityFieldTransformationUtils.d.ts +20 -0
- package/build/internal/EntityFieldTransformationUtils.js +15 -0
- package/build/internal/EntityFieldTransformationUtils.js.map +1 -1
- package/build/internal/EntityLoadInterfaces.d.ts +8 -0
- package/build/internal/EntityLoadInterfaces.js +2 -0
- package/build/internal/EntityLoadInterfaces.js.map +1 -1
- package/build/internal/EntityTableDataCoordinator.d.ts +2 -0
- package/build/internal/EntityTableDataCoordinator.js +2 -0
- package/build/internal/EntityTableDataCoordinator.js.map +1 -1
- package/build/internal/ReadThroughEntityCache.d.ts +8 -0
- package/build/internal/ReadThroughEntityCache.js +5 -0
- package/build/internal/ReadThroughEntityCache.js.map +1 -1
- package/build/internal/SingleFieldHolder.d.ts +7 -0
- package/build/internal/SingleFieldHolder.js +7 -0
- package/build/internal/SingleFieldHolder.js.map +1 -1
- package/build/metrics/EntityMetricsUtils.d.ts +4 -3
- package/build/metrics/EntityMetricsUtils.js +6 -3
- package/build/metrics/EntityMetricsUtils.js.map +1 -1
- package/build/metrics/IEntityMetricsAdapter.d.ts +21 -0
- package/build/metrics/IEntityMetricsAdapter.js.map +1 -1
- package/package.json +13 -13
- package/src/AuthorizationResultBasedEntityMutator.ts +133 -15
- package/src/EntityFieldDefinition.ts +1 -1
- package/src/EntityLoaderUtils.ts +43 -12
- package/src/EntityQueryContext.ts +68 -13
- package/src/EntityQueryContextProvider.ts +20 -3
- package/src/IEntityGenericCacher.ts +2 -2
- package/src/__tests__/AuthorizationResultBasedEntityLoader-test.ts +98 -0
- package/src/__tests__/EntityQueryContext-test.ts +141 -26
- package/src/internal/CompositeFieldHolder.ts +15 -0
- package/src/internal/CompositeFieldValueMap.ts +3 -0
- package/src/internal/EntityDataManager.ts +170 -10
- package/src/internal/EntityFieldTransformationUtils.ts +20 -0
- package/src/internal/EntityLoadInterfaces.ts +8 -0
- package/src/internal/EntityTableDataCoordinator.ts +2 -0
- package/src/internal/ReadThroughEntityCache.ts +8 -0
- package/src/internal/SingleFieldHolder.ts +7 -0
- package/src/internal/__tests__/EntityDataManager-test.ts +708 -186
- package/src/metrics/EntityMetricsUtils.ts +7 -0
- package/src/metrics/IEntityMetricsAdapter.ts +27 -0
- package/src/utils/__testfixtures__/StubDatabaseAdapter.ts +13 -1
|
@@ -4,14 +4,15 @@ import {
|
|
|
4
4
|
when,
|
|
5
5
|
anything,
|
|
6
6
|
verify,
|
|
7
|
-
objectContaining,
|
|
8
7
|
spy,
|
|
9
8
|
anyString,
|
|
10
9
|
resetCalls,
|
|
11
10
|
deepEqual,
|
|
11
|
+
anyNumber,
|
|
12
12
|
} from 'ts-mockito';
|
|
13
13
|
|
|
14
14
|
import EntityDatabaseAdapter from '../../EntityDatabaseAdapter';
|
|
15
|
+
import { TransactionalDataLoaderMode } from '../../EntityQueryContext';
|
|
15
16
|
import IEntityMetricsAdapter, {
|
|
16
17
|
EntityMetricsLoadType,
|
|
17
18
|
IncrementLoadCountEventType,
|
|
@@ -310,6 +311,237 @@ describe(EntityDataManager, () => {
|
|
|
310
311
|
cacheSpy.mockReset();
|
|
311
312
|
});
|
|
312
313
|
|
|
314
|
+
it('loads and in-memory batches (dataloader) loads in transaction when enabled with TransactionalDataLoaderMode.ENABLED_BATCH_ONLY and does not read from cache for transactions and nested transactions', async () => {
|
|
315
|
+
const objects = getObjects();
|
|
316
|
+
const dataStore = StubDatabaseAdapter.convertFieldObjectsToDataStore(
|
|
317
|
+
testEntityConfiguration,
|
|
318
|
+
objects,
|
|
319
|
+
);
|
|
320
|
+
const databaseAdapter = new StubDatabaseAdapter<TestFields, 'customIdField'>(
|
|
321
|
+
testEntityConfiguration,
|
|
322
|
+
dataStore,
|
|
323
|
+
);
|
|
324
|
+
const cacheAdapterProvider = new InMemoryFullCacheStubCacheAdapterProvider();
|
|
325
|
+
const cacheAdapter = cacheAdapterProvider.getCacheAdapter(testEntityConfiguration);
|
|
326
|
+
const entityCache = new ReadThroughEntityCache(testEntityConfiguration, cacheAdapter);
|
|
327
|
+
const entityDataManager = new EntityDataManager(
|
|
328
|
+
databaseAdapter,
|
|
329
|
+
entityCache,
|
|
330
|
+
new StubQueryContextProvider(),
|
|
331
|
+
new NoOpEntityMetricsAdapter(),
|
|
332
|
+
TestEntity.name,
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
const dbSpy = jest.spyOn(databaseAdapter, 'fetchManyWhereAsync');
|
|
336
|
+
const cacheSpy = jest.spyOn(entityCache, 'readManyThroughAsync');
|
|
337
|
+
|
|
338
|
+
const [entityData, entityData2, entityData3, entityData4] =
|
|
339
|
+
await new StubQueryContextProvider().runInTransactionAsync(async (queryContext) => {
|
|
340
|
+
const [entityData, entityData2] = await Promise.all([
|
|
341
|
+
entityDataManager.loadManyEqualingAsync(
|
|
342
|
+
queryContext,
|
|
343
|
+
new SingleFieldHolder('stringField'),
|
|
344
|
+
[new SingleFieldValueHolder('hello'), new SingleFieldValueHolder('world')],
|
|
345
|
+
),
|
|
346
|
+
entityDataManager.loadManyEqualingAsync(
|
|
347
|
+
queryContext,
|
|
348
|
+
new SingleFieldHolder('stringField'),
|
|
349
|
+
[new SingleFieldValueHolder('hello'), new SingleFieldValueHolder('world')],
|
|
350
|
+
),
|
|
351
|
+
]);
|
|
352
|
+
const [entityData3, entityData4] = await queryContext.runInNestedTransactionAsync(
|
|
353
|
+
async (innerQueryContext) => {
|
|
354
|
+
const entityData3 = await entityDataManager.loadManyEqualingAsync(
|
|
355
|
+
innerQueryContext,
|
|
356
|
+
new SingleFieldHolder('stringField'),
|
|
357
|
+
[new SingleFieldValueHolder('hello'), new SingleFieldValueHolder('world')],
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
const entityData4 = await queryContext.runInNestedTransactionAsync(
|
|
361
|
+
async (innerInnerQueryContext) => {
|
|
362
|
+
return await entityDataManager.loadManyEqualingAsync(
|
|
363
|
+
innerInnerQueryContext,
|
|
364
|
+
new SingleFieldHolder('stringField'),
|
|
365
|
+
[new SingleFieldValueHolder('hello'), new SingleFieldValueHolder('world')],
|
|
366
|
+
);
|
|
367
|
+
},
|
|
368
|
+
);
|
|
369
|
+
|
|
370
|
+
return [entityData3, entityData4];
|
|
371
|
+
},
|
|
372
|
+
);
|
|
373
|
+
return [entityData, entityData2, entityData3, entityData4];
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
// entityData, entityData3 (new nested transaction), and entityData4 (new nested transaction) loads should all need to call the database
|
|
377
|
+
// entityData and entityData2 should be batched to one db load call
|
|
378
|
+
expect(dbSpy).toHaveBeenCalledTimes(3);
|
|
379
|
+
expect(cacheSpy).toHaveBeenCalledTimes(0);
|
|
380
|
+
|
|
381
|
+
expect(entityData).toMatchObject(entityData2);
|
|
382
|
+
expect(entityData2).toMatchObject(entityData3);
|
|
383
|
+
expect(entityData3).toMatchObject(entityData4);
|
|
384
|
+
expect(entityData.get(new SingleFieldValueHolder('hello'))).toHaveLength(2);
|
|
385
|
+
expect(entityData.get(new SingleFieldValueHolder('world'))).toHaveLength(1);
|
|
386
|
+
|
|
387
|
+
dbSpy.mockReset();
|
|
388
|
+
cacheSpy.mockReset();
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
it('loads and in-memory caches (dataloader) loads in transaction when enabled with TransactionalDataLoaderMode.ENABLED and does not read from cache for transactions and nested transactions', async () => {
|
|
392
|
+
const objects = getObjects();
|
|
393
|
+
const dataStore = StubDatabaseAdapter.convertFieldObjectsToDataStore(
|
|
394
|
+
testEntityConfiguration,
|
|
395
|
+
objects,
|
|
396
|
+
);
|
|
397
|
+
const databaseAdapter = new StubDatabaseAdapter<TestFields, 'customIdField'>(
|
|
398
|
+
testEntityConfiguration,
|
|
399
|
+
dataStore,
|
|
400
|
+
);
|
|
401
|
+
const cacheAdapterProvider = new InMemoryFullCacheStubCacheAdapterProvider();
|
|
402
|
+
const cacheAdapter = cacheAdapterProvider.getCacheAdapter(testEntityConfiguration);
|
|
403
|
+
const entityCache = new ReadThroughEntityCache(testEntityConfiguration, cacheAdapter);
|
|
404
|
+
const entityDataManager = new EntityDataManager(
|
|
405
|
+
databaseAdapter,
|
|
406
|
+
entityCache,
|
|
407
|
+
new StubQueryContextProvider(),
|
|
408
|
+
new NoOpEntityMetricsAdapter(),
|
|
409
|
+
TestEntity.name,
|
|
410
|
+
);
|
|
411
|
+
|
|
412
|
+
const dbSpy = jest.spyOn(databaseAdapter, 'fetchManyWhereAsync');
|
|
413
|
+
const cacheSpy = jest.spyOn(entityCache, 'readManyThroughAsync');
|
|
414
|
+
|
|
415
|
+
const [entityData, entityData2, entityData3, entityData4] =
|
|
416
|
+
await new StubQueryContextProvider().runInTransactionAsync(async (queryContext) => {
|
|
417
|
+
const entityData = await entityDataManager.loadManyEqualingAsync(
|
|
418
|
+
queryContext,
|
|
419
|
+
new SingleFieldHolder('stringField'),
|
|
420
|
+
[new SingleFieldValueHolder('hello'), new SingleFieldValueHolder('world')],
|
|
421
|
+
);
|
|
422
|
+
const entityData2 = await entityDataManager.loadManyEqualingAsync(
|
|
423
|
+
queryContext,
|
|
424
|
+
new SingleFieldHolder('stringField'),
|
|
425
|
+
[new SingleFieldValueHolder('hello'), new SingleFieldValueHolder('world')],
|
|
426
|
+
);
|
|
427
|
+
const [entityData3, entityData4] = await queryContext.runInNestedTransactionAsync(
|
|
428
|
+
async (innerQueryContext) => {
|
|
429
|
+
const entityData3 = await entityDataManager.loadManyEqualingAsync(
|
|
430
|
+
innerQueryContext,
|
|
431
|
+
new SingleFieldHolder('stringField'),
|
|
432
|
+
[new SingleFieldValueHolder('hello'), new SingleFieldValueHolder('world')],
|
|
433
|
+
);
|
|
434
|
+
|
|
435
|
+
const entityData4 = await queryContext.runInNestedTransactionAsync(
|
|
436
|
+
async (innerInnerQueryContext) => {
|
|
437
|
+
return await entityDataManager.loadManyEqualingAsync(
|
|
438
|
+
innerInnerQueryContext,
|
|
439
|
+
new SingleFieldHolder('stringField'),
|
|
440
|
+
[new SingleFieldValueHolder('hello'), new SingleFieldValueHolder('world')],
|
|
441
|
+
);
|
|
442
|
+
},
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
return [entityData3, entityData4];
|
|
446
|
+
},
|
|
447
|
+
);
|
|
448
|
+
return [entityData, entityData2, entityData3, entityData4];
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
// entityData, entityData3 (new nested transaction), and entityData4 (new nested transaction) loads should all need to call the database
|
|
452
|
+
// entityData2 load should be cached in the dataloader
|
|
453
|
+
expect(dbSpy).toHaveBeenCalledTimes(3);
|
|
454
|
+
expect(cacheSpy).toHaveBeenCalledTimes(0);
|
|
455
|
+
|
|
456
|
+
expect(entityData).toMatchObject(entityData2);
|
|
457
|
+
expect(entityData2).toMatchObject(entityData3);
|
|
458
|
+
expect(entityData3).toMatchObject(entityData4);
|
|
459
|
+
expect(entityData.get(new SingleFieldValueHolder('hello'))).toHaveLength(2);
|
|
460
|
+
expect(entityData.get(new SingleFieldValueHolder('world'))).toHaveLength(1);
|
|
461
|
+
|
|
462
|
+
dbSpy.mockReset();
|
|
463
|
+
cacheSpy.mockReset();
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
it('loads and does not in-memory cache (dataloader) loads in transaction when disabled', async () => {
|
|
467
|
+
const objects = getObjects();
|
|
468
|
+
const dataStore = StubDatabaseAdapter.convertFieldObjectsToDataStore(
|
|
469
|
+
testEntityConfiguration,
|
|
470
|
+
objects,
|
|
471
|
+
);
|
|
472
|
+
const databaseAdapter = new StubDatabaseAdapter<TestFields, 'customIdField'>(
|
|
473
|
+
testEntityConfiguration,
|
|
474
|
+
dataStore,
|
|
475
|
+
);
|
|
476
|
+
const cacheAdapterProvider = new InMemoryFullCacheStubCacheAdapterProvider();
|
|
477
|
+
const cacheAdapter = cacheAdapterProvider.getCacheAdapter(testEntityConfiguration);
|
|
478
|
+
const entityCache = new ReadThroughEntityCache(testEntityConfiguration, cacheAdapter);
|
|
479
|
+
const entityDataManager = new EntityDataManager(
|
|
480
|
+
databaseAdapter,
|
|
481
|
+
entityCache,
|
|
482
|
+
new StubQueryContextProvider(),
|
|
483
|
+
new NoOpEntityMetricsAdapter(),
|
|
484
|
+
TestEntity.name,
|
|
485
|
+
);
|
|
486
|
+
|
|
487
|
+
const dbSpy = jest.spyOn(databaseAdapter, 'fetchManyWhereAsync');
|
|
488
|
+
const cacheSpy = jest.spyOn(entityCache, 'readManyThroughAsync');
|
|
489
|
+
|
|
490
|
+
const [entityData, entityData2, entityData3, entityData4] =
|
|
491
|
+
await new StubQueryContextProvider().runInTransactionAsync(
|
|
492
|
+
async (queryContext) => {
|
|
493
|
+
const entityData = await entityDataManager.loadManyEqualingAsync(
|
|
494
|
+
queryContext,
|
|
495
|
+
new SingleFieldHolder('stringField'),
|
|
496
|
+
[new SingleFieldValueHolder('hello'), new SingleFieldValueHolder('world')],
|
|
497
|
+
);
|
|
498
|
+
const entityData2 = await entityDataManager.loadManyEqualingAsync(
|
|
499
|
+
queryContext,
|
|
500
|
+
new SingleFieldHolder('stringField'),
|
|
501
|
+
[new SingleFieldValueHolder('hello'), new SingleFieldValueHolder('world')],
|
|
502
|
+
);
|
|
503
|
+
const [entityData3, entityData4] = await queryContext.runInNestedTransactionAsync(
|
|
504
|
+
async (innerQueryContext) => {
|
|
505
|
+
const entityData3 = await entityDataManager.loadManyEqualingAsync(
|
|
506
|
+
innerQueryContext,
|
|
507
|
+
new SingleFieldHolder('stringField'),
|
|
508
|
+
[new SingleFieldValueHolder('hello'), new SingleFieldValueHolder('world')],
|
|
509
|
+
);
|
|
510
|
+
|
|
511
|
+
const entityData4 = await queryContext.runInNestedTransactionAsync(
|
|
512
|
+
async (innerInnerQueryContext) => {
|
|
513
|
+
return await entityDataManager.loadManyEqualingAsync(
|
|
514
|
+
innerInnerQueryContext,
|
|
515
|
+
new SingleFieldHolder('stringField'),
|
|
516
|
+
[new SingleFieldValueHolder('hello'), new SingleFieldValueHolder('world')],
|
|
517
|
+
);
|
|
518
|
+
},
|
|
519
|
+
);
|
|
520
|
+
|
|
521
|
+
return [entityData3, entityData4];
|
|
522
|
+
},
|
|
523
|
+
);
|
|
524
|
+
return [entityData, entityData2, entityData3, entityData4];
|
|
525
|
+
},
|
|
526
|
+
{
|
|
527
|
+
transactionalDataLoaderMode: TransactionalDataLoaderMode.DISABLED,
|
|
528
|
+
},
|
|
529
|
+
);
|
|
530
|
+
|
|
531
|
+
// entityData, entityData2, entityData3 (new nested transaction), and entityData4 (new nested transaction) loads should all need to call the database
|
|
532
|
+
expect(dbSpy).toHaveBeenCalledTimes(4);
|
|
533
|
+
expect(cacheSpy).toHaveBeenCalledTimes(0);
|
|
534
|
+
|
|
535
|
+
expect(entityData).toMatchObject(entityData2);
|
|
536
|
+
expect(entityData2).toMatchObject(entityData3);
|
|
537
|
+
expect(entityData3).toMatchObject(entityData4);
|
|
538
|
+
expect(entityData.get(new SingleFieldValueHolder('hello'))).toHaveLength(2);
|
|
539
|
+
expect(entityData.get(new SingleFieldValueHolder('world'))).toHaveLength(1);
|
|
540
|
+
|
|
541
|
+
dbSpy.mockReset();
|
|
542
|
+
cacheSpy.mockReset();
|
|
543
|
+
});
|
|
544
|
+
|
|
313
545
|
it('invalidates objects', async () => {
|
|
314
546
|
const objects = getObjects();
|
|
315
547
|
const dataStore = StubDatabaseAdapter.convertFieldObjectsToDataStore(
|
|
@@ -412,7 +644,141 @@ describe(EntityDataManager, () => {
|
|
|
412
644
|
cacheSpy.mockReset();
|
|
413
645
|
});
|
|
414
646
|
|
|
415
|
-
it('
|
|
647
|
+
it('invalidates transactions and nested transactions correctly', async () => {
|
|
648
|
+
const objects = getObjects();
|
|
649
|
+
const dataStore = StubDatabaseAdapter.convertFieldObjectsToDataStore(
|
|
650
|
+
testEntityConfiguration,
|
|
651
|
+
objects,
|
|
652
|
+
);
|
|
653
|
+
const databaseAdapter = new StubDatabaseAdapter<TestFields, 'customIdField'>(
|
|
654
|
+
testEntityConfiguration,
|
|
655
|
+
dataStore,
|
|
656
|
+
);
|
|
657
|
+
const cacheAdapterProvider = new InMemoryFullCacheStubCacheAdapterProvider();
|
|
658
|
+
const cacheAdapter = cacheAdapterProvider.getCacheAdapter(testEntityConfiguration);
|
|
659
|
+
const entityCache = new ReadThroughEntityCache(testEntityConfiguration, cacheAdapter);
|
|
660
|
+
const entityDataManager = new EntityDataManager(
|
|
661
|
+
databaseAdapter,
|
|
662
|
+
entityCache,
|
|
663
|
+
new StubQueryContextProvider(),
|
|
664
|
+
new NoOpEntityMetricsAdapter(),
|
|
665
|
+
TestEntity.name,
|
|
666
|
+
);
|
|
667
|
+
|
|
668
|
+
await new StubQueryContextProvider().runInTransactionAsync(async (queryContext) => {
|
|
669
|
+
const objectInQuestion = objects.get(testEntityConfiguration.tableName)![1]!;
|
|
670
|
+
|
|
671
|
+
const dbSpy = jest.spyOn(databaseAdapter, 'fetchManyWhereAsync');
|
|
672
|
+
const cacheSpy = jest.spyOn(entityCache, 'readManyThroughAsync');
|
|
673
|
+
|
|
674
|
+
await entityDataManager.loadManyEqualingAsync(
|
|
675
|
+
queryContext,
|
|
676
|
+
new SingleFieldHolder('testIndexedField'),
|
|
677
|
+
[new SingleFieldValueHolder(objectInQuestion['testIndexedField'])],
|
|
678
|
+
);
|
|
679
|
+
entityDataManager.invalidateKeyValuePairsForTransaction(queryContext, [
|
|
680
|
+
[
|
|
681
|
+
new SingleFieldHolder('testIndexedField'),
|
|
682
|
+
new SingleFieldValueHolder(objectInQuestion['testIndexedField']),
|
|
683
|
+
],
|
|
684
|
+
]);
|
|
685
|
+
await entityDataManager.loadManyEqualingAsync(
|
|
686
|
+
queryContext,
|
|
687
|
+
new SingleFieldHolder('testIndexedField'),
|
|
688
|
+
[new SingleFieldValueHolder(objectInQuestion['testIndexedField'])],
|
|
689
|
+
);
|
|
690
|
+
|
|
691
|
+
expect(dbSpy).toHaveBeenCalledTimes(2);
|
|
692
|
+
expect(cacheSpy).toHaveBeenCalledTimes(0);
|
|
693
|
+
|
|
694
|
+
dbSpy.mockClear();
|
|
695
|
+
cacheSpy.mockClear();
|
|
696
|
+
|
|
697
|
+
await queryContext.runInNestedTransactionAsync(async (innerQueryContext) => {
|
|
698
|
+
await entityDataManager.loadManyEqualingAsync(
|
|
699
|
+
innerQueryContext,
|
|
700
|
+
new SingleFieldHolder('testIndexedField'),
|
|
701
|
+
[new SingleFieldValueHolder(objectInQuestion['testIndexedField'])],
|
|
702
|
+
);
|
|
703
|
+
entityDataManager.invalidateKeyValuePairsForTransaction(innerQueryContext, [
|
|
704
|
+
[
|
|
705
|
+
new SingleFieldHolder('testIndexedField'),
|
|
706
|
+
new SingleFieldValueHolder(objectInQuestion['testIndexedField']),
|
|
707
|
+
],
|
|
708
|
+
]);
|
|
709
|
+
await entityDataManager.loadManyEqualingAsync(
|
|
710
|
+
innerQueryContext,
|
|
711
|
+
new SingleFieldHolder('testIndexedField'),
|
|
712
|
+
[new SingleFieldValueHolder(objectInQuestion['testIndexedField'])],
|
|
713
|
+
);
|
|
714
|
+
|
|
715
|
+
expect(dbSpy).toHaveBeenCalledTimes(2);
|
|
716
|
+
expect(cacheSpy).toHaveBeenCalledTimes(0);
|
|
717
|
+
|
|
718
|
+
dbSpy.mockClear();
|
|
719
|
+
cacheSpy.mockClear();
|
|
720
|
+
});
|
|
721
|
+
});
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
it('does not use transactional dataloader when disabled', async () => {
|
|
725
|
+
const objects = getObjects();
|
|
726
|
+
const dataStore = StubDatabaseAdapter.convertFieldObjectsToDataStore(
|
|
727
|
+
testEntityConfiguration,
|
|
728
|
+
objects,
|
|
729
|
+
);
|
|
730
|
+
const databaseAdapter = new StubDatabaseAdapter<TestFields, 'customIdField'>(
|
|
731
|
+
testEntityConfiguration,
|
|
732
|
+
dataStore,
|
|
733
|
+
);
|
|
734
|
+
const cacheAdapterProvider = new InMemoryFullCacheStubCacheAdapterProvider();
|
|
735
|
+
const cacheAdapter = cacheAdapterProvider.getCacheAdapter(testEntityConfiguration);
|
|
736
|
+
const entityCache = new ReadThroughEntityCache(testEntityConfiguration, cacheAdapter);
|
|
737
|
+
const entityDataManager = new EntityDataManager(
|
|
738
|
+
databaseAdapter,
|
|
739
|
+
entityCache,
|
|
740
|
+
new StubQueryContextProvider(),
|
|
741
|
+
new NoOpEntityMetricsAdapter(),
|
|
742
|
+
TestEntity.name,
|
|
743
|
+
);
|
|
744
|
+
|
|
745
|
+
await new StubQueryContextProvider().runInTransactionAsync(
|
|
746
|
+
async (queryContext) => {
|
|
747
|
+
const objectInQuestion = objects.get(testEntityConfiguration.tableName)![1]!;
|
|
748
|
+
|
|
749
|
+
const dbSpy = jest.spyOn(databaseAdapter, 'fetchManyWhereAsync');
|
|
750
|
+
const cacheSpy = jest.spyOn(entityCache, 'readManyThroughAsync');
|
|
751
|
+
|
|
752
|
+
await entityDataManager.loadManyEqualingAsync(
|
|
753
|
+
queryContext,
|
|
754
|
+
new SingleFieldHolder('testIndexedField'),
|
|
755
|
+
[new SingleFieldValueHolder(objectInQuestion['testIndexedField'])],
|
|
756
|
+
);
|
|
757
|
+
entityDataManager.invalidateKeyValuePairsForTransaction(queryContext, [
|
|
758
|
+
[
|
|
759
|
+
new SingleFieldHolder('testIndexedField'),
|
|
760
|
+
new SingleFieldValueHolder(objectInQuestion['testIndexedField']),
|
|
761
|
+
],
|
|
762
|
+
]);
|
|
763
|
+
await entityDataManager.loadManyEqualingAsync(
|
|
764
|
+
queryContext,
|
|
765
|
+
new SingleFieldHolder('testIndexedField'),
|
|
766
|
+
[new SingleFieldValueHolder(objectInQuestion['testIndexedField'])],
|
|
767
|
+
);
|
|
768
|
+
|
|
769
|
+
expect(dbSpy).toHaveBeenCalledTimes(2);
|
|
770
|
+
expect(cacheSpy).toHaveBeenCalledTimes(0);
|
|
771
|
+
|
|
772
|
+
dbSpy.mockClear();
|
|
773
|
+
cacheSpy.mockClear();
|
|
774
|
+
},
|
|
775
|
+
{ transactionalDataLoaderMode: TransactionalDataLoaderMode.DISABLED },
|
|
776
|
+
);
|
|
777
|
+
|
|
778
|
+
expect(entityDataManager['transactionalDataLoaders'].size).toBe(0);
|
|
779
|
+
});
|
|
780
|
+
|
|
781
|
+
it('does not load from cache when in transaction', async () => {
|
|
416
782
|
const objects = getObjects();
|
|
417
783
|
const dataStore = StubDatabaseAdapter.convertFieldObjectsToDataStore(
|
|
418
784
|
testEntityConfiguration,
|
|
@@ -533,194 +899,350 @@ describe(EntityDataManager, () => {
|
|
|
533
899
|
).rejects.toThrow();
|
|
534
900
|
});
|
|
535
901
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
queryContext,
|
|
615
|
-
new SingleFieldHolder('testIndexedField'),
|
|
616
|
-
[new SingleFieldValueHolder('unique1')],
|
|
617
|
-
);
|
|
618
|
-
verify(metricsAdapterMock.incrementDataManagerLoadCount(anything())).once();
|
|
619
|
-
verify(
|
|
620
|
-
metricsAdapterMock.incrementDataManagerLoadCount(
|
|
621
|
-
deepEqual({
|
|
622
|
-
type: IncrementLoadCountEventType.DATALOADER,
|
|
623
|
-
fieldValueCount: 1,
|
|
624
|
-
entityClassName: TestEntity.name,
|
|
625
|
-
loadType: EntityLoadMethodType.SINGLE,
|
|
626
|
-
}),
|
|
627
|
-
),
|
|
628
|
-
).once();
|
|
629
|
-
|
|
630
|
-
resetCalls(metricsAdapterMock);
|
|
631
|
-
|
|
632
|
-
// make third call in new data manager but query two keys, ensure only one of the keys is fetched
|
|
633
|
-
// from the database and the other is fetched from the cache
|
|
634
|
-
const entityCache2 = new ReadThroughEntityCache(testEntityConfiguration, cacheAdapter);
|
|
635
|
-
const entityDataManager2 = new EntityDataManager(
|
|
636
|
-
databaseAdapter,
|
|
637
|
-
entityCache2,
|
|
638
|
-
new StubQueryContextProvider(),
|
|
639
|
-
metricsAdapter,
|
|
640
|
-
TestEntity.name,
|
|
641
|
-
);
|
|
642
|
-
await entityDataManager2.loadManyEqualingAsync(
|
|
643
|
-
queryContext,
|
|
644
|
-
new SingleFieldHolder('testIndexedField'),
|
|
645
|
-
[new SingleFieldValueHolder('unique1'), new SingleFieldValueHolder('unique2')],
|
|
646
|
-
);
|
|
647
|
-
verify(metricsAdapterMock.incrementDataManagerLoadCount(anything())).thrice();
|
|
648
|
-
verify(
|
|
649
|
-
metricsAdapterMock.incrementDataManagerLoadCount(
|
|
650
|
-
deepEqual({
|
|
651
|
-
type: IncrementLoadCountEventType.DATALOADER,
|
|
652
|
-
fieldValueCount: 2,
|
|
653
|
-
entityClassName: TestEntity.name,
|
|
654
|
-
loadType: EntityLoadMethodType.SINGLE,
|
|
655
|
-
}),
|
|
656
|
-
),
|
|
657
|
-
).once();
|
|
658
|
-
verify(
|
|
659
|
-
metricsAdapterMock.incrementDataManagerLoadCount(
|
|
660
|
-
deepEqual({
|
|
661
|
-
type: IncrementLoadCountEventType.CACHE,
|
|
662
|
-
fieldValueCount: 2,
|
|
663
|
-
entityClassName: TestEntity.name,
|
|
664
|
-
loadType: EntityLoadMethodType.SINGLE,
|
|
665
|
-
}),
|
|
666
|
-
),
|
|
667
|
-
).once();
|
|
668
|
-
verify(
|
|
669
|
-
metricsAdapterMock.incrementDataManagerLoadCount(
|
|
670
|
-
deepEqual({
|
|
671
|
-
type: IncrementLoadCountEventType.DATABASE,
|
|
672
|
-
fieldValueCount: 1,
|
|
673
|
-
entityClassName: TestEntity.name,
|
|
674
|
-
loadType: EntityLoadMethodType.SINGLE,
|
|
675
|
-
}),
|
|
676
|
-
),
|
|
677
|
-
).once();
|
|
678
|
-
|
|
679
|
-
resetCalls(metricsAdapterMock);
|
|
902
|
+
describe('metrics', () => {
|
|
903
|
+
it('records metrics appropriately outside of transactions', async () => {
|
|
904
|
+
const metricsAdapterMock = mock<IEntityMetricsAdapter>();
|
|
905
|
+
const metricsAdapter = instance(metricsAdapterMock);
|
|
906
|
+
|
|
907
|
+
const objects = getObjects();
|
|
908
|
+
const dataStore = StubDatabaseAdapter.convertFieldObjectsToDataStore(
|
|
909
|
+
testEntityConfiguration,
|
|
910
|
+
objects,
|
|
911
|
+
);
|
|
912
|
+
const databaseAdapter = new StubDatabaseAdapter<TestFields, 'customIdField'>(
|
|
913
|
+
testEntityConfiguration,
|
|
914
|
+
dataStore,
|
|
915
|
+
);
|
|
916
|
+
const cacheAdapterProvider = new InMemoryFullCacheStubCacheAdapterProvider();
|
|
917
|
+
const cacheAdapter = cacheAdapterProvider.getCacheAdapter(testEntityConfiguration);
|
|
918
|
+
const entityCache = new ReadThroughEntityCache(testEntityConfiguration, cacheAdapter);
|
|
919
|
+
const entityDataManager = new EntityDataManager(
|
|
920
|
+
databaseAdapter,
|
|
921
|
+
entityCache,
|
|
922
|
+
new StubQueryContextProvider(),
|
|
923
|
+
metricsAdapter,
|
|
924
|
+
TestEntity.name,
|
|
925
|
+
);
|
|
926
|
+
const queryContext = new StubQueryContextProvider().getQueryContext();
|
|
927
|
+
|
|
928
|
+
// make call to loadManyByFieldEqualingAsync to populate cache and dataloader, ensure metrics are recorded
|
|
929
|
+
// for dataloader, cache, and database
|
|
930
|
+
await entityDataManager.loadManyEqualingAsync(
|
|
931
|
+
queryContext,
|
|
932
|
+
new SingleFieldHolder('testIndexedField'),
|
|
933
|
+
[new SingleFieldValueHolder('unique1')],
|
|
934
|
+
);
|
|
935
|
+
verify(
|
|
936
|
+
metricsAdapterMock.logDataManagerLoadEvent(
|
|
937
|
+
deepEqual({
|
|
938
|
+
type: EntityMetricsLoadType.LOAD_MANY,
|
|
939
|
+
isInTransaction: false,
|
|
940
|
+
entityClassName: TestEntity.name,
|
|
941
|
+
duration: anyNumber(),
|
|
942
|
+
count: 1,
|
|
943
|
+
}),
|
|
944
|
+
),
|
|
945
|
+
).once();
|
|
946
|
+
verify(metricsAdapterMock.incrementDataManagerLoadCount(anything())).thrice();
|
|
947
|
+
verify(
|
|
948
|
+
metricsAdapterMock.incrementDataManagerLoadCount(
|
|
949
|
+
deepEqual({
|
|
950
|
+
type: IncrementLoadCountEventType.DATALOADER,
|
|
951
|
+
isInTransaction: false,
|
|
952
|
+
fieldValueCount: 1,
|
|
953
|
+
entityClassName: TestEntity.name,
|
|
954
|
+
loadType: EntityLoadMethodType.SINGLE,
|
|
955
|
+
}),
|
|
956
|
+
),
|
|
957
|
+
).once();
|
|
958
|
+
verify(
|
|
959
|
+
metricsAdapterMock.incrementDataManagerLoadCount(
|
|
960
|
+
deepEqual({
|
|
961
|
+
type: IncrementLoadCountEventType.CACHE,
|
|
962
|
+
isInTransaction: false,
|
|
963
|
+
fieldValueCount: 1,
|
|
964
|
+
entityClassName: TestEntity.name,
|
|
965
|
+
loadType: EntityLoadMethodType.SINGLE,
|
|
966
|
+
}),
|
|
967
|
+
),
|
|
968
|
+
).once();
|
|
969
|
+
verify(
|
|
970
|
+
metricsAdapterMock.incrementDataManagerLoadCount(
|
|
971
|
+
deepEqual({
|
|
972
|
+
type: IncrementLoadCountEventType.DATABASE,
|
|
973
|
+
isInTransaction: false,
|
|
974
|
+
fieldValueCount: 1,
|
|
975
|
+
entityClassName: TestEntity.name,
|
|
976
|
+
loadType: EntityLoadMethodType.SINGLE,
|
|
977
|
+
}),
|
|
978
|
+
),
|
|
979
|
+
).once();
|
|
680
980
|
|
|
681
|
-
|
|
682
|
-
queryContext,
|
|
683
|
-
[
|
|
684
|
-
{
|
|
685
|
-
fieldName: 'testIndexedField',
|
|
686
|
-
fieldValue: 'unique1',
|
|
687
|
-
},
|
|
688
|
-
],
|
|
689
|
-
{},
|
|
690
|
-
);
|
|
691
|
-
verify(
|
|
692
|
-
metricsAdapterMock.logDataManagerLoadEvent(
|
|
693
|
-
objectContaining({
|
|
694
|
-
type: EntityMetricsLoadType.LOAD_MANY_EQUALITY_CONJUNCTION,
|
|
695
|
-
entityClassName: TestEntity.name,
|
|
696
|
-
count: 1,
|
|
697
|
-
}),
|
|
698
|
-
),
|
|
699
|
-
).once();
|
|
981
|
+
resetCalls(metricsAdapterMock);
|
|
700
982
|
|
|
701
|
-
|
|
983
|
+
// make second call to loadManyByFieldEqualingAsync, ensure metrics are only recorded for dataloader since
|
|
984
|
+
// entity is in local dataloader
|
|
985
|
+
await entityDataManager.loadManyEqualingAsync(
|
|
986
|
+
queryContext,
|
|
987
|
+
new SingleFieldHolder('testIndexedField'),
|
|
988
|
+
[new SingleFieldValueHolder('unique1')],
|
|
989
|
+
);
|
|
990
|
+
verify(metricsAdapterMock.incrementDataManagerLoadCount(anything())).once();
|
|
991
|
+
verify(
|
|
992
|
+
metricsAdapterMock.incrementDataManagerLoadCount(
|
|
993
|
+
deepEqual({
|
|
994
|
+
type: IncrementLoadCountEventType.DATALOADER,
|
|
995
|
+
isInTransaction: false,
|
|
996
|
+
fieldValueCount: 1,
|
|
997
|
+
entityClassName: TestEntity.name,
|
|
998
|
+
loadType: EntityLoadMethodType.SINGLE,
|
|
999
|
+
}),
|
|
1000
|
+
),
|
|
1001
|
+
).once();
|
|
1002
|
+
|
|
1003
|
+
resetCalls(metricsAdapterMock);
|
|
1004
|
+
|
|
1005
|
+
// make third call in new data manager but query two keys, ensure only one of the keys is fetched
|
|
1006
|
+
// from the database and the other is fetched from the cache
|
|
1007
|
+
const entityCache2 = new ReadThroughEntityCache(testEntityConfiguration, cacheAdapter);
|
|
1008
|
+
const entityDataManager2 = new EntityDataManager(
|
|
1009
|
+
databaseAdapter,
|
|
1010
|
+
entityCache2,
|
|
1011
|
+
new StubQueryContextProvider(),
|
|
1012
|
+
metricsAdapter,
|
|
1013
|
+
TestEntity.name,
|
|
1014
|
+
);
|
|
1015
|
+
await entityDataManager2.loadManyEqualingAsync(
|
|
1016
|
+
queryContext,
|
|
1017
|
+
new SingleFieldHolder('testIndexedField'),
|
|
1018
|
+
[new SingleFieldValueHolder('unique1'), new SingleFieldValueHolder('unique2')],
|
|
1019
|
+
);
|
|
1020
|
+
verify(metricsAdapterMock.incrementDataManagerLoadCount(anything())).thrice();
|
|
1021
|
+
verify(
|
|
1022
|
+
metricsAdapterMock.incrementDataManagerLoadCount(
|
|
1023
|
+
deepEqual({
|
|
1024
|
+
type: IncrementLoadCountEventType.DATALOADER,
|
|
1025
|
+
isInTransaction: false,
|
|
1026
|
+
fieldValueCount: 2,
|
|
1027
|
+
entityClassName: TestEntity.name,
|
|
1028
|
+
loadType: EntityLoadMethodType.SINGLE,
|
|
1029
|
+
}),
|
|
1030
|
+
),
|
|
1031
|
+
).once();
|
|
1032
|
+
verify(
|
|
1033
|
+
metricsAdapterMock.incrementDataManagerLoadCount(
|
|
1034
|
+
deepEqual({
|
|
1035
|
+
type: IncrementLoadCountEventType.CACHE,
|
|
1036
|
+
isInTransaction: false,
|
|
1037
|
+
fieldValueCount: 2,
|
|
1038
|
+
entityClassName: TestEntity.name,
|
|
1039
|
+
loadType: EntityLoadMethodType.SINGLE,
|
|
1040
|
+
}),
|
|
1041
|
+
),
|
|
1042
|
+
).once();
|
|
1043
|
+
verify(
|
|
1044
|
+
metricsAdapterMock.incrementDataManagerLoadCount(
|
|
1045
|
+
deepEqual({
|
|
1046
|
+
type: IncrementLoadCountEventType.DATABASE,
|
|
1047
|
+
isInTransaction: false,
|
|
1048
|
+
fieldValueCount: 1,
|
|
1049
|
+
entityClassName: TestEntity.name,
|
|
1050
|
+
loadType: EntityLoadMethodType.SINGLE,
|
|
1051
|
+
}),
|
|
1052
|
+
),
|
|
1053
|
+
).once();
|
|
702
1054
|
|
|
703
|
-
|
|
704
|
-
when(
|
|
705
|
-
databaseAdapterSpy.fetchManyByRawWhereClauseAsync(
|
|
706
|
-
anything(),
|
|
707
|
-
anyString(),
|
|
708
|
-
anything(),
|
|
709
|
-
anything(),
|
|
710
|
-
),
|
|
711
|
-
).thenResolve([]);
|
|
712
|
-
await entityDataManager.loadManyByRawWhereClauseAsync(queryContext, '', [], {});
|
|
713
|
-
verify(
|
|
714
|
-
metricsAdapterMock.logDataManagerLoadEvent(
|
|
715
|
-
objectContaining({
|
|
716
|
-
type: EntityMetricsLoadType.LOAD_MANY_RAW,
|
|
717
|
-
entityClassName: TestEntity.name,
|
|
718
|
-
count: 0,
|
|
719
|
-
}),
|
|
720
|
-
),
|
|
721
|
-
).once();
|
|
1055
|
+
resetCalls(metricsAdapterMock);
|
|
722
1056
|
|
|
723
|
-
|
|
1057
|
+
await entityDataManager.loadManyByFieldEqualityConjunctionAsync(
|
|
1058
|
+
queryContext,
|
|
1059
|
+
[
|
|
1060
|
+
{
|
|
1061
|
+
fieldName: 'testIndexedField',
|
|
1062
|
+
fieldValue: 'unique1',
|
|
1063
|
+
},
|
|
1064
|
+
],
|
|
1065
|
+
{},
|
|
1066
|
+
);
|
|
1067
|
+
verify(
|
|
1068
|
+
metricsAdapterMock.logDataManagerLoadEvent(
|
|
1069
|
+
deepEqual({
|
|
1070
|
+
type: EntityMetricsLoadType.LOAD_MANY_EQUALITY_CONJUNCTION,
|
|
1071
|
+
isInTransaction: false,
|
|
1072
|
+
entityClassName: TestEntity.name,
|
|
1073
|
+
duration: anyNumber(),
|
|
1074
|
+
count: 1,
|
|
1075
|
+
}),
|
|
1076
|
+
),
|
|
1077
|
+
).once();
|
|
1078
|
+
|
|
1079
|
+
resetCalls(metricsAdapterMock);
|
|
1080
|
+
|
|
1081
|
+
const databaseAdapterSpy = spy(databaseAdapter);
|
|
1082
|
+
when(
|
|
1083
|
+
databaseAdapterSpy.fetchManyByRawWhereClauseAsync(
|
|
1084
|
+
anything(),
|
|
1085
|
+
anyString(),
|
|
1086
|
+
anything(),
|
|
1087
|
+
anything(),
|
|
1088
|
+
),
|
|
1089
|
+
).thenResolve([]);
|
|
1090
|
+
await entityDataManager.loadManyByRawWhereClauseAsync(queryContext, '', [], {});
|
|
1091
|
+
verify(
|
|
1092
|
+
metricsAdapterMock.logDataManagerLoadEvent(
|
|
1093
|
+
deepEqual({
|
|
1094
|
+
type: EntityMetricsLoadType.LOAD_MANY_RAW,
|
|
1095
|
+
isInTransaction: false,
|
|
1096
|
+
entityClassName: TestEntity.name,
|
|
1097
|
+
duration: anyNumber(),
|
|
1098
|
+
count: 0,
|
|
1099
|
+
}),
|
|
1100
|
+
),
|
|
1101
|
+
).once();
|
|
1102
|
+
|
|
1103
|
+
verify(metricsAdapterMock.incrementDataManagerLoadCount(anything())).never();
|
|
1104
|
+
});
|
|
1105
|
+
|
|
1106
|
+
it('records metrics appropriately inside of transactions', async () => {
|
|
1107
|
+
const metricsAdapterMock = mock<IEntityMetricsAdapter>();
|
|
1108
|
+
const metricsAdapter = instance(metricsAdapterMock);
|
|
1109
|
+
|
|
1110
|
+
const objects = getObjects();
|
|
1111
|
+
const dataStore = StubDatabaseAdapter.convertFieldObjectsToDataStore(
|
|
1112
|
+
testEntityConfiguration,
|
|
1113
|
+
objects,
|
|
1114
|
+
);
|
|
1115
|
+
const databaseAdapter = new StubDatabaseAdapter<TestFields, 'customIdField'>(
|
|
1116
|
+
testEntityConfiguration,
|
|
1117
|
+
dataStore,
|
|
1118
|
+
);
|
|
1119
|
+
const cacheAdapterProvider = new InMemoryFullCacheStubCacheAdapterProvider();
|
|
1120
|
+
const cacheAdapter = cacheAdapterProvider.getCacheAdapter(testEntityConfiguration);
|
|
1121
|
+
const entityCache = new ReadThroughEntityCache(testEntityConfiguration, cacheAdapter);
|
|
1122
|
+
const entityDataManager = new EntityDataManager(
|
|
1123
|
+
databaseAdapter,
|
|
1124
|
+
entityCache,
|
|
1125
|
+
new StubQueryContextProvider(),
|
|
1126
|
+
metricsAdapter,
|
|
1127
|
+
TestEntity.name,
|
|
1128
|
+
);
|
|
1129
|
+
|
|
1130
|
+
await new StubQueryContextProvider().runInTransactionAsync(async (queryContext) => {
|
|
1131
|
+
// make call to loadManyByFieldEqualingAsync to populate cache and dataloader, ensure metrics are recorded
|
|
1132
|
+
// for dataloader, cache, and database
|
|
1133
|
+
await entityDataManager.loadManyEqualingAsync(
|
|
1134
|
+
queryContext,
|
|
1135
|
+
new SingleFieldHolder('testIndexedField'),
|
|
1136
|
+
[new SingleFieldValueHolder('unique1')],
|
|
1137
|
+
);
|
|
1138
|
+
verify(
|
|
1139
|
+
metricsAdapterMock.logDataManagerLoadEvent(
|
|
1140
|
+
deepEqual({
|
|
1141
|
+
type: EntityMetricsLoadType.LOAD_MANY,
|
|
1142
|
+
isInTransaction: true,
|
|
1143
|
+
entityClassName: TestEntity.name,
|
|
1144
|
+
duration: anyNumber(),
|
|
1145
|
+
count: 1,
|
|
1146
|
+
}),
|
|
1147
|
+
),
|
|
1148
|
+
).once();
|
|
1149
|
+
verify(metricsAdapterMock.incrementDataManagerLoadCount(anything())).twice();
|
|
1150
|
+
verify(
|
|
1151
|
+
metricsAdapterMock.incrementDataManagerLoadCount(
|
|
1152
|
+
deepEqual({
|
|
1153
|
+
type: IncrementLoadCountEventType.DATALOADER,
|
|
1154
|
+
isInTransaction: true,
|
|
1155
|
+
fieldValueCount: 1,
|
|
1156
|
+
entityClassName: TestEntity.name,
|
|
1157
|
+
loadType: EntityLoadMethodType.SINGLE,
|
|
1158
|
+
}),
|
|
1159
|
+
),
|
|
1160
|
+
).once();
|
|
1161
|
+
verify(
|
|
1162
|
+
metricsAdapterMock.incrementDataManagerLoadCount(
|
|
1163
|
+
deepEqual({
|
|
1164
|
+
type: IncrementLoadCountEventType.DATABASE,
|
|
1165
|
+
isInTransaction: true,
|
|
1166
|
+
fieldValueCount: 1,
|
|
1167
|
+
entityClassName: TestEntity.name,
|
|
1168
|
+
loadType: EntityLoadMethodType.SINGLE,
|
|
1169
|
+
}),
|
|
1170
|
+
),
|
|
1171
|
+
).once();
|
|
1172
|
+
|
|
1173
|
+
resetCalls(metricsAdapterMock);
|
|
1174
|
+
|
|
1175
|
+
// make second call to loadManyByFieldEqualingAsync, ensure metrics are only recorded for dataloader since
|
|
1176
|
+
// entity is in local dataloader
|
|
1177
|
+
await entityDataManager.loadManyEqualingAsync(
|
|
1178
|
+
queryContext,
|
|
1179
|
+
new SingleFieldHolder('testIndexedField'),
|
|
1180
|
+
[new SingleFieldValueHolder('unique1')],
|
|
1181
|
+
);
|
|
1182
|
+
verify(metricsAdapterMock.incrementDataManagerLoadCount(anything())).once();
|
|
1183
|
+
verify(
|
|
1184
|
+
metricsAdapterMock.incrementDataManagerLoadCount(
|
|
1185
|
+
deepEqual({
|
|
1186
|
+
type: IncrementLoadCountEventType.DATALOADER,
|
|
1187
|
+
isInTransaction: true,
|
|
1188
|
+
fieldValueCount: 1,
|
|
1189
|
+
entityClassName: TestEntity.name,
|
|
1190
|
+
loadType: EntityLoadMethodType.SINGLE,
|
|
1191
|
+
}),
|
|
1192
|
+
),
|
|
1193
|
+
).once();
|
|
1194
|
+
|
|
1195
|
+
resetCalls(metricsAdapterMock);
|
|
1196
|
+
|
|
1197
|
+
await entityDataManager.loadManyByFieldEqualityConjunctionAsync(
|
|
1198
|
+
queryContext,
|
|
1199
|
+
[
|
|
1200
|
+
{
|
|
1201
|
+
fieldName: 'testIndexedField',
|
|
1202
|
+
fieldValue: 'unique1',
|
|
1203
|
+
},
|
|
1204
|
+
],
|
|
1205
|
+
{},
|
|
1206
|
+
);
|
|
1207
|
+
verify(
|
|
1208
|
+
metricsAdapterMock.logDataManagerLoadEvent(
|
|
1209
|
+
deepEqual({
|
|
1210
|
+
type: EntityMetricsLoadType.LOAD_MANY_EQUALITY_CONJUNCTION,
|
|
1211
|
+
isInTransaction: true,
|
|
1212
|
+
entityClassName: TestEntity.name,
|
|
1213
|
+
duration: anyNumber(),
|
|
1214
|
+
count: 1,
|
|
1215
|
+
}),
|
|
1216
|
+
),
|
|
1217
|
+
).once();
|
|
1218
|
+
|
|
1219
|
+
resetCalls(metricsAdapterMock);
|
|
1220
|
+
|
|
1221
|
+
const databaseAdapterSpy = spy(databaseAdapter);
|
|
1222
|
+
when(
|
|
1223
|
+
databaseAdapterSpy.fetchManyByRawWhereClauseAsync(
|
|
1224
|
+
anything(),
|
|
1225
|
+
anyString(),
|
|
1226
|
+
anything(),
|
|
1227
|
+
anything(),
|
|
1228
|
+
),
|
|
1229
|
+
).thenResolve([]);
|
|
1230
|
+
await entityDataManager.loadManyByRawWhereClauseAsync(queryContext, '', [], {});
|
|
1231
|
+
verify(
|
|
1232
|
+
metricsAdapterMock.logDataManagerLoadEvent(
|
|
1233
|
+
deepEqual({
|
|
1234
|
+
type: EntityMetricsLoadType.LOAD_MANY_RAW,
|
|
1235
|
+
isInTransaction: true,
|
|
1236
|
+
entityClassName: TestEntity.name,
|
|
1237
|
+
duration: anyNumber(),
|
|
1238
|
+
count: 0,
|
|
1239
|
+
}),
|
|
1240
|
+
),
|
|
1241
|
+
).once();
|
|
1242
|
+
|
|
1243
|
+
verify(metricsAdapterMock.incrementDataManagerLoadCount(anything())).never();
|
|
1244
|
+
});
|
|
1245
|
+
});
|
|
724
1246
|
});
|
|
725
1247
|
|
|
726
1248
|
it('throws when a single value load-by value is null or undefined', async () => {
|