1
+ using System . Collections . Concurrent ;
1
2
using System . Diagnostics ;
2
3
using NTorSpectator . Observer . TorIntegration ;
3
4
using NTorSpectator . Services ;
@@ -22,15 +23,13 @@ public class SpectatorJob : IJob
22
23
23
24
private readonly ILogger < SpectatorJob > _logger ;
24
25
private readonly ISitesCatalogue _sitesCatalogue ;
25
- private readonly TorControlManager _torControl ;
26
- private readonly ISiteObserver _siteObserver ;
27
-
28
- public SpectatorJob ( ILogger < SpectatorJob > logger , ISitesCatalogue sitesCatalogue , TorControlManager torControl , ISiteObserver siteObserver )
26
+ private readonly IServiceProvider _services ;
27
+
28
+ public SpectatorJob ( ILogger < SpectatorJob > logger , ISitesCatalogue sitesCatalogue , IServiceProvider services )
29
29
{
30
30
_logger = logger ;
31
31
_sitesCatalogue = sitesCatalogue ;
32
- _torControl = torControl ;
33
- _siteObserver = siteObserver ;
32
+ _services = services ;
34
33
}
35
34
36
35
public async Task Execute ( IJobExecutionContext context )
@@ -40,50 +39,74 @@ public async Task Execute(IJobExecutionContext context)
40
39
_logger . LogDebug ( "Starting sites observations" ) ;
41
40
var sites = await _sitesCatalogue . GetAllSites ( ) ;
42
41
_logger . LogDebug ( "Got {Count} sites to observe" , sites . Count ) ;
43
-
44
- var siteQueue = new Queue < QueuedSite > ( sites . Select ( x => new QueuedSite ( x , 0 ) ) ) ;
45
- while ( siteQueue . TryDequeue ( out var queuedSite ) )
42
+ var queuedSites = sites . Select ( x => new QueuedSite ( x , 0 ) ) ;
43
+ var siteQueue = new ConcurrentQueue < QueuedSite > ( queuedSites ) ;
44
+ while ( true )
46
45
{
47
46
QueueLength . Set ( siteQueue . Count ) ;
48
- using var _ = _logger . BeginScope ( new Dictionary < string , object > { { "HiddenService" , queuedSite . Site . SiteUri } } ) ;
49
- _logger . LogDebug ( "Starting observations on the next site" ) ;
50
- try
51
- {
52
- var observations = await ObserveSite ( queuedSite . Site . SiteUri ) ;
53
- ObservationsCount . Inc ( ) ;
54
- if ( ! observations . IsOk )
55
- {
56
- _logger . LogDebug ( "Site observed as not available" ) ;
57
- var siteObservationsCount = queuedSite . ObservationsCount ;
58
- if ( siteObservationsCount < 3 )
59
- {
60
- _logger . LogDebug ( "Site has been observed {Count} times, returning it to queue" , siteObservationsCount ) ;
61
- siteQueue . Enqueue ( queuedSite with { ObservationsCount = siteObservationsCount + 1 } ) ;
62
- RetriesCount . Inc ( ) ;
63
- continue ;
64
- }
65
- }
66
- _logger . LogDebug ( "Site seems to be up" ) ;
67
- await _siteObserver . AddNewObservation ( queuedSite . Site . SiteUri , observations . IsOk ) ;
68
- SiteStatus . WithLabels ( queuedSite . Site . SiteUri ) . Set ( observations . IsOk ? 1 : 0 ) ;
69
- _logger . LogInformation ( "Site observed" ) ;
70
- }
71
- catch ( Exception e )
72
- {
73
- _logger . LogError ( e , "Observation for site failed" ) ;
74
- }
47
+ if ( ! siteQueue . Any ( ) )
48
+ break ;
49
+ var chunk = TryDequeueMax ( siteQueue , 4 ) ;
50
+ var tasks = chunk . Select ( x => TryCheckSite ( x , s => siteQueue . Enqueue ( s ) ) ) . ToArray ( ) ;
51
+ await Task . WhenAll ( tasks ) ;
75
52
}
76
53
_logger . LogDebug ( "The queue is finally empty, observations finished" ) ;
77
54
sw . Stop ( ) ;
78
55
TotalSessionDuration . Set ( sw . ElapsedMilliseconds ) ;
79
56
}
57
+
58
+
59
+ private QueuedSite [ ] TryDequeueMax ( ConcurrentQueue < QueuedSite > queue , int max )
60
+ {
61
+ var result = new List < QueuedSite > ( max ) ;
62
+ for ( int i = 0 ; i < max ; ++ i )
63
+ {
64
+ if ( queue . TryDequeue ( out var item ) )
65
+ result . Add ( item ) ;
66
+ }
67
+ return result . ToArray ( ) ;
68
+ }
80
69
70
+ private async Task TryCheckSite ( QueuedSite queuedSite , Action < QueuedSite > putBackIntoQueue )
71
+ {
72
+ using var _ = _logger . BeginScope ( new Dictionary < string , object > { { "HiddenService" , queuedSite . Site . SiteUri } } ) ;
73
+ using var scope = _services . CreateScope ( ) ;
74
+ var torControl = scope . ServiceProvider . GetRequiredService < TorControlManager > ( ) ;
75
+ var siteObserver = scope . ServiceProvider . GetRequiredService < ISiteObserver > ( ) ;
76
+ _logger . LogDebug ( "Starting observations on the next site" ) ;
77
+ try
78
+ {
79
+ var observations = await ObserveSite ( queuedSite . Site . SiteUri , torControl ) ;
80
+ ObservationsCount . Inc ( ) ;
81
+ if ( ! observations . IsOk )
82
+ {
83
+ _logger . LogDebug ( "Site observed as not available" ) ;
84
+ var siteObservationsCount = queuedSite . ObservationsCount ;
85
+ if ( siteObservationsCount < 3 )
86
+ {
87
+ _logger . LogDebug ( "Site has been observed {Count} times, returning it to queue" , siteObservationsCount ) ;
88
+ putBackIntoQueue ( queuedSite with { ObservationsCount = siteObservationsCount + 1 } ) ;
89
+ RetriesCount . Inc ( ) ;
90
+ return ;
91
+ }
92
+ }
93
+ _logger . LogDebug ( "Site seems to be up" ) ;
94
+ await siteObserver . AddNewObservation ( queuedSite . Site . SiteUri , observations . IsOk ) ;
95
+ SiteStatus . WithLabels ( queuedSite . Site . SiteUri ) . Set ( observations . IsOk ? 1 : 0 ) ;
96
+ _logger . LogInformation ( "Site observed" ) ;
97
+ }
98
+ catch ( Exception e )
99
+ {
100
+ _logger . LogError ( e , "Observation for site failed" ) ;
101
+ }
102
+ }
103
+
81
104
private record QueuedSite ( Site Site , int ObservationsCount ) ;
82
105
83
- private async Task < TorWatchResults > ObserveSite ( string site )
106
+ private async Task < TorWatchResults > ObserveSite ( string site , TorControlManager torControl )
84
107
{
85
108
using var _ = RequestDuration . NewTimer ( ) ;
86
- var torReply = await _torControl . HsFetch ( site ) ;
109
+ var torReply = await torControl . HsFetch ( site ) ;
87
110
var positive = torReply . Count ( x => x . Action == HsDescAction . Received ) ;
88
111
var negative = torReply . Count ( x => x . Action == HsDescAction . Failed ) ;
89
112
return new ( site , positive , negative ) ;
0 commit comments