26
26
using System . Net . Http ;
27
27
using System . Collections . Concurrent ;
28
28
using System . Net ;
29
- using System . Threading ;
30
29
using System . Security . Cryptography . X509Certificates ;
31
30
using Confluent . Kafka ;
31
+ using Confluent . Shared . CollectionUtils ;
32
32
using Microsoft . Extensions . Caching . Memory ;
33
33
34
34
@@ -69,23 +69,21 @@ private record struct SchemaId(int Id, string Format);
69
69
private IRestService restService ;
70
70
private int identityMapCapacity ;
71
71
private int latestCacheTtlSecs ;
72
- private readonly ConcurrentDictionary < SchemaId , Schema > schemaById = new ConcurrentDictionary < SchemaId , Schema > ( ) ;
72
+ private readonly ConcurrentDictionary < SchemaId , Task < Schema > > schemaById = new ConcurrentDictionary < SchemaId , Task < Schema > > ( ) ;
73
73
74
- private readonly ConcurrentDictionary < string /*subject*/ , ConcurrentDictionary < Schema , int > > idBySchemaBySubject =
75
- new ConcurrentDictionary < string , ConcurrentDictionary < Schema , int > > ( ) ;
74
+ private readonly ConcurrentDictionary < string /*subject*/ , ConcurrentDictionary < Schema , Task < int > > > idBySchemaBySubject =
75
+ new ConcurrentDictionary < string , ConcurrentDictionary < Schema , Task < int > > > ( ) ;
76
76
77
- private readonly ConcurrentDictionary < string /*subject*/ , ConcurrentDictionary < int , RegisteredSchema > > schemaByVersionBySubject =
78
- new ConcurrentDictionary < string , ConcurrentDictionary < int , RegisteredSchema > > ( ) ;
77
+ private readonly ConcurrentDictionary < string /*subject*/ , ConcurrentDictionary < int , Task < RegisteredSchema > > > schemaByVersionBySubject =
78
+ new ConcurrentDictionary < string , ConcurrentDictionary < int , Task < RegisteredSchema > > > ( ) ;
79
79
80
- private readonly ConcurrentDictionary < string /*subject*/ , ConcurrentDictionary < Schema , RegisteredSchema > > registeredSchemaBySchemaBySubject =
81
- new ConcurrentDictionary < string , ConcurrentDictionary < Schema , RegisteredSchema > > ( ) ;
80
+ private readonly ConcurrentDictionary < string /*subject*/ , ConcurrentDictionary < Schema , Task < RegisteredSchema > > > registeredSchemaBySchemaBySubject =
81
+ new ConcurrentDictionary < string , ConcurrentDictionary < Schema , Task < RegisteredSchema > > > ( ) ;
82
82
83
83
private readonly MemoryCache latestVersionBySubject = new MemoryCache ( new MemoryCacheOptions ( ) ) ;
84
84
85
85
private readonly MemoryCache latestWithMetadataBySubject = new MemoryCache ( new MemoryCacheOptions ( ) ) ;
86
86
87
- private readonly SemaphoreSlim cacheMutex = new SemaphoreSlim ( 1 ) ;
88
-
89
87
private SubjectNameStrategyDelegate keySubjectNameStrategy ;
90
88
private SubjectNameStrategyDelegate valueSubjectNameStrategy ;
91
89
@@ -607,45 +605,26 @@ public async Task<int> GetSchemaIdAsync(string subject, Schema schema, bool norm
607
605
{
608
606
if ( idBySchemaBySubject . TryGetValue ( subject , out var idBySchema ) )
609
607
{
610
- if ( idBySchema . TryGetValue ( schema , out int schemaId ) )
608
+ if ( idBySchema . TryGetValue ( schema , out var schemaId ) )
611
609
{
612
- return schemaId ;
610
+ return await schemaId ;
613
611
}
614
612
}
615
613
616
- await cacheMutex . WaitAsync ( ) . ConfigureAwait ( continueOnCapturedContext : false ) ;
617
- try
618
- {
619
- if ( ! this . idBySchemaBySubject . TryGetValue ( subject , out idBySchema ) )
620
- {
621
- idBySchema = new ConcurrentDictionary < Schema , int > ( ) ;
622
- this . idBySchemaBySubject . TryAdd ( subject , idBySchema ) ;
623
- }
624
-
625
- // TODO: The following could be optimized in the usual case where idBySchema only
626
- // contains very few elements and the schema string passed in is always the same
627
- // instance.
614
+ CleanCacheIfFull ( ) ;
628
615
629
- if ( ! idBySchema . TryGetValue ( schema , out int schemaId ) )
630
- {
631
- CleanCacheIfFull ( ) ;
632
-
633
- // throws SchemaRegistryException if schema is not known.
634
- var registeredSchema = await restService . LookupSchemaAsync ( subject , schema , true , normalize )
635
- . ConfigureAwait ( continueOnCapturedContext : false ) ;
636
- idBySchema [ schema ] = registeredSchema . Id ;
637
-
638
- var format = GetSchemaFormat ( schema . SchemaString ) ;
639
- schemaById . TryAdd ( new SchemaId ( registeredSchema . Id , format ) , registeredSchema . Schema ) ;
640
- schemaId = registeredSchema . Id ;
641
- }
642
-
643
- return schemaId ;
644
- }
645
- finally
616
+ idBySchema = idBySchemaBySubject . GetOrAdd ( subject , _ => new ConcurrentDictionary < Schema , Task < int > > ( ) ) ;
617
+ return await idBySchema . GetOrAdd ( schema , async _ =>
646
618
{
647
- cacheMutex . Release ( ) ;
648
- }
619
+ var registeredSchema = await LookupSchemaAsync ( subject , schema , true , normalize )
620
+ . ConfigureAwait ( continueOnCapturedContext : false ) ;
621
+
622
+ // We already have the schema so we can add it to the cache.
623
+ var format = GetSchemaFormat ( registeredSchema . SchemaString ) ;
624
+ schemaById . TryAdd ( new SchemaId ( registeredSchema . Id , format ) , Task . FromResult ( registeredSchema . Schema ) ) ;
625
+
626
+ return registeredSchema . Id ;
627
+ } ) . ConfigureAwait ( continueOnCapturedContext : false ) ;
649
628
}
650
629
651
630
@@ -656,41 +635,15 @@ public async Task<int> RegisterSchemaAsync(string subject, Schema schema, bool n
656
635
{
657
636
if ( idBySchema . TryGetValue ( schema , out var schemaId ) )
658
637
{
659
- return schemaId ;
638
+ return await schemaId ;
660
639
}
661
640
}
662
641
663
- await cacheMutex . WaitAsync ( ) . ConfigureAwait ( continueOnCapturedContext : false ) ;
664
- try
665
- {
666
- if ( ! this . idBySchemaBySubject . TryGetValue ( subject , out idBySchema ) )
667
- {
668
- idBySchema = new ConcurrentDictionary < Schema , int > ( ) ;
669
- idBySchemaBySubject . TryAdd ( subject , idBySchema ) ;
670
- }
671
-
672
- // TODO: This could be optimized in the usual case where idBySchema only
673
- // contains very few elements and the schema string passed in is always
674
- // the same instance.
675
-
676
- if ( ! idBySchema . TryGetValue ( schema , out int schemaId ) )
677
- {
678
- CleanCacheIfFull ( ) ;
679
-
680
- schemaId = await restService . RegisterSchemaAsync ( subject , schema , normalize )
681
- . ConfigureAwait ( continueOnCapturedContext : false ) ;
682
- idBySchema [ schema ] = schemaId ;
683
- }
684
-
685
- return schemaId ;
686
- }
687
- finally
688
- {
689
- cacheMutex . Release ( ) ;
690
- }
642
+ CleanCacheIfFull ( ) ;
643
+ idBySchema = idBySchemaBySubject . GetOrAdd ( subject , _ => new ConcurrentDictionary < Schema , Task < int > > ( ) ) ;
644
+ return await idBySchema . GetOrAddAsync ( schema , _ => restService . RegisterSchemaAsync ( subject , schema , normalize ) ) . ConfigureAwait ( continueOnCapturedContext : false ) ;
691
645
}
692
646
693
-
694
647
/// <inheritdoc/>
695
648
public Task < int > RegisterSchemaAsync ( string subject , string avroSchema , bool normalize = false )
696
649
=> RegisterSchemaAsync ( subject , new Schema ( avroSchema , EmptyReferencesList , SchemaType . Avro ) , normalize ) ;
@@ -712,31 +665,14 @@ public async Task<RegisteredSchema> LookupSchemaAsync(string subject, Schema sch
712
665
{
713
666
if ( registeredSchemaBySchema . TryGetValue ( schema , out var registeredSchema ) )
714
667
{
715
- return registeredSchema ;
668
+ return await registeredSchema ;
716
669
}
717
670
}
718
671
719
- await cacheMutex . WaitAsync ( ) . ConfigureAwait ( continueOnCapturedContext : false ) ;
720
- try
721
- {
722
- if ( ! registeredSchemaBySchemaBySubject . TryGetValue ( subject , out registeredSchemaBySchema ) )
723
- {
724
- CleanCacheIfFull ( ) ;
725
- registeredSchemaBySchema = new ConcurrentDictionary < Schema , RegisteredSchema > ( ) ;
726
- registeredSchemaBySchemaBySubject [ subject ] = registeredSchemaBySchema ;
727
- }
728
- if ( ! registeredSchemaBySchema . TryGetValue ( schema , out var registeredSchema ) )
729
- {
730
- registeredSchema = await restService . LookupSchemaAsync ( subject , schema , ignoreDeletedSchemas , normalize ) . ConfigureAwait ( continueOnCapturedContext : false ) ;
731
- registeredSchemaBySchema [ schema ] = registeredSchema ;
732
- }
733
-
734
- return registeredSchema ;
735
- }
736
- finally
737
- {
738
- cacheMutex . Release ( ) ;
739
- }
672
+ CleanCacheIfFull ( ) ;
673
+
674
+ registeredSchemaBySchema = registeredSchemaBySchemaBySubject . GetOrAdd ( subject , _ => new ConcurrentDictionary < Schema , Task < RegisteredSchema > > ( ) ) ;
675
+ return await registeredSchemaBySchema . GetOrAddAsync ( schema , _ => restService . LookupSchemaAsync ( subject , schema , ignoreDeletedSchemas , normalize ) ) . ConfigureAwait ( continueOnCapturedContext : false ) ;
740
676
}
741
677
742
678
/// <inheritdoc/>
@@ -745,25 +681,11 @@ public async Task<Schema> GetSchemaAsync(int id, string format = null)
745
681
var schemaId = new SchemaId ( id , format ) ;
746
682
if ( schemaById . TryGetValue ( schemaId , out var schema ) )
747
683
{
748
- return schema ;
684
+ return await schema ;
749
685
}
750
686
751
- await cacheMutex . WaitAsync ( ) . ConfigureAwait ( continueOnCapturedContext : false ) ;
752
- try
753
- {
754
- if ( ! this . schemaById . TryGetValue ( schemaId , out schema ) )
755
- {
756
- CleanCacheIfFull ( ) ;
757
- schema = await restService . GetSchemaAsync ( id , format ) . ConfigureAwait ( continueOnCapturedContext : false ) ;
758
- schemaById . TryAdd ( schemaId , schema ) ;
759
- }
760
-
761
- return schema ;
762
- }
763
- finally
764
- {
765
- cacheMutex . Release ( ) ;
766
- }
687
+ CleanCacheIfFull ( ) ;
688
+ return await schemaById . GetOrAddAsync ( schemaId , _ => restService . GetSchemaAsync ( id , format ) ) . ConfigureAwait ( continueOnCapturedContext : false ) ;
767
689
}
768
690
769
691
@@ -773,64 +695,36 @@ public async Task<Schema> GetSchemaBySubjectAndIdAsync(string subject, int id, s
773
695
var schemaId = new SchemaId ( id , format ) ;
774
696
if ( this . schemaById . TryGetValue ( schemaId , out var schema ) )
775
697
{
776
- return schema ;
698
+ return await schema ;
777
699
}
778
700
779
- await cacheMutex . WaitAsync ( ) . ConfigureAwait ( continueOnCapturedContext : false ) ;
780
- try
781
- {
782
- if ( ! this . schemaById . TryGetValue ( schemaId , out schema ) )
783
- {
784
- CleanCacheIfFull ( ) ;
785
- schema = await restService . GetSchemaBySubjectAndIdAsync ( subject , id , format )
786
- . ConfigureAwait ( continueOnCapturedContext : false ) ;
787
- schemaById . TryAdd ( schemaId , schema ) ;
788
- }
789
-
790
- return schema ;
791
- }
792
- finally
793
- {
794
- cacheMutex . Release ( ) ;
795
- }
701
+ return await schemaById . GetOrAddAsync ( schemaId , _ => restService . GetSchemaBySubjectAndIdAsync ( subject , id , format ) ) . ConfigureAwait ( continueOnCapturedContext : false ) ;
796
702
}
797
703
798
704
799
705
/// <inheritdoc/>
800
706
public async Task < RegisteredSchema > GetRegisteredSchemaAsync ( string subject , int version , bool ignoreDeletedSchemas = true )
801
707
{
802
- if ( schemaByVersionBySubject . TryGetValue ( subject , out var schemaByVersion ) &&
803
- schemaByVersion . TryGetValue ( version , out var schema ) )
804
- {
805
- return schema ;
806
- }
807
-
808
- await cacheMutex . WaitAsync ( ) . ConfigureAwait ( continueOnCapturedContext : false ) ;
809
- try
708
+ if ( schemaByVersionBySubject . TryGetValue ( subject , out var schemaByVersion ) )
810
709
{
811
- CleanCacheIfFull ( ) ;
812
-
813
- if ( ! schemaByVersionBySubject . TryGetValue ( subject , out schemaByVersion ) )
710
+ if ( schemaByVersion . TryGetValue ( version , out var schema ) )
814
711
{
815
- schemaByVersion = new ConcurrentDictionary < int , RegisteredSchema > ( ) ;
816
- schemaByVersionBySubject [ subject ] = schemaByVersion ;
712
+ return await schema ;
817
713
}
818
-
819
- if ( ! schemaByVersion . TryGetValue ( version , out schema ) )
820
- {
821
- schema = await restService . GetSchemaAsync ( subject , version )
822
- . ConfigureAwait ( continueOnCapturedContext : false ) ;
823
- schemaByVersion [ version ] = schema ;
824
- var format = GetSchemaFormat ( schema . SchemaString ) ;
825
- schemaById . TryAdd ( new SchemaId ( schema . Id , format ) , schema . Schema ) ;
826
- }
827
-
828
- return schema ;
829
- }
830
- finally
831
- {
832
- cacheMutex . Release ( ) ;
833
714
}
715
+
716
+ CleanCacheIfFull ( ) ;
717
+ schemaByVersion = schemaByVersionBySubject . GetOrAdd ( subject , _ => new ConcurrentDictionary < int , Task < RegisteredSchema > > ( ) ) ;
718
+ return await schemaByVersion . GetOrAddAsync ( version , async _ =>
719
+ {
720
+ var schema = await restService . GetSchemaAsync ( subject , version ) . ConfigureAwait ( continueOnCapturedContext : false ) ;
721
+
722
+ // We already have the schema so we can add it to the cache.
723
+ var format = GetSchemaFormat ( schema . SchemaString ) ;
724
+ schemaById . TryAdd ( new SchemaId ( schema . Id , format ) , Task . FromResult ( schema . Schema ) ) ;
725
+
726
+ return schema ;
727
+ } ) . ConfigureAwait ( continueOnCapturedContext : false ) ;
834
728
}
835
729
836
730
0 commit comments