@fluid-experimental/tree 1.3.3 → 1.3.4

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.
@@ -350,6 +350,22 @@ describe('IdCompressor', () => {
350
350
  const secondRange = compressor.takeNextCreationRange();
351
351
  expect(() => compressor.finalizeCreationRange(secondRange)).to.throw('Ranges finalized out of order.');
352
352
  });
353
+ it('can finalize ranges into clusters of varying sizes', () => {
354
+ for (let i = 1; i < 5; i++) {
355
+ for (let j = 0; j <= i; j++) {
356
+ const compressor = createCompressor(Client.Client1, i);
357
+ const ids = new Set();
358
+ for (let k = 0; k <= j; k++) {
359
+ ids.add(compressor.generateCompressedId());
360
+ }
361
+ compressor.finalizeCreationRange(compressor.takeNextCreationRange());
362
+ const opIds = new Set();
363
+ ids.forEach((id) => opIds.add(compressor.normalizeToOpSpace(id)));
364
+ expect(ids.size).to.equal(opIds.size);
365
+ opIds.forEach((id) => expect(isFinalId(id)).to.be.true);
366
+ }
367
+ }
368
+ });
353
369
  it('prevents finalizing unacceptably enormous amounts of ID allocation', () => {
354
370
  const compressor1 = createCompressor(Client.Client1);
355
371
  const integerLargerThanHalfMax = Math.round((Number.MAX_SAFE_INTEGER / 3) * 2);
@@ -568,6 +584,198 @@ describe('IdCompressor', () => {
568
584
  expect(normalizedToClient1SessionSpace).to.equal(id);
569
585
  });
570
586
  });
587
+ describe('Eager final ID allocation', () => {
588
+ it('eagerly allocates final IDs when cluster creation has been finalized', () => {
589
+ const compressor = createCompressor(Client.Client1, 5);
590
+ const localId1 = compressor.generateCompressedId();
591
+ expect(isLocalId(localId1)).to.be.true;
592
+ const localId2 = compressor.generateCompressedId();
593
+ expect(isLocalId(localId2)).to.be.true;
594
+ compressor.finalizeCreationRange(compressor.takeNextCreationRange());
595
+ const finalId3 = compressor.generateCompressedId();
596
+ expect(isFinalId(finalId3)).to.be.true;
597
+ const finalId4 = compressor.generateCompressedId();
598
+ expect(isFinalId(finalId4)).to.be.true;
599
+ const finalId5 = compressor.generateCompressedId();
600
+ expect(isFinalId(finalId5)).to.be.true;
601
+ const localId6 = compressor.generateCompressedId();
602
+ expect(isLocalId(localId6)).to.be.true;
603
+ compressor.finalizeCreationRange(compressor.takeNextCreationRange());
604
+ const opSpaceId1 = compressor.normalizeToOpSpace(localId1);
605
+ const opSpaceId2 = compressor.normalizeToOpSpace(localId2);
606
+ const opSpaceId3 = compressor.normalizeToOpSpace(finalId3);
607
+ const opSpaceId4 = compressor.normalizeToOpSpace(finalId4);
608
+ const opSpaceId5 = compressor.normalizeToOpSpace(finalId5);
609
+ const opSpaceId6 = compressor.normalizeToOpSpace(localId6);
610
+ expectAssert(isFinalId(opSpaceId1));
611
+ expectAssert(isFinalId(opSpaceId2));
612
+ expectAssert(isFinalId(opSpaceId3) && opSpaceId3 === finalId3);
613
+ expectAssert(isFinalId(opSpaceId4) && opSpaceId4 === finalId4);
614
+ expectAssert(isFinalId(opSpaceId5) && opSpaceId5 === finalId5);
615
+ expectAssert(isFinalId(opSpaceId6));
616
+ expect(compressor.normalizeToSessionSpace(opSpaceId1)).to.equal(localId1);
617
+ expect(compressor.normalizeToSessionSpace(opSpaceId2)).to.equal(localId2);
618
+ expect(compressor.normalizeToSessionSpace(opSpaceId3)).to.equal(finalId3);
619
+ expect(compressor.normalizeToSessionSpace(opSpaceId4)).to.equal(finalId4);
620
+ expect(compressor.normalizeToSessionSpace(opSpaceId5)).to.equal(finalId5);
621
+ expect(compressor.normalizeToSessionSpace(opSpaceId6)).to.equal(localId6);
622
+ });
623
+ it('does not eagerly allocate final IDs for IDs with overrides', () => {
624
+ const compressor = createCompressor(Client.Client1, 5);
625
+ const localId1 = compressor.generateCompressedId();
626
+ compressor.finalizeCreationRange(compressor.takeNextCreationRange());
627
+ const override1 = compressor.generateCompressedId('override1');
628
+ expect(isLocalId(override1)).to.be.true;
629
+ const finalId1 = compressor.generateCompressedId();
630
+ expect(isFinalId(finalId1)).to.be.true;
631
+ generateCompressedIds(compressor, 5);
632
+ compressor.finalizeCreationRange(compressor.takeNextCreationRange());
633
+ const override2 = compressor.generateCompressedId('override2');
634
+ expect(isLocalId(override2)).to.be.true;
635
+ const finalId2 = compressor.generateCompressedId();
636
+ expect(isFinalId(finalId2)).to.be.true;
637
+ compressor.finalizeCreationRange(compressor.takeNextCreationRange());
638
+ const opSpaceId1 = compressor.normalizeToOpSpace(localId1);
639
+ const opSpaceId2 = compressor.normalizeToOpSpace(override1);
640
+ const opSpaceId3 = compressor.normalizeToOpSpace(finalId1);
641
+ const opSpaceId4 = compressor.normalizeToOpSpace(override2);
642
+ const opSpaceId5 = compressor.normalizeToOpSpace(finalId2);
643
+ expectAssert(isFinalId(opSpaceId1));
644
+ expectAssert(isFinalId(opSpaceId2));
645
+ expectAssert(isFinalId(opSpaceId3) && opSpaceId3 === finalId1);
646
+ expectAssert(isFinalId(opSpaceId4));
647
+ expectAssert(isFinalId(opSpaceId5) && opSpaceId5 === finalId2);
648
+ expect(compressor.normalizeToSessionSpace(opSpaceId1)).to.equal(localId1);
649
+ expect(compressor.normalizeToSessionSpace(opSpaceId2)).to.equal(override1);
650
+ expect(compressor.normalizeToSessionSpace(opSpaceId3)).to.equal(finalId1);
651
+ expect(compressor.normalizeToSessionSpace(opSpaceId4)).to.equal(override2);
652
+ expect(compressor.normalizeToSessionSpace(opSpaceId5)).to.equal(finalId2);
653
+ });
654
+ it('correctly normalizes eagerly allocated final IDs', () => {
655
+ const compressor = createCompressor(Client.Client1, 5);
656
+ const localId1 = compressor.generateCompressedId();
657
+ const range1 = compressor.takeNextCreationRange();
658
+ const localId2 = compressor.generateCompressedId();
659
+ const range2 = compressor.takeNextCreationRange();
660
+ expect(isLocalId(localId1)).to.be.true;
661
+ expect(isLocalId(localId2)).to.be.true;
662
+ compressor.finalizeCreationRange(range1);
663
+ compressor.finalizeCreationRange(range2);
664
+ const opSpaceId1 = compressor.normalizeToOpSpace(localId1);
665
+ const opSpaceId2 = compressor.normalizeToOpSpace(localId2);
666
+ expectAssert(isFinalId(opSpaceId1));
667
+ expectAssert(isFinalId(opSpaceId2));
668
+ expect(compressor.normalizeToSessionSpace(opSpaceId1)).to.equal(localId1);
669
+ expect(compressor.normalizeToSessionSpace(opSpaceId2)).to.equal(localId2);
670
+ });
671
+ it('generates correct eager finals when there are outstanding locals after cluster expansion', () => {
672
+ const compressor = createCompressor(Client.Client1, 2);
673
+ // Before cluster expansion
674
+ expect(isLocalId(compressor.generateCompressedId())).to.be.true;
675
+ const rangeA = compressor.takeNextCreationRange();
676
+ compressor.finalizeCreationRange(rangeA);
677
+ expect(isFinalId(compressor.generateCompressedId())).to.be.true;
678
+ // After cluster expansion
679
+ expect(isLocalId(compressor.generateCompressedId())).to.be.true;
680
+ const rangeB = compressor.takeNextCreationRange();
681
+ const localId = compressor.generateCompressedId();
682
+ expect(isLocalId(localId)).to.be.true;
683
+ // Take a range that won't be finalized in this test; the finalizing of range B should associate this range with finals
684
+ const rangeC = compressor.takeNextCreationRange();
685
+ compressor.finalizeCreationRange(rangeB);
686
+ const eagerId = compressor.generateCompressedId();
687
+ expect(isFinalId(eagerId)).to.be.true;
688
+ expect(compressor.recompress(compressor.decompress(localId))).to.equal(localId);
689
+ expect(compressor.recompress(compressor.decompress(eagerId))).to.equal(eagerId);
690
+ compressor.finalizeCreationRange(rangeC);
691
+ expect(compressor.recompress(compressor.decompress(localId))).to.equal(localId);
692
+ expect(compressor.recompress(compressor.decompress(eagerId))).to.equal(eagerId);
693
+ });
694
+ it('generates unique eager finals when multiple outstanding creation ranges during finalizing', () => {
695
+ const compressor = createCompressor(Client.Client1, 10 /* must be 10 for the test to make sense */);
696
+ // Make a first outstanding range
697
+ const id1_1 = compressor.generateCompressedId();
698
+ const id1_2 = compressor.generateCompressedId();
699
+ expect(isLocalId(id1_1)).to.be.true;
700
+ expect(isLocalId(id1_2)).to.be.true;
701
+ const range1 = compressor.takeNextCreationRange();
702
+ // Make a second outstanding range
703
+ const id2_1 = compressor.generateCompressedId();
704
+ const id2_2 = compressor.generateCompressedId();
705
+ expect(isLocalId(id2_1)).to.be.true;
706
+ expect(isLocalId(id2_2)).to.be.true;
707
+ const range2 = compressor.takeNextCreationRange();
708
+ // Finalize just the first one, which should create finals that align with both outstanding ranges
709
+ compressor.finalizeCreationRange(range1);
710
+ // Make a third range. This one should be composed of eager finals that align after the two ranges above.
711
+ const id3_1 = compressor.generateCompressedId();
712
+ const id3_2 = compressor.generateCompressedId();
713
+ expect(isFinalId(id3_1)).to.be.true;
714
+ expect(isFinalId(id3_2)).to.be.true;
715
+ const range3 = compressor.takeNextCreationRange();
716
+ // Finalize both initial ranges.
717
+ compressor.finalizeCreationRange(range2);
718
+ compressor.finalizeCreationRange(range3);
719
+ // Make some more eager finals that should be aligned correctly.
720
+ const id4_1 = compressor.generateCompressedId();
721
+ const id4_2 = compressor.generateCompressedId();
722
+ expect(isFinalId(id4_1)).to.be.true;
723
+ expect(isFinalId(id4_2)).to.be.true;
724
+ // Assert everything is unique and consistent.
725
+ const ids = new Set();
726
+ const uuids = new Set();
727
+ [id1_1, id1_2, id2_1, id2_2, id3_1, id3_2, id4_1, id4_2].forEach((id) => {
728
+ ids.add(id);
729
+ uuids.add(compressor.decompress(id));
730
+ });
731
+ expect(ids.size).to.equal(8);
732
+ expect(uuids.size).to.equal(8);
733
+ });
734
+ it('generates unique eager finals when there are still outstanding locals after a cluster is expanded', () => {
735
+ // const compressor = createCompressor(Client.Client1, 4 /* must be 4 for the test to make sense */);
736
+ const compressor = new IdCompressor(sessionIds.get(Client.Client1), 0);
737
+ compressor.clusterCapacity = 4;
738
+ // Make locals to fill half the future cluster
739
+ const id1_1 = compressor.generateCompressedId();
740
+ const id1_2 = compressor.generateCompressedId();
741
+ expect(isLocalId(id1_1)).to.be.true;
742
+ expect(isLocalId(id1_2)).to.be.true;
743
+ const range1 = compressor.takeNextCreationRange();
744
+ // Make locals to overflow the future cluster
745
+ const id2_1 = compressor.generateCompressedId();
746
+ const id2_2 = compressor.generateCompressedId();
747
+ const id2_3 = compressor.generateCompressedId();
748
+ expect(isLocalId(id2_1)).to.be.true;
749
+ expect(isLocalId(id2_2)).to.be.true;
750
+ expect(isLocalId(id2_3)).to.be.true;
751
+ const range2 = compressor.takeNextCreationRange();
752
+ // Finalize the first range. This should align the first four locals (i.e. all of range1, and 2/3 of range2)
753
+ compressor.finalizeCreationRange(range1);
754
+ // Make a single range that should still be overflowing the initial cluster (i.e. be local)
755
+ const id3_1 = compressor.generateCompressedId();
756
+ expect(isLocalId(id3_1)).to.be.true;
757
+ const range3 = compressor.takeNextCreationRange();
758
+ // First finalize should expand the cluster and align all outstanding ranges.
759
+ compressor.finalizeCreationRange(range2);
760
+ // All generated IDs should have aligned finals (even though range3 has not been finalized)
761
+ const allIds = [id1_1, id1_2, id2_1, id2_2, id2_3, id3_1];
762
+ allIds.forEach((id) => expect(isFinalId(compressor.normalizeToOpSpace(id))).to.be.true);
763
+ compressor.finalizeCreationRange(range3);
764
+ // Make one eager final
765
+ const id4_1 = compressor.generateCompressedId();
766
+ allIds.push(id4_1);
767
+ expect(isFinalId(id4_1)).to.be.true;
768
+ // Assert everything is unique and consistent.
769
+ const ids = new Set();
770
+ const uuids = new Set();
771
+ allIds.forEach((id) => {
772
+ ids.add(id);
773
+ uuids.add(compressor.decompress(id));
774
+ });
775
+ expect(ids.size).to.equal(7);
776
+ expect(uuids.size).to.equal(7);
777
+ });
778
+ });
571
779
  describe('Serialization', () => {
572
780
  it('can serialize an empty compressor', () => {
573
781
  const compressor = createCompressor(Client.Client1);
@@ -905,7 +1113,7 @@ describe('IdCompressor', () => {
905
1113
  });
906
1114
  });
907
1115
  itNetwork('produces consistent IDs with large fuzz input', (network) => {
908
- const generator = take(1000, makeOpGenerator({ includeOverrides: true }));
1116
+ const generator = take(2000, makeOpGenerator({ includeOverrides: true }));
909
1117
  performFuzzActions(generator, network, 1984, undefined, true, (network) => network.assertNetworkState());
910
1118
  network.deliverOperations(DestinationClient.All);
911
1119
  });
@@ -941,91 +1149,6 @@ describe('IdCompressor', () => {
941
1149
  const emptyId = (id + 1);
942
1150
  expect(() => network.getCompressor(Client.Client2).decompress(emptyId)).to.throw('Compressed ID was not generated by this compressor');
943
1151
  });
944
- describe('Eager final ID allocation', () => {
945
- it('eagerly allocates final IDs when cluster creation has been finalized', () => {
946
- const compressor = createCompressor(Client.Client1, 5);
947
- const localId1 = compressor.generateCompressedId();
948
- expect(isLocalId(localId1)).to.be.true;
949
- const localId2 = compressor.generateCompressedId();
950
- expect(isLocalId(localId2)).to.be.true;
951
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
952
- const finalId3 = compressor.generateCompressedId();
953
- expect(isFinalId(finalId3)).to.be.true;
954
- const finalId4 = compressor.generateCompressedId();
955
- expect(isFinalId(finalId4)).to.be.true;
956
- const finalId5 = compressor.generateCompressedId();
957
- expect(isFinalId(finalId5)).to.be.true;
958
- const localId6 = compressor.generateCompressedId();
959
- expect(isLocalId(localId6)).to.be.true;
960
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
961
- const opSpaceId1 = compressor.normalizeToOpSpace(localId1);
962
- const opSpaceId2 = compressor.normalizeToOpSpace(localId2);
963
- const opSpaceId3 = compressor.normalizeToOpSpace(finalId3);
964
- const opSpaceId4 = compressor.normalizeToOpSpace(finalId4);
965
- const opSpaceId5 = compressor.normalizeToOpSpace(finalId5);
966
- const opSpaceId6 = compressor.normalizeToOpSpace(localId6);
967
- expectAssert(isFinalId(opSpaceId1));
968
- expectAssert(isFinalId(opSpaceId2));
969
- expectAssert(isFinalId(opSpaceId3) && opSpaceId3 === finalId3);
970
- expectAssert(isFinalId(opSpaceId4) && opSpaceId4 === finalId4);
971
- expectAssert(isFinalId(opSpaceId5) && opSpaceId5 === finalId5);
972
- expectAssert(isFinalId(opSpaceId6));
973
- expect(compressor.normalizeToSessionSpace(opSpaceId1)).to.equal(localId1);
974
- expect(compressor.normalizeToSessionSpace(opSpaceId2)).to.equal(localId2);
975
- expect(compressor.normalizeToSessionSpace(opSpaceId3)).to.equal(finalId3);
976
- expect(compressor.normalizeToSessionSpace(opSpaceId4)).to.equal(finalId4);
977
- expect(compressor.normalizeToSessionSpace(opSpaceId5)).to.equal(finalId5);
978
- expect(compressor.normalizeToSessionSpace(opSpaceId6)).to.equal(localId6);
979
- });
980
- it('does not eagerly allocate final IDs for IDs with overrides', () => {
981
- const compressor = createCompressor(Client.Client1, 5);
982
- const localId1 = compressor.generateCompressedId();
983
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
984
- const override1 = compressor.generateCompressedId('override1');
985
- expect(isLocalId(override1)).to.be.true;
986
- const finalId1 = compressor.generateCompressedId();
987
- expect(isFinalId(finalId1)).to.be.true;
988
- generateCompressedIds(compressor, 5);
989
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
990
- const override2 = compressor.generateCompressedId('override2');
991
- expect(isLocalId(override2)).to.be.true;
992
- const finalId2 = compressor.generateCompressedId();
993
- expect(isFinalId(finalId2)).to.be.true;
994
- compressor.finalizeCreationRange(compressor.takeNextCreationRange());
995
- const opSpaceId1 = compressor.normalizeToOpSpace(localId1);
996
- const opSpaceId2 = compressor.normalizeToOpSpace(override1);
997
- const opSpaceId3 = compressor.normalizeToOpSpace(finalId1);
998
- const opSpaceId4 = compressor.normalizeToOpSpace(override2);
999
- const opSpaceId5 = compressor.normalizeToOpSpace(finalId2);
1000
- expectAssert(isFinalId(opSpaceId1));
1001
- expectAssert(isFinalId(opSpaceId2));
1002
- expectAssert(isFinalId(opSpaceId3) && opSpaceId3 === finalId1);
1003
- expectAssert(isFinalId(opSpaceId4));
1004
- expectAssert(isFinalId(opSpaceId5) && opSpaceId5 === finalId2);
1005
- expect(compressor.normalizeToSessionSpace(opSpaceId1)).to.equal(localId1);
1006
- expect(compressor.normalizeToSessionSpace(opSpaceId2)).to.equal(override1);
1007
- expect(compressor.normalizeToSessionSpace(opSpaceId3)).to.equal(finalId1);
1008
- expect(compressor.normalizeToSessionSpace(opSpaceId4)).to.equal(override2);
1009
- expect(compressor.normalizeToSessionSpace(opSpaceId5)).to.equal(finalId2);
1010
- });
1011
- it('correctly normalizes eagerly allocated final IDs', () => {
1012
- const compressor = createCompressor(Client.Client1, 5);
1013
- const localId1 = compressor.generateCompressedId();
1014
- const range1 = compressor.takeNextCreationRange();
1015
- const localId2 = compressor.generateCompressedId();
1016
- const range2 = compressor.takeNextCreationRange();
1017
- expect(isLocalId(localId1)).to.be.true;
1018
- expect(isLocalId(localId2)).to.be.true;
1019
- compressor.finalizeCreationRange(range1);
1020
- compressor.finalizeCreationRange(range2);
1021
- const opSpaceId1 = compressor.normalizeToOpSpace(localId1);
1022
- const opSpaceId2 = compressor.normalizeToOpSpace(localId2);
1023
- expectAssert(isFinalId(opSpaceId1));
1024
- expectAssert(isFinalId(opSpaceId2));
1025
- expect(compressor.normalizeToSessionSpace(opSpaceId1)).to.equal(localId1);
1026
- expect(compressor.normalizeToSessionSpace(opSpaceId2)).to.equal(localId2);
1027
- });
1028
- });
1029
1152
  describe('Finalizing', () => {
1030
1153
  itNetwork('can finalize IDs from multiple clients', (network) => {
1031
1154
  network.allocateAndSendIds(Client.Client1, 3, {
@@ -1163,7 +1286,7 @@ describe('IdCompressor', () => {
1163
1286
  expectSequencedLogsAlign(network, Client.Client1, Client.Client2);
1164
1287
  });
1165
1288
  itNetwork('can serialize after a large fuzz input', 3, (network) => {
1166
- const generator = take(1000, makeOpGenerator({ includeOverrides: true }));
1289
+ const generator = take(2000, makeOpGenerator({ includeOverrides: true }));
1167
1290
  performFuzzActions(generator, network, Math.PI, undefined, true, (network) => {
1168
1291
  // Periodically check that everyone in the network has the same serialized state
1169
1292
  network.deliverOperations(DestinationClient.All);