2014-12-11 21 views
5

Ecco il mio semplice codice Python memcached di seguito:Come aggiungere un elemento a un elenco memcached atomicamente (in Python)

import memcache 
memcache_client = memcache.Client(['127.0.0.1:11211'], debug=True) 
key = "myList" 
obj = ["A", "B", "C"] 
memcache_client.set(key, obj) 

Ora, supponiamo che io voglio per aggiungere un elemento "D" alla lista memorizzato nella cache come myList come posso farlo atomicamente?

So che questo è sbagliato, perché non è atomica:

memcache_client.set(key, memcache_client.get(key) + ["D"]) 

La dichiarazione di cui sopra contiene una condizione di competizione. Se un altro thread esegue questa stessa istruzione nel momento esatto in cui uno degli aggiornamenti si bloccherà.

Come posso risolvere questa condizione di gara? Come posso aggiornare un elenco o un dizionario memorizzato in memcached atomicamente?

+0

https://code.google.com/p/memcached/wiki/NewCommands – user3159253

+0

Probabilmente questa è la risposta, si dovrebbe usare 'cas()' (check-and-set) al posto del semplice 'set()' – user3159253

+0

quale lib di memcache python stai usando? – Anentropic

risposta

8

Ecco la funzione corrispondente API client pitone

https://cloud.google.com/appengine/docs/python/memcache/clientclass#Client_cas

Anche qui un nice tutorial da Guido van Rossum. Spero che avrebbe fatto meglio a spiegare roba pitone di me;)

Ecco come il codice dovrebbe essere simile nel tuo caso:

memcache_client = memcache.Client(['127.0.0.1:11211'], debug=True) 
key = "myList" 
while True: # Retry loop, probably it should be limited to some reasonable retries 
    obj = memcache_client.gets(key) 
    assert obj is not None, 'Uninitialized object' 
    if memcache_client.cas(key, obj + ["D"]): 
    break 

L'intero flusso di lavoro rimane la stessa: prima si recupera un valore (w/un po ' informazioni interne associate a una chiave), quindi modificare il valore recuperato, quindi tentare di aggiornarlo nella memcache. L'unica differenza è che il valore (in realtà, coppia chiave/valore) viene verificato che non è stato modificato simultaneamente da un processo parallelo. In quest'ultimo caso la chiamata fallisce e si dovrebbe riprovare il flusso di lavoro dall'inizio. Inoltre, se si dispone di un'applicazione multi-thread, allora ogni istanza di memcache_client dovrebbe essere thread-local.

Inoltre, non dimenticare che esistono metodi incr() e decr() per semplici contatori interi che sono "atomici" per loro natura.

+0

Puoi per favore mostrare come dovrei usare la dichiarazione cas nel mio caso sopra? I documenti non contengono un esempio. Inoltre, se possibile, mostrare come (in python) aggiungere atomicamente una chiave/valore a un dizionario memorizzato in memcached. –

+1

nota la sintassi 'gets' /' cas' è leggermente diversa se si utilizza 'pylibmc' http://sendapatch.se/projects/pylibmc/reference.html – Anentropic

-2

Se non si desidera ricevere una condizione di competizione, è necessario utilizzare Blocca primitiva dal modulo di threading. Per esempio

lock = threading.Lock() 

def thread_func(): 
    obj = get_obj() 
    lock.acquire() 
    memcache_client.set(key, obj) 
    lock.release() 
+0

c'è ancora la condizione di gara con _other_ client del server memcache – Anentropic

+0

@Anentropic Saqib Ali ha chiesto informazioni sui thread Python non su più server, quindi se utilizzerà solo un client con più thread la mia risposta è ancora valida. –