• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ThreeMammals / Ocelot / 26687890573

30 May 2026 03:30PM UTC coverage: 95.403% (-0.01%) from 95.414%
26687890573

push

github

raman-m
Pre-Release Ocelot v25 beta 3 and Ocelot.Testing v25 beta 8

6766 of 7092 relevant lines covered (95.4%)

2869.19 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.97
src/Ocelot/Configuration/Repository/FileConfigurationPoller.cs
1
using Microsoft.Extensions.Hosting;
2
using Newtonsoft.Json;
3
using Ocelot.Configuration.Creator;
4
using Ocelot.Configuration.File;
5
using Ocelot.DependencyInjection;
6
using Ocelot.Logging;
7

8
namespace Ocelot.Configuration.Repository;
9

10
/// <summary>
11
/// This hosted service pools periodically (~1 sec) configuration data from the <see cref="IFileConfigurationRepository"/> and propagates data into the <see cref="IInternalConfigurationRepository"/> to be reused across Ocelot services.
12
/// Thus, this service is responsible for getting actual configuration from anywhere and reflect the state to internal Ocelot repo.
13
/// </summary>
14
/// <remarks>
15
/// Feature: <see cref="IOcelotBuilder.AddConfigurationPoller()"/>.<br/>
16
/// Feature PR: <see href="https://github.com/ThreeMammals/Ocelot/pull/157/">157</see>.<br/>
17
/// Note, that the service is reused in Consul service discovery provider.
18
/// </remarks>
19
public class FileConfigurationPoller : IFileConfigurationPoller, IHostedService, IDisposable
20
{
21
    private readonly IOcelotLogger _logger;
22
    private readonly IFileConfigurationRepository _repo;
23
    private string _previousAsJson;
24
    private Timer _timer;
25
    private volatile bool _polling;
26
    private readonly IFileConfigurationPollerOptions _options;
27
    private readonly IInternalConfigurationRepository _internalConfigRepo;
28
    private readonly IInternalConfigurationCreator _internalConfigCreator;
29

30
    public FileConfigurationPoller(
21✔
31
        IOcelotLoggerFactory factory,
21✔
32
        IFileConfigurationRepository repo,
21✔
33
        IFileConfigurationPollerOptions options,
21✔
34
        IInternalConfigurationRepository internalConfigRepo,
21✔
35
        IInternalConfigurationCreator internalConfigCreator)
21✔
36
    {
21✔
37
        _internalConfigRepo = internalConfigRepo;
21✔
38
        _internalConfigCreator = internalConfigCreator;
21✔
39
        _options = options;
21✔
40
        _logger = factory.CreateLogger<FileConfigurationPoller>();
21✔
41
        _repo = repo;
21✔
42
        _previousAsJson = string.Empty;
21✔
43
    }
21✔
44

45
    private void OnTimer(object state)
46
    {
13✔
47
        if (_polling)
13✔
48
            return;
1✔
49

50
        try
51
        {
12✔
52
            Poll(); // PollAsync().GetAwaiter().GetResult(); // TODO This is not good, TimerCallback must be synchronous
12✔
53
        }
12✔
54
        finally
55
        {
12✔
56
            _polling = false;
12✔
57
        }
12✔
58
    }
13✔
59

60
    public async Task StartAsync(CancellationToken cancellationToken)
61
    {
11✔
62
        if (_timer is not null)
11✔
63
            return;
1✔
64

65
        _logger.LogInformation(() => $"{nameof(FileConfigurationPoller)} is starting.");
10✔
66
        int delay = await _options.DelayAsync(cancellationToken);
10✔
67
        _timer = new(OnTimer, null, delay, delay); // TODO state could be CancellationToken?
10✔
68
    }
11✔
69

70
    public Task StopAsync(CancellationToken cancellationToken)
71
    {
3✔
72
        if (_timer is null)
3✔
73
            return Task.CompletedTask;
1✔
74

75
        _logger.LogInformation(() => $"{nameof(FileConfigurationPoller)} is stopping.");
2✔
76
        return Task.Run(() => _timer.Change(Timeout.Infinite, 0), cancellationToken);
2✔
77
    }
3✔
78

79
    public void Poll()
80
    {
15✔
81
        if (_polling)
15✔
82
            return;
×
83

84
        _polling = true;
15✔
85
        _logger.LogInformation(() => $"{nameof(Poll)}: Started polling");
15✔
86

87
        FileConfiguration configuration;
88
        try
89
        {
15✔
90
            configuration = _repo.Get();
15✔
91
        }
14✔
92
        catch (Exception e)
1✔
93
        {
1✔
94
            _logger.LogWarning(() => $"{nameof(Poll)}: Error getting {nameof(FileConfiguration)} -> {e}.");
1✔
95
            return;
1✔
96
        }
97

98
        if (configuration is null)
14✔
99
        {
3✔
100
            _logger.LogWarning(() => $"{nameof(Poll)}: Null object while getting {nameof(FileConfiguration)} via the {_repo.GetType().Name} service.");
3✔
101
            return;
3✔
102
        }
103

104
        var asJson = ToJson(configuration);
11✔
105
        if (asJson != _previousAsJson)
11✔
106
        {
7✔
107
            var config = _internalConfigCreator.Create(configuration).GetAwaiter().GetResult(); // TODO Extend interface with sync version
7✔
108
            if (!config.IsError)
7✔
109
                _internalConfigRepo.AddOrReplace(config.Data);
6✔
110

111
            _previousAsJson = asJson;
7✔
112
        }
7✔
113

114
        _logger.LogInformation(() => $"{nameof(Poll)}: Finished polling");
11✔
115
    }
15✔
116

117
    public async Task PollAsync(CancellationToken cancellationToken = default)
118
    {
7✔
119
        if (_polling)
7✔
120
            return;
2✔
121

122
        _polling = true;
5✔
123
        _logger.LogInformation(() => $"{nameof(PollAsync)}: Started polling");
5✔
124

125
        FileConfiguration configuration;
126
        try
127
        {
5✔
128
            configuration = await _repo.GetAsync(cancellationToken);
5✔
129
        }
4✔
130
        catch (Exception e)
1✔
131
        {
1✔
132
            _logger.LogWarning(() => $"{nameof(PollAsync)}: Error getting {nameof(FileConfiguration)} -> {e}.");
1✔
133
            return;
1✔
134
        }
135

136
        if (configuration is null)
4✔
137
        {
1✔
138
            _logger.LogWarning(() => $"{nameof(PollAsync)}: Null object while getting {nameof(FileConfiguration)} via the {_repo.GetType().Name} service.");
139
            return;
1✔
140
        }
141

142
        var asJson = ToJson(configuration);
3✔
143
        if (asJson != _previousAsJson)
3✔
144
        {
3✔
145
            var config = await _internalConfigCreator.Create(configuration);
3✔
146
            if (!config.IsError)
3✔
147
                _internalConfigRepo.AddOrReplace(config.Data);
2✔
148

149
            _previousAsJson = asJson;
3✔
150
        }
3✔
151

152
        _logger.LogInformation(() => $"{nameof(PollAsync)}: Finished polling");
3✔
153
    }
7✔
154

155
    /// <summary>
156
    /// We could do object comparison here but performance isnt really a problem. This might be an issue one day!.
157
    /// </summary>
158
    /// <returns>hash of the config.</returns>
159
    private static string ToJson(FileConfiguration config)
160
    {
14✔
161
        var currentHash = JsonConvert.SerializeObject(config); // TODO WTF?
14✔
162
        return currentHash;
14✔
163
    }
14✔
164

165
    public void Dispose()
166
    {
23✔
167
        _timer?.Dispose();
23✔
168
        _timer = null;
23✔
169
        GC.SuppressFinalize(this);
23✔
170
    }
23✔
171
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc