Aspnet caching strategy / pattern

1

Nel mio controller WebApi ho il seguente codice (pseudo) che riceve le notifiche di aggiornamento da API in tempo reale di Instagram :

[HttpPost]
public void Post(InstagramUpdate instagramUpdate)
{
    var subscriptionId = instagramUpdate.SubscriptionId;
    var lastUpdate = GetLastUpdate(subscriptionId);

    // To avoid breaking my Instagram request limit, do not fetch new images too often.
    if (lastUpdate.AddSeconds(5) < DateTime.UtcNow)
    {
        // More than 5 seconds ago since last update for this subscription. Get new images
        GetNewImagesFromInstagram(subscriptionId);
        UpdateLastUpdate(subscriptionId, DateTime.UtcNow);
    }
}

Questo non funzionerà molto bene se ricevo due notifiche di aggiornamento per la stessa sottoscrizione quasi contemporaneamente, poiché lastUpdate non sarà stata aggiornata fino a dopo che la prima richiesta è stata elaborata. E anche se aggiorno lastUpdate direttamente, c'è un "ritardo" a causa del round trip nel database.

Quale sarebbe il modo migliore per affrontare questo problema? Sto pensando di usare un qualche tipo di cache (che sarebbe più veloce del roundtrip del database), ma non sono sicuro di come. C'è qualche tipo di best practice per questo genere di cose? Suppongo che sia un problema comune: "ricevere notifiche, fare qualcosa se qualcosa non è stato fatto di recente ..."

    
posta Joel 20.03.2015 - 08:11
fonte

2 risposte

2

Ho un altro suggerimento per il meccanismo: quando arriva un aggiornamento, contrassegna un abbonamento come se dovessi aggiornarlo e attendi il tuo ritardo prima di effettuare l'aggiornamento. Cioè, non c'è bisogno di una cache, hai solo bisogno di memorizzare alcuni stati per ogni ID di sottoscrizione come "dover aggiornare" o meno.

Userò async perché rende il codice piuttosto semplice.

Ad esempio:

private struct Empty {}
private ConcurrentDictionary<int, Empty> subscriptionsToUpdate = new ConcurrentDictionary<int, Empty>();

[HttpPost]
public async Task PostAsync(InstagramUpdate instagramUpdate)
{
    var subscriptionId = instagramUpdate.SubscriptionId;

    if (subscriptionsToUpdate.TryAdd(subscriptionId, default(Empty)) {
        await Task.Delay(TimeSpan.FromSeconds(5));

        if (subscriptionsToUpdate.TryRemove(subscriptionId)) {
            GetNewImagesFromInstagram(subscriptionId);
            UpdateLastUpdate(subscriptionId, DateTime.UtcNow);
        }
    }
}

Si noti che l'implementazione qui considera solo le richieste gestite da una singola procedura API Web. Ad esempio, se si dispone di più server, tale codice potrebbe potenzialmente effettuare una richiesta ogni cinque secondi per ciascun server in uso. Se si desidera evitare ciò, è necessario utilizzare una sorta di archivio condiviso anziché ConcurrentDictionary . (Vorrei raccomandare qualcosa come un archivio di valori chiave, come Redis o memcached.))

    
risposta data 20.03.2015 - 11:56
fonte
1

Che ne dici di qualcosa di simile?

private static readonly object _syncRoot = new object();

[HttpPost]
public void Post(InstagramUpdate instagramUpdate)
{
    var subscriptionId = instagramUpdate.SubscriptionId;
    var lastUpdate = GetLastUpdate(subscriptionId);

    // To avoid breaking my Instagram request limit, do not fetch new images too often.
    if (lastUpdate.AddSeconds(5) < DateTime.UtcNow)
    {
        lock (_syncRoot)
        {
           lastUpdate = GetLastUpdate(subscriptionId);

           if (lastUpdate.AddSeconds(5) < DateTime.UtcNow){
               GetNewImagesFromInstagram(subscriptionId);
               UpdateLastUpdate(subscriptionId, DateTime.UtcNow);
           }
        }
    }

}

    
risposta data 21.03.2015 - 17:47
fonte

Leggi altre domande sui tag