Migrer votre application d’Amazon DynamoDB vers Azure Cosmos DB

S’APPLIQUE À : NoSQL

Azure Cosmos DB est une base de données scalable, distribuée à l’échelle mondiale et complètement managée. Elle fournit un accès à faible latence garanti à vos données. Pour en savoir plus sur Azure Cosmos DB, consultez l’article de présentation. Cet article explique comment migrer votre application .NET à partir de DynamoDB vers Azure Cosmos DB avec des modifications de code minimes.

Différences conceptuelles

Voici les principales différences conceptuelles entre Azure Cosmos DB et DynamoDB :

DynamoDB Azure Cosmos DB
Non applicable Base de données
Table de charge de travail Collection
Élément Document
Attribut Champ
Index secondaire Index secondaire
Clé primaire - clé de partition Partition Key
Clé primaire - clé de tri Facultatif
STREAM ChangeFeed
Écrire l’unité Compute Unité de requête (flexible, pour être utilisée pour les lectures et les écritures)
Lire l’unité Compute Unité de requête (flexible, pour être utilisée pour les lectures et les écritures)
Tables globales Non exigé. Vous pouvez sélectionner directement la région lors de l’approvisionnement du compte Azure Cosmos DB (vous pouvez modifier la région ultérieurement)

Différences structurelles

La structure JSON d’Azure Cosmos DB est plus simple que celle de DynamoDB. L’exemple suivant répertorie les différences

DynamoDB :

L’objet JSON suivant représente le format de données dans DynamoDB

{
TableName: "Music",
KeySchema: [
{ 
  AttributeName: "Artist",
  KeyType: "HASH", //Partition key
},
{ 
  AttributeName: "SongTitle",
  KeyType: "RANGE" //Sort key
}
],
AttributeDefinitions: [
{ 
  AttributeName: "Artist",
  AttributeType: "S"
},
{ 
  AttributeName: "SongTitle",
  AttributeType: "S"
}
],
ProvisionedThroughput: {
  ReadCapacityUnits: 1,
  WriteCapacityUnits: 1
 }
}

Azure Cosmos DB :

L’objet JSON suivant représente le format de données dans Azure Cosmos DB

{
"Artist": "",
"SongTitle": "",
"AlbumTitle": "",
"Year": 9999,
"Price": 0.0,
"Genre": "",
"Tags": ""
}

Migration de votre code

Cet article est étendu à la migration du code d’une application vers Azure Cosmos DB, qui est l’aspect essentiel de la migration de bases de données. Pour vous aider à réduire la courbe d’apprentissage, les sections suivantes incluent une comparaison de code côte à côte entre Amazon DynamoDB et l’extrait de code équivalent d’Azure Cosmos DB.

Pour télécharger le code source, clonez le référentiel suivant :

git clone https://github.com/Azure-Samples/DynamoDB-to-CosmosDB

Conditions préalables

  • .NET Framework 4.7.2
  • Dernière version de Visual Studio avec la charge de travail de développement Azure. Vous pouvez commencer avec l’IDE gratuitVisual Studio Community. Activez la charge de travail Développement Azure pendant la configuration de Visual Studio.
  • Accès à un compte Azure Cosmos DB pour NoSQL
  • Installation locale d’Amazon DynamoDB
  • Java 8
  • Exécutez la version téléchargeable d’Amazon DynamoDB sur le port 8000 (vous pouvez modifier et configurer le code)

Configurez votre code

Ajoutez le « package NuGet » suivant dans votre projet :

Install-Package Microsoft.Azure.Azure Cosmos DB

Établissez la connexion

DynamoDB :

Dans Amazon DynamoDB, le code suivant est utilisé pour se connecter :

    AmazonDynamoDBConfig addbConfig = new AmazonDynamoDBConfig() ;
        addbConfig.ServiceURL = "endpoint";
        try { aws_dynamodbclient = new AmazonDynamoDBClient( addbConfig ); }

Azure Cosmos DB :

Pour vous connecter à Azure Cosmos DB, mettez à jour votre code pour :

client_documentDB = new CosmosClient("your connectionstring from the Azure portal");

Optimiser la connexion dans Azure Cosmos DB

Avec Azure Cosmos DB, vous pouvez utiliser les options suivantes pour optimiser votre connexion :

  • ConnectionMode : utilise le mode de connexion directe pour la connexion aux nœuds de données dans le service Azure Cosmos DB. Utilisez le mode passerelle uniquement pour initialiser et mettre en cache les adresses logiques et les actualiser sur les mises à jour. Pour plus d’informations, consultez les modes de connectivité.

  • ApplicationRegion : cette option permet de définir la région répliquée géographiquement qui est utilisée pour interagir avec Azure Cosmos DB. Pour plus d’informations, consultez la distribution mondiale.

  • ConsistencyLevel : cette option est utilisée pour remplacer le niveau de cohérence par défaut. Pour plus d’informations, consultez les niveaux de cohérence.

  • BulkExecutionMode : cette option permet d’exécuter des opérations en bloc en définissant la propriété AllowBulkExecution sur true. Pour en savoir plus, consultez l’importation en bloc.

    client_cosmosDB = new CosmosClient(" Your connection string ",new CosmosClientOptions()
    { 
      ConnectionMode=ConnectionMode.Direct,
      ApplicationRegion=Regions.EastUS2,
      ConsistencyLevel=ConsistencyLevel.Session,
      AllowBulkExecution=true  
    });
    

Créer le conteneur

DynamoDB :

Pour stocker les données dans Amazon DynamoDB, vous devez d’abord créer la table. Dans le processus de création de table, vous définissez le schéma, le type de clé et les attributs comme indiqué dans le code suivant :

// movies_key_schema
public static List<KeySchemaElement> movies_key_schema
  = new List<KeySchemaElement>
{
  new KeySchemaElement
  {
    AttributeName = partition_key_name,
    KeyType = "HASH"
  },
  new KeySchemaElement
  {
    AttributeName = sort_key_name,
    KeyType = "RANGE"
  }
};

// key names for the Movies table
public const string partition_key_name = "year";
public const string sort_key_name      = "title";
  public const int readUnits=1, writeUnits=1; 

    // movie_items_attributes
    public static List<AttributeDefinition> movie_items_attributes
  = new List<AttributeDefinition>
{
  new AttributeDefinition
  {
    AttributeName = partition_key_name,
    AttributeType = "N"
  },
  new AttributeDefinition
  {
    AttributeName = sort_key_name,
    AttributeType = "S"
  }

CreateTableRequest  request;
CreateTableResponse response;

// Build the 'CreateTableRequest' structure for the new table
request = new CreateTableRequest
{
  TableName             = table_name,
  AttributeDefinitions  = table_attributes,
  KeySchema             = table_key_schema,
  // Provisioned-throughput settings are always required,
  // although the local test version of DynamoDB ignores them.
  ProvisionedThroughput = new ProvisionedThroughput( readUnits, writeUnits );
};

Azure Cosmos DB :

Dans Amazon DynamoDB, vous devez approvisionner les unités Compute de lecture et d’écriture. Dans Azure Cosmos DB, spécifiez le débit comme Unités de requête (RU/s), qui peuvent être utilisées pour toutes les opérations de manière dynamique. Les données sont organisées sous forme de base de données --> conteneur--> élément. Vous pouvez spécifier le débit au niveau de la base de données ou au niveau de la collection ou les deux.

Pour créer une base de données :

await client_cosmosDB.CreateDatabaseIfNotExistsAsync(movies_table_name);

Pour créer le conteneur :

await cosmosDatabase.CreateContainerIfNotExistsAsync(new ContainerProperties() { PartitionKeyPath = "/" + partitionKey, Id = new_collection_name }, provisionedThroughput);

Chargement des données

DynamoDB :

Le code suivant montre comment charger les données dans Amazon DynamoDB. MoviesArray se compose d’une liste de documents JSON, vous devez ensuite effectuer une itération et charger le document JSON dans Amazon DynamoDB :

int n = moviesArray.Count;
for( int i = 0, j = 99; i < n; i++ )
    {
  try
  {
    string itemJson = moviesArray[i].ToString();
    Document doc = Document.FromJson(itemJson);
    Task putItem = moviesTable.PutItemAsync(doc);
    if( i >= j )
    {
      j++;
      Console.Write( "{0,5:#,##0}, ", j );
      if( j % 1000 == 0 )
        Console.Write( "\n " );
      j += 99;
    }
    await putItem;

Azure Cosmos DB :

Dans Azure Cosmos DB, vous pouvez choisir un flux et écrire avec moviesContainer.CreateItemStreamAsync(). Toutefois, dans cet exemple, le code JSON sera désérialisé dans le type de MovieModel pour illustrer la fonctionnalité de diffusion du type. Le code est multithread et utilisera l’architecture distribuée d’Azure Cosmos DB et accélérera le chargement :

List<Task> concurrentTasks = new List<Task>();
for (int i = 0, j = 99; i < n; i++)
{
  try
  {
      MovieModel doc= JsonConvert.DeserializeObject<MovieModel>(moviesArray[i].ToString());
      doc.Id = Guid.NewGuid().ToString();
      concurrentTasks.Add(moviesContainer.CreateItemAsync(doc,new PartitionKey(doc.Year)));
      {
          j++;
          Console.Write("{0,5:#,##0}, ", j);
          if (j % 1000 == 0)
              Console.Write("\n               ");
          j += 99;
      }
      
  }
  catch (Exception ex)
  {
      Console.WriteLine("\n     ERROR: Could not write the movie record #{0:#,##0}, because:\n       {1}",
                          i, ex.Message);
      operationFailed = true;
      break;
  }
}
await Task.WhenAll(concurrentTasks);

Créer un document

DynamoDB :

L’écriture d’un nouveau document dans Amazon DynamoDB n’est pas du type sécurisé, l’exemple suivant utilise newItem comme type de document :

Task<Document> writeNew = moviesTable.PutItemAsync(newItem, token);
await writeNew;

Azure Cosmos DB :

Azure Cosmos DB fournit la cohérence des types via le modèle de données. Nous utilisons un modèle de données nommé « MovieModel » :

public class MovieModel
{
    [JsonProperty("id")]
    public string Id { get; set; }
    [JsonProperty("title")]
    public string Title{ get; set; }
    [JsonProperty("year")]
    public int Year { get; set; }
    public MovieModel(string title, int year)
    {
        this.Title = title;
        this.Year = year;
    }
    public MovieModel()
    {

    }
    [JsonProperty("info")]
    public   MovieInfo MovieInfo { get; set; }

    internal string PrintInfo()
    {
        if(this.MovieInfo!=null)
        return            string.Format("\nMovie with title:{1}\n Year: {2}, Actors: {3}\n Directors:{4}\n Rating:{5}\n", this.Id, this.Title, this.Year, String.Join(",",this.MovieInfo.Actors), this.MovieInfo, this.MovieInfo.Rating);
        else
            return string.Format("\nMovie with  title:{0}\n Year: {1}\n",  this.Title, this.Year);
    }
}

Dans Azure Cosmos DB newItem sera MovieModel :

 MovieModel movieModel = new MovieModel()
            {
                Id = Guid.NewGuid().ToString(),
                Title = "The Big New Movie",
                Year = 2018,
                MovieInfo = new MovieInfo() { Plot = "Nothing happens at all.", Rating = 0 }
            };
    var writeNew= moviesContainer.CreateItemAsync(movieModel, new Microsoft.Azure.Cosmos.PartitionKey(movieModel.Year));
    await writeNew;

Lire un document

DynamoDB :

Pour lire dans Amazon DynamoDB, vous devez définir des primitives :

// Create Primitives for the HASH and RANGE portions of the primary key
Primitive hash = new Primitive(year.ToString(), true);
Primitive range = new Primitive(title, false);

  Task<Document> readMovie = moviesTable.GetItemAsync(hash, range, token);
  movie_record = await readMovie;

Azure Cosmos DB :

Toutefois, avec Azure Cosmos DB la requête est naturelle (LINQ) :

IQueryable<MovieModel> movieQuery = moviesContainer.GetItemLinqQueryable<MovieModel>(true)
                        .Where(f => f.Year == year && f.Title == title);
// The query is executed synchronously here, but can also be executed asynchronously via the IDocumentQuery<T> interface
    foreach (MovieModel movie in movieQuery)
    {
      movie_record_cosmosdb = movie;
    }

La collection de documents dans l’exemple ci-dessus est :

  • type sécurisé
  • fournir une option de requête naturelle.

Mettre à jour un élément

DynamoDB : Pour mettre à jour l’élément dans Amazon DynamoDB :

updateResponse = await client.UpdateItemAsync( updateRequest );

Azure Cosmos DB :

Dans Azure Cosmos DB, la mise à jour est traitée comme une opération Upsert, ce qui signifie qu’il faut insérer le document s’il n’existe pas :

await moviesContainer.UpsertItemAsync<MovieModel>(updatedMovieModel);

Supprimer un document

DynamoDB :

Pour supprimer un élément dans Amazon DynamoDB, vous devez à nouveau tomber sur les primitives :

Primitive hash = new Primitive(year.ToString(), true);
      Primitive range = new Primitive(title, false);
      DeleteItemOperationConfig deleteConfig = new DeleteItemOperationConfig( );
      deleteConfig.ConditionalExpression = condition;
      deleteConfig.ReturnValues = ReturnValues.AllOldAttributes;
      
  Task<Document> delItem = table.DeleteItemAsync( hash, range, deleteConfig );
        deletedItem = await delItem;

Azure Cosmos DB :

Dans Azure Cosmos DB, nous pouvons récupérer le document et les supprimer de manière asynchrone :

var result= ReadingMovieItem_async_List_CosmosDB("select * from c where c.info.rating>7 AND c.year=2018 AND c.title='The Big New Movie'");
while (result.HasMoreResults)
{
  var resultModel = await result.ReadNextAsync();
  foreach (var movie in resultModel.ToList<MovieModel>())
  {
    await moviesContainer.DeleteItemAsync<MovieModel>(movie.Id, new PartitionKey(movie.Year));
  }
  }

Interroger des documents

DynamoDB :

Dans Amazon DynamoDB, les fonctions d’API sont requises pour interroger les données :

QueryOperationConfig config = new QueryOperationConfig( );
  config.Filter = new QueryFilter( );
  config.Filter.AddCondition( "year", QueryOperator.Equal, new DynamoDBEntry[ ] { 1992 } );
  config.Filter.AddCondition( "title", QueryOperator.Between, new DynamoDBEntry[ ] { "B", "Hzz" } );
  config.AttributesToGet = new List<string> { "year", "title", "info" };
  config.Select = SelectValues.SpecificAttributes;
  search = moviesTable.Query( config ); 

Azure Cosmos DB :

Dans Azure Cosmos DB, vous pouvez effectuer une projection et un filtre à l’intérieur d’une requête SQL simple :

var result = moviesContainer.GetItemQueryIterator<MovieModel>( 
  "select c.Year, c.Title, c.info from c where Year=1998 AND (CONTAINS(Title,'B') OR CONTAINS(Title,'Hzz'))");

Pour les opérations de plage, par exemple, « between », vous devez effectuer une analyse dans Amazon DynamoDB :

ScanRequest sRequest = new ScanRequest
{
  TableName = "Movies",
  ExpressionAttributeNames = new Dictionary<string, string>
  {
    { "#yr", "year" }
  },
  ExpressionAttributeValues = new Dictionary<string, AttributeValue>
  {
      { ":y_a", new AttributeValue { N = "1960" } },
      { ":y_z", new AttributeValue { N = "1969" } },
  },
  FilterExpression = "#yr between :y_a and :y_z",
  ProjectionExpression = "#yr, title, info.actors[0], info.directors, info.running_time_secs"
};

ClientScanning_async( sRequest ).Wait( );

Dans Azure Cosmos DB, vous pouvez utiliser une requête SQL et une instruction sur une seule ligne :

var result = moviesContainer.GetItemQueryIterator<MovieModel>( 
  "select c.title, c.info.actors[0], c.info.directors,c.info.running_time_secs from c where BETWEEN year 1960 AND 1969");

Supprimer un conteneur

DynamoDB :

Pour supprimer la table dans Amazon DynamoDB, vous pouvez spécifier :

client.DeleteTableAsync( tableName );

Azure Cosmos DB :

Pour supprimer la collection dans Azure Cosmos DB, vous pouvez spécifier :

await moviesContainer.DeleteContainerAsync();

Ensuite, supprimez également la base de données si nécessaire :

await cosmosDatabase.DeleteAsync();

Comme vous pouvez le voir, Azure Cosmos DB prend en charge des requêtes naturelles (SQL), les opérations sont asynchrones et beaucoup plus faciles. Vous pouvez facilement migrer votre code complexe vers Azure Cosmos DB, ce qui devient plus simple après la migration.

Étapes suivantes