Pubblicato il 25/11/2023 da alnao nella categoria AWS

Il Cloud AWS mette a disposizione una serie di servizi per la gestione di base di dati di tipo Serverless, quindi il programmatore non deve preoccuparsi di gestire il server fisico/virtuale dove sono gestiti i dati ma deve solo preoccuparsi dei dati stessi. Il principale servizio di base dati DynamoDB è un servizio proprietario di AWS di tipo noSQL e schema-less quindi non si tratta di un database relazione e non si possono eseguire query nel linguaggio SQL ma sono presenti delle API che permettono di scrivere e leggere i dati. Si tratta di un database di tipo chiave-valore quindi bisogna sempre ricordare che ogni elemento deve sempre avere una chiave univoca che identifica un elemento, i dati contenuti negli elementi sono sempre rappresentati nel formato json standard e le chiavi univoche possono gestire il tipo HASH nativamente.

Il servizio è studiato per gestire carichi di lavoro di qualsiasi tipo, può gestire da poche richieste a più di 10 trilioni di richieste al giorno; infatti la disponibilità, la durabilità e tolleranza ai guasti sono funzionalità integrate che eliminano la necessità di progettare le applicazioni per queste funzionalità. Il costo del servizio è calcolato in base alla quantità di dati e al numero di richieste con 25 GB di archiviazione e fino a 200 milioni di richieste di lettura/scrittura al mese con il Piano gratuito AWS. Si rimanda sempre alla documentazione ufficiale per verificare costi e caratteristiche di DynamoDB.

Il servizio prevede molti sotto-servizi come DynamoDB stream, editor PartiQL, gestione di Clusters e molto altro ma bisogna sempre ricordare che l’entità base di questo servizio sono le tabelle che necessitano “sempre” una partition-key e una sort-key, campi necessari per permettere al servizio di gestire correttamente ai dati.

Nella console è possibile le tabella con una intuitiva console amministrativa:

Dove è possibile anche visualizzare e modificare gli item manualmente:


Con la CLI è possibile gestire tabelle in maniera molto semplice, infatti sono disponibili comandi per tutte le operazioni necessarie sulla base di dati ben descritti nella documentazione ufficiali, i principali comandi sono:

  • elenco delle tabelle:
    aws dynamodb list-tables
  • creazione di una tabella:
    aws dynamodb create-table --table-name nome-tabella --attribute-definitions AttributeName=id,AttributeType=S --key-schema AttributeName=id,KeyType=HASH --table-class STANDARD --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5
  • scrittura di un elemento in una tabella:
    aws dynamodb put-item --table-name nome-tabella --item '{"id": {"S": "1"}, "Name": {"S": "Alberto"}}'
  • recupero di un elemento da una tabella:
    aws dynamodb get-item --consistent-read --table-name nome-tabella --key '{ "id": {"S": "1"}}'
  • operazione di lettura di tutti i dati di una tabella:
    aws dynamodb scan --table-name nome-tabella
  • operazione di lettura di tutti i dati di una tabella filtrando i campi recuperati:
    aws dynamodb scan --table-name nome-tabella --query "Items[*].[id.S,Name.S]"
  • scarico di tutti gli elementi di una tabella in formato json in un file:
    aws dynamodb scan --table-name nome-tabella --select ALL_ATTRIBUTES --output json > a.json
  • eliminazione di una tabella:
    aws dynamodb delete-table --table-name nome-tabella

Bisogna sempre ricordare che il comando che cancella una tabella esegue anche la cancellazione dei dati contenuti, non essendoci nessun controllo sulla presenta di dati in una tabella, bisogna sempre prestare attenzione quando si lancia questo comando per evitare che i dati vengano cancellati per sbaglio.


La libreria SDK boto3 permette di gestire da codice le tabelle in maniera veloce e semplice, la documentazione ufficiale è veramente ricca di esempi, i principali metodi sono:

def table_list(profile_name):
  boto3.setup_default_session(profile_name=profile_name)
  client = boto3.client('dynamodb')
  response = client.list_tables( Limit=100)
  tables = []
  for table in response['TableNames']:
    tables.append(table)
  return tables
def write_element_with_id(profile_name, table,element):
  boto3.setup_default_session(profile_name=profile_name)
  client = boto3.client('dynamodb')
  if 'id' not in element:
    element['id'] = str(uuid.uuid4()) #calcolo id univoco
  ts= TypeSerializer()
  serialized_post= ts.serialize(element)["M"]
  res = client.put_item(TableName=table,Item=serialized_post)
  res['id']=element['id']
  return res
def get_element_by_id(profile_name,table,id):
  boto3.setup_default_session(profile_name=profile_name)
  client = boto3.client('dynamodb')
  response = client.get_item( Key={ 'id': {'S': id, } } , TableName=table,)
  return response['Item']
def delete_element_by_id(profile_name,table,id):
  boto3.setup_default_session(profile_name=profile_name)
  client = boto3.client('dynamodb')
  response = client.delete_item( Key={ 'id': {'S': id, } } , TableName=table,)
  return response
def scan_table(profile_name,table):
  boto3.setup_default_session(profile_name=profile_name)
  client = boto3.client('dynamodb')
  response = client.scan(TableName=table,Limit=123)
  return response['Items']
def full_scan_table(profile_name,table):
  boto3.setup_default_session(profile_name=profile_name)
  dynamodb = boto3.resource('dynamodb')#, region_name="eu-west-1"
  table = dynamodb.Table(table)
  today = datetime.datetime.now()
  response = table.scan( ) #FilterExpression=Attr('country').eq('US') & Attr('city').eq('NYC')
  data = response['Items']
  while 'LastEvaluatedKey' in response:
    response = table.scan(ExclusiveStartKey=response['LastEvaluatedKey'], ) #FilterExpression
    data.extend(response['Items'])
  return data

Come si può notare per eseguire la ricerca si usa il metodo scan che prevede un sistema paginante con il parametro LastEvaluatedKey che permette le ricerche multiple, inoltre nelle esecuzioni del comando scan è possibile aggiungere il parametro FilterExpression se necessario aggiungere filtri su attributi non chiave, nell’esempio qui presente si usa un attributi id come chiave primaria ma è possibile costruire chiavi con chiavi primarie più complesse.


Con CloudFormation è possibile creare tabelle in maniera veloce e velocissima, il tipo specifico è veramente basico e prevede come parametri il nome, la definizione della chiave primaria e i parametri del “Throughput”.

Dynamo:
  Type: AWS::DynamoDB::Table
  Properties:
    TableName: !Ref DynamoName
    AttributeDefinitions:
      -
        AttributeName: "id"
        AttributeType: "S"
    KeySchema:
      -
        AttributeName: "id"
        KeyType: "HASH"
    ProvisionedThroughput:
      ReadCapacityUnits: "5"
      WriteCapacityUnits: "5"

L’esempio completo e funzionante è disponibile al solito repository

https://github.com/alnao/AWSCloudFormationExamples/tree/master/Esempio11dynamoApiCrud

Ovviamente questo servizio è studiato per interagire con altri servizi di tipo Serverless di AWS come Lambda, SQS, Step function e Glue.

In questo esempio è presente una lambda che scrive e legge i valori dalla tabella, in successivi esempi saranno introdotte anche altre operazioni con altri servizi.

MENU