Query sur un GSI DynamoDB en Python (boto3)
Une requête sur un GSI est un query normal plus IndexName, et AlbumTitle-index nous donne les morceaux par album — un modèle d'accès que la clé de table Artist + SongTitle ne peut pas servir. Ce qui change en Python, c'est la gestion des erreurs : les deux erreurs d'index les plus courantes échouent dans des couches différentes de boto3, et une seule des deux est attrapable par classe d'exception.
Code
import boto3
client = boto3.client("dynamodb")
paginator = client.get_paginator("query")
items = []
for page in paginator.paginate(
TableName="Music",
IndexName="AlbumTitle-index",
KeyConditionExpression="#hashKey = :hashKeyValue",
ExpressionAttributeNames={"#hashKey": "AlbumTitle"},
ExpressionAttributeValues={":hashKeyValue": {"S": "Danzon"}},
):
items.extend(page["Items"])
print(f"Found {len(items)} songs on the album")except ValidationException ne compilera même pas, encore moins n'attrapera
Ajoute ConsistentRead=True à la requête ci-dessus et boto3 lève ceci, aussi bien sur l'API client que sur l'API resource :
botocore.exceptions.ClientError: An error occurred (ValidationException) when
calling the Query operation: Consistent reads are not supported on global
secondary indexesLe gestionnaire évident est except client.exceptions.ValidationException. Il n'existe pas :
AttributeError: <botocore.errorfactory.DynamoDBExceptions object> has no
attribute ValidationException. Valid exceptions are: BackupInUseException,
... IndexNotFoundException, ... ProvisionedThroughputExceededException, ...botocore génère les classes d'exception à partir du modèle de service, et DynamoDB en modélise 33. ValidationException est une erreur de niveau protocole et n'en fait pas partie : la seule branche fiable porte donc sur le code :
except ClientError as exc:
if exc.response["Error"]["Code"] == "ValidationException":
...L'asymétrie est réelle. Écris mal le nom de l'index et tu obtiens IndexNotFoundException, qui est modélisée et attrapable par classe. Détourne le flag de cohérence et tu obtiens une comparaison de chaînes. Ce sont deux erreurs d'index ; une seule a un type.
Le curseur porte aussi la clé de table
Le paginateur cache LastEvaluatedKey, mais il vaut la peine de savoir ce qu'il contient sur un index. Sur 300 morceaux d'un même album :
page 1: Count 271 capacity 128.5 LastEvaluatedKey ['AlbumTitle', 'Artist', 'SongTitle']
page 2: Count 29 capacity 14.0 LastEvaluatedKey []Une clé de GSI n'est pas unique : la clé d'index seule ne peut donc pas reprendre la lecture, et DynamoDB renvoie ensemble la clé d'index et la clé de la table de base. Une pagination faite maison qui ne stocke que la clé d'index répète ou perd des éléments.
Reproduit le 2026-07-28 sur DynamoDB Local (amazon/dynamodb-local) avec boto3 1.43.58 sur CPython 3.14.6. Le texte d'erreur et les listes de clés sont la sortie de la bibliothèque elle-même.
Explication
IndexNamene remplace pasTableName. Les deux vont dans le même appel, et laKeyConditionExpressionnomme la clé de partition de l'index (AlbumTitle), avec le même jeu d'opérateurs qu'une requête sur table.- Tu obtiens la projection et rien d'autre. L'index renvoie ce qu'il projette (
ALL,KEYS_ONLYou la listeINCLUDE) ; selon la référence de l'API, "global secondary index queries cannot fetch attributes from the parent table". Un attribut manquant signifie unget_itemde suivi sur la clé de base, ou une projection plus large sur un nouvel index. - Les éléments dépourvus de la clé d'index n'apparaissent jamais — le motif d'index épars. Il garde petit un index sur
status = "OPEN", et c'est aussi pourquoi une requête sur GSI peut renvoyer moins que prévu sans rien lever. - L'API resource prend le même
IndexName:table.query(IndexName="AlbumTitle-index", KeyConditionExpression=Key("AlbumTitle").eq("Danzon")), avec des valeurs Python natives en entrée et desDecimalen sortie. - Une écriture sur un GSI arrive après l'écriture sur la table. La réplication est asynchrone : un chemin de lecture-après-écriture visant l'index ratera de temps en temps. Le réessayer dans une boucle serrée brûle de la capacité sans accélérer la réplication.
Le faire visuellement
Le DynamoDB Expression Builder écrit la condition de clé d'index et la map de valeurs typées en Python prêt pour boto3, y compris les enveloppes {"S": ...} que le client exige et que l'API resource interdit.
Pour pointer la même requête d'index sur tes propres tables depuis un formulaire et lire les résultats dans une grille paginée, télécharge DynoTable.
Exemples liés
- Query sur un GSI DynamoDB en Node.js — la même requête d'index avec l'AWS SDK v3.
- Query sur un GSI DynamoDB avec l'AWS CLI — la même requête d'index depuis le shell.
- Query DynamoDB en Python — interroger la table de base.
- GSI vs. LSI — quel type d'index convient au modèle d'accès.
- Pourquoi les GSI sont en cohérence à terme — le décalage de réplication expliqué.
- "The table does not have the specified index" — le nom de l'index ne correspond pas (les noms de GSI sont sensibles à la casse).
- "Consistent reads are not supported on global secondary indexes" — pourquoi le flag de lecture fortement cohérente échoue sur un GSI.