In questo modulo, eseguirai il provisioning di un database Amazon DynamoDB e imparerai a utilizzare DynamoDB per archiviare informazioni sul tuo gioco basato su turni.

Tempo necessario per completare il modulo: 30 minuti


Amazon DynamoDB è un database NoSQL completamente gestito di AWS. Fornisce tempi di risposta di millisecondi a una cifra e una scalabilità pressoché infinita. DynamoDB viene utilizzato da un'ampia gamma di applicazioni e settori industriali, dal carrello di Amazon.com al servizio di geolocalizzazione di Lyft e a un'ampia varietà di giochi online.

Tutta l'interazione con DynamoDB viene effettuata tramite HTTPS, con l'uso di AWS Identity and Access Management (IAM) per l'autenticazione e l'autorizzazione. Di norma, per far interagire DynamoDB la lingua scelta con DynamoDB si utilizza SDK AWS. Se utilizzi le opzioni di calcolo AWS per l'applicazione, come Amazon Elastic Compute Cloud (Amazon EC2) o AWS Lambda, l'applicazione può usare le credenziali AWS presenti nell'ambiente di calcolo per effettuare richieste a DynamoDB.

Nei passaggi seguenti, in primo luogo effettuerai il provisioning di un database DynamoDB, quindi apprenderai come interagire con il database DynamoDB utilizzando SDK AWS per JavaScript in Node.js.


  • Fase 1. Provisioning di un database Amazon DynamoDB

    Per prima cosa, eseguiamo il provisioning di un database DynamoDB. Un database all'interno di DynamoDB è denominato anche tabella.

    Quando crei una tabella DynamoDB, devi specificare gli attributi che andranno a formare la chiave principale della tabella. Ciascun record che scrivi nella tabella DynamoDB è denominato vocee ciascuna voce deve includere la chiave principale della tabella.

    La modellazione dei dati DynamoDB e la progettazione della chiave principale sono un argomento importante. Il tuo gioco, tuttavia, presenta un semplice modello di accesso che non richiede la modellazione dei dati avanzata, pertanto in questo tutorial non verranno trattati argomenti avanzati sui modelli di dati DynamoDB. Per ulteriori dettagli sulla modellazione dei dati in DynamoDB, puoi leggere i tutorial su Progettazione di un database per un'app per dispositivi mobili con Amazon DynamoDB o su Modellazione dei dati di un'applicazione di gioco con Amazon DynamoDB.

    DynamoDB viene utilizzato nell'applicazione come archivio di valori a chiave semplice. Ciascun gioco presenta un attributo gameId che lo identifica in modo univoco. L'attributo gameId viene utilizzato come chiave principale per la tabella.

    Nella directory scripts/ esiste un file denominato create-table.sh che crea la tabella DynamoDB con l'interfaccia a riga di comando AWS (CLI AWS). I contenuti del file sono i seguenti:
     

    aws dynamodb create-table \
      --table-name turn-based-game \
      --attribute-definitions '[
        {
          "AttributeName": "gameId",
          "AttributeType": "S"
        }
      ]' \
      --key-schema '[
        {
          "AttributeName": "gameId",
          "KeyType": "HASH"
        }
      ]' \
      --provisioned-throughput '{
        "ReadCapacityUnits": 5,
        "WriteCapacityUnits": 5
      }'

    In primo luogo, il file assegna alla tabella un nome turn-based-game. Quindi, dichiara gli attributi utilizzati nella chiave principale della tabella. Nell'esempio viene utilizzata una chiave principale semplice, in tal modo dichiari solo un singolo attributo, gameId, che è di un tipo di dato di stringa. Successivamente, specifichi lo schema della chiave principale affermando che l'attributo gameId viene utilizzato come chiave hash della tabella.

    Infine, specifichi il numero di unità di capacità di lettura e scrittura desiderate per la tabella. DynamoDB non è dotato di una modalità dei prezzi on demand dove paghi ciascuna richiesta di lettura e scrittura a fronte della tabella. Tuttavia, la modalità di throughput fornita si adatta al piano gratuito AWS, pertanto puoi utilizzarla qui.

    Crea la tua tabella eseguendo il seguente comando nel tuo terminale:

    bash scripts/create-table.sh

    Dovresti visualizzare l'output seguente nel terminale:

    {
        "TableDescription": {
            "AttributeDefinitions": [
                {
                    "AttributeName": "gameId",
                    "AttributeType": "S"
                }
            ],
            "TableName": "turn-based-game",
            "KeySchema": [
                {
                    "AttributeName": "gameId",
                    "KeyType": "HASH"
                }
            ],
            "TableStatus": "CREATING",
            "CreationDateTime": 1574086642.07,
            "ProvisionedThroughput": {
                "NumberOfDecreasesToday": 0,
                "ReadCapacityUnits": 5,
                "WriteCapacityUnits": 5
            },
            "TableSizeBytes": 0,
            "ItemCount": 0,
            "TableArn": "arn:aws:dynamodb:us-east-1:955617200811:table/turn-based-game",
            "TableId": "c62cb86a-211e-4a50-a160-4a616c8f3445"
        }
    }
  • Fase 2. Salvataggio di una voce di gioco di esempio nella tabella

    Adesso che l'hai creata, puoi aggiungere voci nella tabella. Ogni gioco è rappresentato da una singola voce nella tabella.

    Lo schema di ciascuna voce di gioco è il seguente:

    Ogni gioco comprende un GameId, ossia un identificatore univoco per il gioco. Gli attributi User1 e User2 archiviano i nomi utenti dei due utenti che stanno giocando. Gli attributi Heap1, Heap2 e Heap3 archiviano il numero di oggetti in ciascuno dei tre heap. Infine, l'attributo LastMoveBy indica il giocatore che ha fatto la mossa più recente.

    Nella directory scripts/, è presente un file createGame.js che aggiunge un gioco di esempio alla tabella. I contenuti del file sono i seguenti:

    const AWS = require('aws-sdk')
    const documentClient = new AWS.DynamoDB.DocumentClient()
    
    const params = {
      TableName: 'turn-based-game',
      Item: {
        gameId: '5b5ee7d8',
        user1: 'myfirstuser',
        user2: 'theseconduser',
        heap1: 5,
        heap2: 4,
        heap3: 5,
        lastMoveBy: 'myfirstuser'
      }
    }
    
    documentClient.put(params).promise()
      .then(() => console.log('Game added successfully!'))
      .catch((error) => console.log('Error adding game', error))

    Dopo aver importato l'SDK AWS, crei un'istanza del client per documenti DynamoDB. Il client per documenti è un'astrazione di livello superiore rispetto all'API DynamoDB di basso livello e facilita l'uso delle voci DynamoDB. Dopo aver creato il client, lo script assembla i parametri per una chiamata API PutItem , includendo in nome e gli attributi della tabella nella voce. Richiama quindi il metodo put() sul client per documenti e disconnette le informazioni sulla riuscita o non riuscita dell'operazione.

    Puoi eseguire lo script per inserire un gioco nella tabella eseguendo il seguente comando nel terminale:

    node scripts/createGame.js

    Dovresti visualizzare l'output seguente nel terminale:

    Game added successfully!

    Nota: se esegui il comando troppo velocemente, potresti ricevere un errore di tabella non ancora disponibile. Attendi un minuto, quindi prova ad eseguire di nuovo il comando.

    Ottimo! Adesso hai aggiunto un singolo gioco alla tabella. Nella fase successiva, apprenderai ad aggiornare la voce del gioco per simulare un utente che effettua una mossa.

  • Fase 3. Aggiornamento di una voce di gioco nella tabella

    Ora che hai inserito una voce di gioco nella tabella, puoi imparare a simulare un giocatore che effettua una mossa per un gioco in corso.

    Puoi gestire questa operazione in due modi. Nel primo, recuperi la voce utilizzando l'API GetItem. Quindi, aggiorni la voce di gioco nell'applicazione in base alla mossa compiuta dal giocatore. Infine, sostituisci la voce esistente in DynamoDB utilizzando l'API PutItem. Sebbene questa opzione funzioni, sono necessarie più richieste alla tabella DynamoDB e rischi di sovrascrivere eventuali modifiche apportate tra il recupero e la riscrittura della voce di gioco.

    Il secondo modo per gestire l'operazione consiste nell'utilizzare la chiamata API UpdateItem in DynamoDB. Con l'API UpdateItem puoi aggiornare la voce DynamoDB sul posto con una sola richiesta. Specifichi la voce e gli attributi da modificare e le eventuali condizioni che desideri asserire sulla voce. Questo è il modo preferito per apportare una modifica a una voce perché non sono richieste chiamate multiple al database.

    Nella directory scripts/, è presente un file denominato performMove.js con i contenuti seguenti:

    const AWS = require('aws-sdk')
    const documentClient = new AWS.DynamoDB.DocumentClient()
    
    const performMove = async ({ gameId, user, changedHeap, changedHeapValue }) => {
      if (changedHeapValue < 0 ) {
        throw new Error('Cannot set heap value below 0')
      }
      const params = {
        TableName: 'turn-based-game',
        Key: { 
          gameId: gameId
        },
        UpdateExpression: `SET lastMoveBy = :user, ${changedHeap} = :changedHeapValue`,
        ConditionExpression: `(user1 = :user OR user2 = :user) AND lastMoveBy <> :user AND ${changedHeap} > :changedHeapValue`,
        ExpressionAttributeValues: {
          ':user': user,
          ':changedHeapValue': changedHeapValue,
        },
        ReturnValues: 'ALL_NEW'
      }
      try {
        const resp = await documentClient.update(params).promise()
        console.log('Updated game: ', resp.Attributes)
      } catch (error) {
        console.log('Error updating item: ', error.message)
      }
    }
    
    performMove({ gameId: '5b5ee7d8', user: 'theseconduser', changedHeap: 'heap1', changedHeapValue: 3 })

    Questo script è un po' complicato, quindi procediamo con un passo alla volta.

    Come lo script precedente, importi l'SDK AWS e crei un'istanza del client per documenti DynamoDB.

    Quindi definisci un metodo denominato performMove. Questo metodo è simile a uno interno che verrebbe utilizzato nell'applicazione quando un utente richiede di effettuare una mossa. Lo script assembla i parametri per una chiamata API UpdateItem. Prima, cambia due attributi nel gioco, l'ultimo utente che effettua una mossa e il numero di elementi nell'heap modificato.

    I parametri dell'API UpdateItem fanno quindi alcune asserzioni sullo stato attuale del gioco. La ConditionExpression viene valutata prima dell'aggiornamento della voce per confermare che questa si trova nello stato desiderato. Nell'espressione di condizione fai le tre asserzioni seguenti:

    1. L'utente che richiede di effettuare una mossa è uno dei due utenti nel gioco;
    2. Il turno attuale appartiene all'utente che richiede di effettuare la mossa;
    3. L'heap che viene modificato presenta un valore attuale superiore rispetto a quello in cui viene modificato.

    Infine, i parametri affermano un ReturnValue di ALL_NEW, che significa che DynamoDB restituisce l'intera voce dopo l'aggiornamento dei rispettivi valori. Una volta ottenuto ciò, l'applicazione può valutare il gioco per verificare l'eventuale presenza di un vincitore.

    In fondo al file è riportato un esempio di come questo metodo viene chiamato nell'applicazione. Puoi eseguire lo script con il comando seguente:

    node scripts/performMove.js

    Dovresti visualizzare l'output seguente nel terminale:

    Updated game:  { heap2: 4,
      heap1: 3,
      heap3: 5,
      gameId: '5b5ee7d8',
      user2: 'theseconduser',
      user1: 'myfirstuser',
      lastMoveBy: 'theseconduser' }

    Puoi vedere che la scrittura ha avuto esito positivo e che il gioco è stato aggiornato.

    Prova a eseguire di nuovo lo script nel terminale. Dovresti visualizzare l'output seguente:

    Error updating item:  The conditional request failed

    Questa volta le richieste condizionali hanno avuto esito negativo. L'utente che effettua la richiesta -- theseconduser -- è stato il giocatore più recente a effettuare la mossa. Inoltre, l'heap modificato -- heap1 -- aveva già un valore di 3, a significare che l'utente non ha modificato nulla. Per questo motivo la richiesta è stata rifiutata.


In questo modulo hai eseguito il provisioning di un database Amazon DynamoDB per archiviare i dati di gioco. Hai appreso informazioni sulle chiavi principali in DynamoDB nella modellazione dei dati. Dopo la creazione di una tabella, hai imparato a inserire voci in una tabella per archiviare lo stato del gioco iniziale. Infine, hai visto come aggiornare voci nei dati per evitare di effettuare più richieste a DynamoDB in una singola richiesta.

Nel modulo successivo, imparerai a utilizzare Amazon Simple Notification Service (Amazon SNS) per inviare agli utenti messaggi SMS di notifica sugli eventi importanti del gioco.