Google Cloud Storage 1000-Read-/400-Write-Limit in Python umgehen

English Deutsch

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:

output.txt
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 call

Du 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.

datastore_chunk_client.py
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)):
            pass

Verwendungsbeispiel:

datastore_usage_example.py
# "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)

Check out similar posts by category: Cloud, Python