Google Cloud Storage 1000-Read-/400-Write-Limit in Python umgehen
Google Cloud Datastore hat ein eingebautes Limit von 1000 Schlüsseln für get-Anfragen und ein Limit von 400 Entitäten pro Anfrage für put. Wenn du dieses Limit erreichst, siehst du eine dieser Fehlermeldungen:
google.api_core.exceptions.InvalidArgument: 400 cannot get more than 1000 keys in a single call
google.api_core.exceptions.InvalidArgument: 400 cannot write more than 500 entities in a single callDu kannst dies beheben, indem du die Anfragen in Chunks aufteilst, d.h. nur 1000 Anfragen gleichzeitig für get usw. ausführst.
Dieser Code bietet ein einsatzbereites Beispiel für eine Klasse, die diesen Prozess automatisiert. Als zusätzlicher Vorteil führt sie die Anfragen in Chunks von 1000 (für get) oder 400 (für put) parallel mit einem concurrent.futures.Executor aus. Da die Leistung voraussichtlich IO-gebunden ist, wird empfohlen, einen concurrent.futures.ThreadPoolExecutor zu verwenden.
Wenn du der Klasse bei der Konstruktion keinen Executor übergibst, erstellt sie selbst einen.
import itertools
from concurrent.futures import ThreadPoolExecutor
def _chunks(l, n=1000):
"""
Yield aufeinanderfolgende n-große Chunks aus l.
https://stackoverflow.com/a/312464/2597135
"""
for i in range(0, len(l), n):
yield l[i:i + n]
def _get_chunk(client, keys):
"""
Einzelnen Chunk abrufen
"""
missing = []
vals = client.get_multi(keys, missing=missing)
return vals, missing
class DatastoreChunkClient(object):
"""
Bietet einen dünnen Wrapper um einen Google Cloud Datastore-Client,
der Möglichkeiten zum Lesen und Schreiben in Chunks bietet.
"""
def __init__(self, client, executor=None):
self.client = client
if executor is None:
executor = ThreadPoolExecutor(16)
self.executor = executor
def get_multi(self, keys):
"""
Dünner Wrapper um client.get_multi(), der das 1000-Read-Requests-Limit
umgeht, indem 1000-große chunked Reads parallel mit self.executor
durchgeführt werden.
Gibt (values, missing) zurück.
"""
all_missing = []
all_vals = []
for vals, missing in self.executor.map(lambda chunk: _get_chunk(self.client, chunk), _chunks(keys, 1000)):
all_vals += vals
all_missing += missing
return all_vals, all_missing
def put_multi(self, entities):
"""
Dünner Wrapper um client.put_multi(), der das 400-Write-Requests-Limit
umgeht, indem 400-große chunked Writes parallel mit self.executor
durchgeführt werden.
Gibt (values, missing) zurück.
"""
for none in self.executor.map(lambda chunk: self.client.put_multi(chunk), _chunks(entities, 400)):
passVerwendungsbeispiel:
# "Rohen" Google-Datastore-Client erstellen
client = datastore.Client(project="myproject-123456")
chunkClient = DatastoreChunkClient(client)
# Die Größe der Schlüsselliste ist nur durch den Arbeitsspeicher begrenzt
keys = [...]
values, missing = chunkClient.get_multi(keys)
# Die Größe der Entitätsliste ist nur durch den Arbeitsspeicher begrenzt
entities = [...]
chunkClient.put_multi(entities)