Migrer de Couchbase vers Azure Cosmos DB for NoSQL

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 fournit des instructions pour migrer des applications Java qui sont connectées à CouchBase vers une API pour un compte NoSQL dans Azure Cosmos DB.

Différences de nomenclature

Voici les principales fonctionnalités qui opèrent différemment dans Azure Cosmos DB et Couchbase :

Couchbase Azure Cosmos DB
Couchbase Server Compte
Compartiment Base de données
Compartiment Conteneur/collection
Document JSON Élément/document

Différences clés

  • Azure Cosmos DB a un champ « ID » dans le document, alors que Couchbase a l’ID dans le cadre d’un compartiment. Le champ « ID » est unique dans la partition.

  • Azure Cosmos DB évolue à l’aide de la technique de partitionnement. Cela signifie qu’il divise les données en plusieurs partitions. Ces partitions sont créées en fonction de la propriété de clé de partition que vous fournissez. Vous pouvez sélectionner la clé de partition pour optimiser les opérations de lecture et d’écriture ou les opérations de lecture/d’écriture optimisées également. Pour plus d’informations, consultez l’article sur le partitionnement.

  • Dans Azure Cosmos DB, il n’est pas nécessaire que la hiérarchie de niveau supérieur désigne la collection, car le nom de celle-ci existe déjà. Cette fonctionnalité simplifie grandement la structure JSON. Voici un exemple qui montre les différences de modèle de données entre Couchbase et Azure Cosmos DB :

    Couchbase : document ID = « 99FF4444 »

    {
      "TravelDocument":
      {
        "Country":"India",
        "Validity" : "2022-09-01",
        "Person":
        {
          "Name": "Manish",
          "Address": "AB Road, City-z"
        },
        "Visas":
        [
          {
          "Country":"India",
          "Type":"Multi-Entry",
          "Validity":"2022-09-01"
          },
          {
          "Country":"US",
          "Type":"Single-Entry",
          "Validity":"2022-08-01"
          }
        ]
      }
    }
    

    Azure Cosmos DB : référence à l’ID dans le document, comme indiqué ci-dessous.

    {
      "id" : "99FF4444",
    
      "Country":"India",
       "Validity" : "2022-09-01",
        "Person":
        {
          "Name": "Manish",
          "Address": "AB Road, City-z"
        },
        "Visas":
        [
          {
          "Country":"India",
          "Type":"Multi-Entry",
          "Validity":"2022-09-01"
          },
          {
          "Country":"US",
          "Type":"Single-Entry",
          "Validity":"2022-08-01"
          }
        ]
      }
    

Support du SDK Java

Azure Cosmos DB a les SDK suivants pour prendre en charge différents frameworks Java :

  • SDK Async
  • SDK Spring Boot

Les sections suivantes décrivent quand utiliser chacun de ces SDK. Prenons un exemple dans lequel nous avons trois types de charges de travail :

Couchbase en tant que dépôt de documents & requêtes personnalisées basées sur des données Spring

Si la charge de travail que vous migrez est basée sur le SDK Spring Boot, vous pouvez utiliser les étapes suivantes :

  1. Ajoutez le parent au fichier POM.xml :

    <parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>2.1.5.RELEASE</version>
       <relativePath/>
    </parent>
    
  2. Ajoutez des propriétés au fichier POM.xml :

    <azure.version>2.1.6</azure.version>
    
  3. Ajoutez des dépendances au fichier POM.xml :

    <dependency>
        <groupId>com.microsoft.azure</groupId>
        <artifactId>azure-cosmosdb-spring-boot-starter</artifactId>
        <version>2.1.6</version>
    </dependency>
    
  4. Ajoutez des propriétés d’application sous les ressources et spécifiez les éléments suivants. Veillez à remplacer les paramètres de l’URL, de la clé et du nom de la base de données :

       azure.cosmosdb.uri=<your-cosmosDB-URL>
       azure.cosmosdb.key=<your-cosmosDB-key>
       azure.cosmosdb.database=<your-cosmosDB-dbName>
    
  5. Définissez le nom de la collection dans le modèle. Vous pouvez également spécifier des annotations supplémentaires. Par exemple, l’ID et la clé de partition pour les désigner de manière explicite :

    @Document(collection = "mycollection")
        public class User {
            @id
            private String id;
            private String firstName;
            @PartitionKey
            private String lastName;
        }
    

Voici les extraits de code pour les opérations CRUD :

Opérations d’insertion et de mise à jour

_repo est l’objet de dépôt et doc est l’objet de la classe POJO. Vous pouvez utiliser .save pour effectuer une insertion ou un upsert (si le document avec l’ID spécifié est trouvé). L’extrait de code suivant montre comment insérer ou mettre à jour un objet doc :

_repo.save(doc);

Opération de suppression

Examinez l’extrait de code suivant, où l’objet doc a un ID et une clé de partition indispensables à sa localisation et à sa suppression :

_repo.delete(doc);

Opération de lecture

Vous pouvez lire le document en utilisant la clé de partition ou sans la spécifier. Si vous ne spécifiez pas la clé de partition, elle est traitée en tant que requête entre plusieurs partitions. Examinez les exemples de code suivants, dont le premier effectue une opération à l’aide d’un ID et d’un champ de clé de partition. Le deuxième exemple utilise un champ normal et ne spécifie pas le champ de clé de partition.

  • _repo.findByIdAndName(objDoc.getId(),objDoc.getName());
  • _repo.findAllByStatus(objDoc.getStatus());

Voilà, vous pouvez désormais utiliser votre application avec Azure Cosmos DB. L’exemple de code complet sur lequel repose l’exemple décrit dans ce document est disponible dans le dépôt GitHub CouchbaseToCosmosDB-SpringCosmos.

Couchbase en tant que dépôt de documents et utilisation de requêtes N1QL

Les requêtes N1QL permettent de définir des requêtes dans Couchbase.

Requête N1QL Requête Azure Cosmos DB
SELECT META(TravelDocument).id AS id, TravelDocument.* FROM TravelDocument WHERE _type = "com.xx.xx.xx.xxx.xxx.xxxx " and country = 'India’ and ANY m in Visas SATISFIES m.type == 'Multi-Entry' and m.Country IN ['India', Bhutan’] ORDER BY Validity DESC LIMIT 25 OFFSET 0 SELECT c.id,c FROM c JOIN m in c.country=’India’ WHERE c._type = " com.xx.xx.xx.xxx.xxx.xxxx" and c.country = 'India' and m.type = 'Multi-Entry' and m.Country IN ('India', 'Bhutan') ORDER BY c.Validity DESC OFFSET 0 LIMIT 25

Vous pouvez remarquer les modifications suivantes dans vos requêtes N1QL :

  • Vous n’avez pas besoin d’utiliser le mot clé META ou de faire référence au document de premier niveau. Au lieu de cela, vous pouvez créer votre propre référence au conteneur. Dans cet exemple, nous l’avons défini par « c » (nous aurions pu utiliser toute autre chose). Cette référence est utilisée comme préfixe pour tous les champs de premier niveau. Par exemple, c.id, c.country etc.

  • Au lieu d’utiliser « ANY », vous pouvez maintenant effectuer une jointure sur un sous-document et y faire référence avec un alias dédié tel que « m ». Une fois créé l’alias d’un sous-document, vous devez l’utiliser. Par exemple, m.Country.

  • La séquence OFFSET est différente dans la requête Azure Cosmos DB ; vous devez d’abord spécifier OFFSET, puis LIMIT. Nous vous recommandons de ne pas recourir au SDK Spring Data si vous utilisez le nombre maximal de requêtes définies personnalisées, car cela peut engendrer une surcharge inutile côté client lors de la transmission de la requête à Azure Cosmos DB. Au lieu de cela, nous disposons d’un SDK Async Java direct, qui peut être utilisé de manière plus efficace dans ce cas.

Opération de lecture

Utilisez le SDK Async Java avec les étapes suivantes :

  1. Configurez la dépendance suivante dans le fichier POM.xml :

    <!-- https://mvnrepository.com/artifact/com.microsoft.azure/azure-cosmosdb -->
    <dependency>
        <groupId>com.microsoft.azure</groupId>
        <artifactId>azure-cosmos</artifactId>
        <version>3.0.0</version>
    </dependency>
    
  2. Créez un objet de connexion pour Azure Cosmos DB à l’aide de la méthode ConnectionBuilder, comme indiqué dans l’exemple suivant. Veillez à placer cette déclaration dans le bean, afin que le code suivant ne soit exécuté qu’une seule fois :

    ConnectionPolicy cp=new ConnectionPolicy();
    cp.connectionMode(ConnectionMode.DIRECT);
    
    if(client==null)
       client= CosmosClient.builder()
          .endpoint(Host)//(Host, PrimaryKey, dbName, collName).Builder()
           .connectionPolicy(cp)
           .key(PrimaryKey)
           .consistencyLevel(ConsistencyLevel.EVENTUAL)
           .build();
    
    container = client.getDatabase(_dbName).getContainer(_collName);
    
  3. Pour exécuter la requête, vous devez exécuter l’extrait de code suivant :

    Flux<FeedResponse<CosmosItemProperties>> objFlux= container.queryItems(query, fo);
    

À présent, à l’aide de la méthode ci-dessus, vous pouvez passer plusieurs requêtes et les exécuter sans aucune difficulté. Si vous devez exécuter une grande requête pouvant être subdivisée en plusieurs requêtes, essayez l’extrait de code suivant plutôt que le précédent :

for(SqlQuerySpec query:queries)
{
   objFlux= container.queryItems(query, fo);
   objFlux .publishOn(Schedulers.elastic())
         .subscribe(feedResponse->
            {
               if(feedResponse.results().size()>0)
               {
                  _docs.addAll(feedResponse.results());
               }
            
            },
            Throwable::printStackTrace,latch::countDown);
   lstFlux.add(objFlux);
}
                  
      Flux.merge(lstFlux);
      latch.await();
}

Avec le code précédent, vous pouvez exécuter des requêtes en parallèle et accroître l’optimisation des exécutions distribuées. Vous pouvez également exécuter les opérations d’insertion et de mise à jour :

Opération d'insertion

Pour insérer le document, exécutez le code suivant :

Mono<CosmosItemResponse> objMono= container.createItem(doc,ro);

Abonnez-vous ensuite à Mono comme suit :

CountDownLatch latch=new CountDownLatch(1);
objMono .subscribeOn(Schedulers.elastic())
        .subscribe(resourceResponse->
        {
           if(resourceResponse.statusCode()!=successStatus)
              {
                 throw new RuntimeException(resourceResponse.toString());
              }
           },
        Throwable::printStackTrace,latch::countDown);
latch.await();

Opérations d’upsert

L’opération d’upsert nécessite que vous spécifiiez le document qui doit être mis à jour. Pour extraire le document complet, vous pouvez utiliser l’extrait de code mentionné sous le titre Opération de lecture, puis modifier le ou les champs obligatoires. L’extrait de code suivant effectue un upsert du document :

Mono<CosmosItemResponse> obs= container.upsertItem(doc, ro);

Abonnez-vous ensuite à Mono. Reportez-vous à l’extrait de code d’abonnement à Mono dans la section Opération d’insertion.

Opération de suppression

L’extrait de code suivant effectue une opération de suppression :

CosmosItem objItem= container.getItem(doc.Id, doc.Tenant);
Mono<CosmosItemResponse> objMono = objItem.delete(ro);

Ensuite, abonnez-vous à Mono (reportez-vous à l’extrait de code d’abonnement à Mono dans la section Opération d’insertion). L’exemple de code complet est disponible dans le dépôt GitHub CouchbaseToCosmosDB-AsyncInSpring.

Couchbase en tant que paire clé/valeur

Il s’agit d’un type simple de charge de travail dans lequel vous pouvez effectuer des recherches au lieu de requêtes. Pour les paires clé/valeur, procédez comme suit :

  1. Envisagez « /ID » comme clé primaire ; vous avez ainsi l’assurance que vous pouvez effectuer une opération de recherche directement dans la partition spécifique. Créez une collection et spécifiez « /ID » comme clé de partition.

  2. Désactivez complètement l’indexation. Étant donné que vous allez exécuter des opérations de recherche, toute surcharge d’indexation est inutile. Pour désactiver l’indexation, connectez-vous au portail Azure, puis accédez à Compte Azure Cosmos DB. Ouvrez l’Explorateur de données, puis sélectionnez votre Base de données et le Conteneur. Ouvrez l’onglet Mise à l’échelle et paramètres et sélectionnez Stratégie d’indexation. La stratégie d’indexation actuelle se présente comme suit :

    {
     "indexingMode": "consistent",
     "automatic": true,
     "includedPaths": [
         {
             "path": "/*"
         }
     ],
     "excludedPaths": [
         {
             "path": "/\"_etag\"/?"
         }
     ]
     }
    

    Remplacez la stratégie d’indexation ci-dessus par la stratégie suivante :

    {
     "indexingMode": "none",
     "automatic": false,
     "includedPaths": [],
     "excludedPaths": []
     }
    
  3. Utilisez l’extrait de code suivant pour créer l’objet de connexion. Objet de connexion (à placer dans @Bean ou à rendre statique) :

    ConnectionPolicy cp=new ConnectionPolicy();
    cp.connectionMode(ConnectionMode.DIRECT);
    
    if(client==null)
       client= CosmosClient.builder()
          .endpoint(Host)//(Host, PrimaryKey, dbName, collName).Builder()
           .connectionPolicy(cp)
           .key(PrimaryKey)
           .consistencyLevel(ConsistencyLevel.EVENTUAL)
           .build();
    
    container = client.getDatabase(_dbName).getContainer(_collName);
    

Vous pouvez maintenant exécuter les opérations CRUD comme suit :

Opération de lecture

Pour lire l’élément, utilisez l’extrait de code suivant :

CosmosItemRequestOptions ro=new CosmosItemRequestOptions();
ro.partitionKey(new PartitionKey(documentId));
CountDownLatch latch=new CountDownLatch(1);
      
var objCosmosItem= container.getItem(documentId, documentId);
Mono<CosmosItemResponse> objMono = objCosmosItem.read(ro);
objMono .subscribeOn(Schedulers.elastic())
        .subscribe(resourceResponse->
        {
           if(resourceResponse.item()!=null)
           {
              doc= resourceResponse.properties().toObject(UserModel.class);
           }
        },
        Throwable::printStackTrace,latch::countDown);
latch.await();

Opération d'insertion

Pour insérer un élément, vous pouvez exécuter le code suivant :

Mono<CosmosItemResponse> objMono= container.createItem(doc,ro);

Abonnez-vous ensuite à Mono comme suit :

CountDownLatch latch=new CountDownLatch(1);
objMono.subscribeOn(Schedulers.elastic())
      .subscribe(resourceResponse->
      {
         if(resourceResponse.statusCode()!=successStatus)
            {
               throw new RuntimeException(resourceResponse.toString());
            }
         },
      Throwable::printStackTrace,latch::countDown);
latch.await();

Opérations d’upsert

Pour mettre à jour la valeur d’un élément, reportez-vous à l’extrait de code ci-dessous :

Mono<CosmosItemResponse> obs= container.upsertItem(doc, ro);

Ensuite, abonnez-vous à Mono (reportez-vous à l’extrait de code d’abonnement à Mono dans la section Opération d’insertion).

Opération de suppression

Utilisez l’extrait de code suivant pour exécuter l’opération de suppression :

CosmosItem objItem= container.getItem(id, id);
Mono<CosmosItemResponse> objMono = objItem.delete(ro);

Ensuite, abonnez-vous à Mono (reportez-vous à l’extrait de code d’abonnement à Mono dans la section Opération d’insertion). L’exemple de code complet est disponible dans le dépôt GitHub CouchbaseToCosmosDB-AsyncKeyValue.

Migration de données

Il existe deux façons de migrer des données.

  • Utiliser Azure Data Factory : il s’agit de la méthode la plus recommandée pour migrer des données. Configurez la source en tant que Couchbase et le récepteur en tant qu’Azure Cosmos DB for NoSQL. Pour plus d’informations, consultez l’article sur le connecteur Azure Cosmos DB Data Factory.

Étapes suivantes