2016-03-15 1 views
0

J'ai écrit une procédure stockée af pour ajouter une propriété Type à tous les documents d'une collection DocumentDB. Malheureusement, la procédure stockée échoue après la mise à jour d'un seul document. La collection contient environ 5000 documents.Echec de la mise à jour de plusieurs documents DocumentDB

Voici la procédure stockée:

function updateSproc() { 
var collection = getContext().getCollection(); 
var collectionLink = collection.getSelfLink(); 
var response = getContext().getResponse(); 
var responseBody = { 
    updated: 0, 
    continuation: true, 
    error: "", 
    log: "" 
}; 

// Validate input. 
tryQueryAndUpdate(); 

// Recursively queries for a document by id w/ support for continuation tokens. 
// Calls tryUpdate(document) as soon as the query returns a document. 
function tryQueryAndUpdate(continuation) { 
    var query = { query: "SELECT * FROM root c WHERE NOT is_defined(c.Type)", parameters: []}; 
    var requestOptions = { continuation: continuation}; 

    var isAccepted = collection.queryDocuments(collectionLink, query, requestOptions, function(err, documents, responseOptions) { 
     if (err) { 
    responseBody.error = err; 
    throw err; 
     } 

     if (documents.length > 0) { 
      // If documents are found, update them. 
      responseBody.log += "Found documents: " + documents.length; 
      tryUpdate(documents); 
     } else if (responseOptions.continuation) { 
      responseBody.log += "Continue query"; 
      tryQueryAndUpdate(responseOptions.continuation); 
     } else { 
      responseBody.log += "No more documents"; 
      responseBody.continuation = false; 
      response.setBody(responseBody); 
     } 

    }); 

    // If we hit execution bounds - throw an exception. 
    if (!isAccepted) { 
     responseBody.log += "Query not accepted"; 
     response.setBody(responseBody); 
    } 
} 

// Updates the supplied document according to the update object passed in to the sproc. 
function tryUpdate(documents) 
{ 
    if (documents.length > 0) { 
     responseBody.log += "Updating documents " + documents.length; 

     var document = documents[0]; 

     // DocumentDB supports optimistic concurrency control via HTTP ETag. 
     var requestOptions = { etag: document._etag}; 

     document.Type="Type value"; 

     // Update the document. 
     var isAccepted = collection.replaceDocument(document._self, document, requestOptions, function(err, updatedDocument, responseOptions) { 
      if (err) { 
       responseBody.error = err; 
       throw err; 
      } 

      responseBody.updated++; 
      documents.shift(); 
      tryUpdate(documents); 
     }); 

     // If we hit execution bounds - throw an exception. 
     if (!isAccepted) { 
      responseBody.log += "Update not accepted"; 
      response.setBody(responseBody); 
     } 
    } else { 
     tryQueryAndUpdate(); 
    } 
}} 

Sur la base de la réponse fournie, je peux voir que la requête renvoie 100 documents. tryUpdate est appelée deux fois mais le second appel à replaceDocument n'est pas accepté. Pourquoi n'est-il pas accepté lorsqu'il y a beaucoup de documents à mettre à jour?

+0

J'ai passé environ 20 minutes à regarder cela et je ne vois rien d'évident. La seule chose mineure que j'ai vue ne causerait pas votre problème. Vous appelez tryUpdate() même si isAccepted est false, ce qui signifie que vous pouvez essayer d'appeler replaceDocument() après que DocumentDB vous ait dit qu'il n'accepte plus les opérations. La seule autre chose que je pourrais essayer serait de mettre des documents dans la portée de fonction de niveau supérieur, puis changer votre callback sur la requête pour dire 'results' au lieu de' documents' et ajouter 'documents = results' juste après votre erreur vérifier. –

+0

Ohh, il semblerait que votre code ne verra jamais le bloc qui appelle tryQueryAndUpdate avec une continuation, mais cela signifierait que vous ne pourriez pas obtenir une seconde page, cela n'entraînerait pas l'échec de votre deuxième appel replaceDocuments().Je vais nouilles dessus encore un peu et peut-être essayer de vivre moi-même. –

+0

Ohh, si vous déplacez des documents dans la portée de niveau supérieur, n'oubliez pas de ne pas passer les documents dans tryUpdate. –

Répondre

1

Selon ma réponse à la même question MSDN

Oui, 700RUs + (estimation) 20RUs par insert, une collection qui permet seulement 250RUs par seconde va être un problème. La requête est 700RUs parce que vous faites une opération NOT, qui est effectivement une analyse car elle ne peut pas être indexée.

Donc, certaines choses à essayer;

1) Modifier la logique pour exclure la vérification NOT is_defined et peut-être ordonner By _ts DESC pour obtenir les documents qui ont été mis à jour en premier. Cela pourrait être moins cher que de faire la vérification NON. Ensuite, vous pouvez vérifier chaque document que vous avez déjà si vous avez déjà une propriété Type, sinon ajoutez-en un et ReplaceDocument

2) Vous pouvez également essayer de mettre à l'échelle la collection jusqu'à un S3 pendant que vous effectuez cette opération, puis l'augmenter redescendre à un S1 à nouveau. Cela vous donnera 2500 RU à jouer avec.

3) Même en utilisant un S3, il se peut que vous vous trouviez dans la même situation, cela peut arriver après plus de docs que le second.

Donc, pour corriger j'exécuter une requête dans une application pour revenir juste l'identifiant des dossiers qui n'ont pas la propriété définie,

SELECT c.id VALEUR DE c OU PAS is_defined (c.Type

Collez ces identifiants dans une liste/tableau de quelque sorte, puis des éléments .Take() dans la liste et passez à sproc en tant que tableau. Faites maintenant passer la boucle sproc à travers le tableau passé en faisant un ReadDocument par id, update et replace et en incrémentant le compteur.

Lorsque isAccepted renvoie la valeur false, définissez le corps de la réponse sur la valeur du compteur et renvoyez-le au code appelant. Maintenant, le code appelant peut alors passer (compteur). Prendre (x) et rappeler le sproc.

Jetez un oeil à this sample pour un exemple de la façon d'effectuer un encombrement via un proc stocké. Cela montre comment enregistrer des lots, exec un sproc, et obtenir la position actuelle du sproc dans ce lot avant isAccepted == false du corps de la réponse.