Polling in Software Systems
Polling in Software Systems
Polling is a technique where a system repeatedly checks for changes or new data at regular intervals. It is often used when event-driven notifications (e.g., WebSockets, SQL Server notifications) are not available or feasible.
1. Types of Polling
a) Regular Polling (Fixed Interval)
- The system checks for updates at a fixed time interval (e.g., every 10 seconds).
- Simple to implement but can cause unnecessary load if updates are infrequent.
🔹 Example:
A background service in .NET Core checking for new database records every minute.
public class PollingService : BackgroundService
{
private readonly ILogger<PollingService> _logger;
public PollingService(ILogger<PollingService> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Checking for updates...");
CheckForUpdates(); // Replace with actual data check logic
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
}
}
private void CheckForUpdates()
{
// Query the database or API to check for changes
_logger.LogInformation("Polling database...");
}
}
✅ Pros:
- Simple to implement.
- No external dependencies.
❌ Cons:
- Wastes resources when there are no changes.
- Database or API load increases with shorter intervals.
b) Adaptive Polling (Backoff Strategy)
- Dynamically adjusts polling frequency based on system activity.
- If no changes occur, polling slows down (e.g., from 5 sec → 10 sec → 30 sec).
🔹 Example:
If the last 5 polls detected no changes, increase the delay.
public class AdaptivePollingService : BackgroundService
{
private readonly ILogger<AdaptivePollingService> _logger;
private int _pollingInterval = 5000; // Start with 5 seconds
private int _missedUpdates = 0;
public AdaptivePollingService(ILogger<AdaptivePollingService> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
bool dataChanged = CheckForUpdates();
if (dataChanged)
{
_pollingInterval = 5000; // Reset to 5 seconds
_missedUpdates = 0;
}
else
{
_missedUpdates++;
if (_missedUpdates >= 5) _pollingInterval = Math.Min(_pollingInterval * 2, 60000); // Max 60 sec
}
await Task.Delay(_pollingInterval, stoppingToken);
}
}
private bool CheckForUpdates()
{
// Simulate checking database for updates
_logger.LogInformation("Checking for updates...");
return new Random().Next(0, 10) < 2; // Simulate a 20% chance of change
}
}
✅ Pros:
- Reduces unnecessary database queries.
- Improves performance & efficiency.
❌ Cons:
- Delayed responses if updates occur after a long backoff.
- More complex than fixed interval polling.
c) Long Polling
- The client sends a request to the server and waits until a change occurs.
- If the server detects a change, it immediately responds.
- If no change occurs within a timeout (e.g., 30 sec), the server responds empty, and the client retries.
🔹 Example:
Long polling to get real-time notifications from a REST API.
[HttpGet("long-polling")]
public async Task<IActionResult> LongPolling(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
if (CheckForUpdates()) // If data changed, return response
return Ok(new { message = "New data available!" });
await Task.Delay(5000, cancellationToken); // Wait before checking again
}
return NoContent();
}
✅ Pros:
- Real-time updates without constant requests.
- Reduces unnecessary polling when no updates exist.
❌ Cons:
- Requires holding HTTP connections, which can be inefficient for large-scale applications.
2. Polling in Different Scenarios
3. Polling in .NET Core with Caching
🔹 Example: Polling SQL Server and caching the results in MemoryCache.
public class DatabasePollingService : BackgroundService
{
private readonly IMemoryCache _cache;
private readonly string _connectionString;
private DateTime _lastUpdated = DateTime.MinValue;
public DatabasePollingService(IMemoryCache cache, IConfiguration config)
{
_cache = cache;
_connectionString = config.GetConnectionString("DefaultConnection");
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await RefreshCacheIfNeeded();
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
}
}
private async Task RefreshCacheIfNeeded()
{
using var connection = new SqlConnection(_connectionString);
using var command = new SqlCommand("SELECT MAX(LastModified) FROM Products", connection);
connection.Open();
var lastModified = (DateTime?)await command.ExecuteScalarAsync();
if (lastModified > _lastUpdated)
{
_lastUpdated = lastModified ?? DateTime.MinValue;
_cache.Remove("ProductsCache"); // Clear cache
}
}
}
4. Polling vs Event-Driven Alternatives
5. When to Use Polling?
✔️ When event-driven options are not available.
✔️ When the data doesn’t change often (adaptive polling).
✔️ When you need simple implementation without extra dependencies.
❌ Avoid polling when real-time performance is required.
❌ Avoid polling when server load is a concern (use SQL Dependency or SignalR instead).
Conclusion
- Fixed polling is simple but wastes resources.
- Adaptive polling improves efficiency by dynamically adjusting the interval.
- Long polling provides real-time-like behavior without excessive requests.
- Consider event-driven solutions like SQL Dependency, WebSockets, or message queues for better performance.
Comments
Post a Comment