403 límite de velocidad en la inserción a veces tiene éxito

Inserto 100 archivos en un bucle. Para esta prueba, he DESACTIVADO la desactivación y vuelva a intentarlo, de modo que si una inserción falla con una 403, la ignoro y procedo con el siguiente archivo. De los 100 archivos, recibo 63 403 excepciones de límite de velocidad.

Sin embargo, al verificar Drive, de esas 63 fallas, 3 realmente tuvieron éxito, es decir. el archivo fue creado en la unidad. Si hubiera hecho el retroceso habitual y vuelva a intentarlo, hubiera terminado con insertos duplicados. Esto confirma el comportamiento que estaba viendo con backoff-retry habilitado, es decir. de mi prueba de 100 archivos, constantemente veo 3-4 inserciones duplicadas.

Huele como si hubiera una conexión asíncrona entre el servidor de punto final de API y los servidores de almacenamiento de Drive que está causando resultados no deterministas, especialmente en escrituras de gran volumen.

Como esto significa que no puedo confiar en el “límite de velocidad 403” para estrangular mis insertos, necesito saber cuál es una tasa de inserción segura para no desencadenar estos errores de sincronización.

Ejecutando el código de abajo, da …

Summary... File insert attempts (a) = 100 rate limit errors (b) = 31 expected number of files (ab) = 69 Actual number of files = 73 

código…

 package com.cnw.test.servlets; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.google.api.client.auth.oauth2.Credential; import com.google.api.client.googleapis.json.GoogleJsonError; import com.google.api.client.googleapis.json.GoogleJsonResponseException; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.jackson.JacksonFactory; import com.google.api.services.drive.Drive; import com.google.api.services.drive.model.ChildList; import com.google.api.services.drive.model.File; import com.google.api.services.drive.model.File.Labels; import com.google.api.services.drive.model.ParentReference; import couk.cleverthinking.cnw.oauth.CredentialMediatorB; import couk.cleverthinking.cnw.oauth.CredentialMediatorB.InvalidClientSecretsException; @SuppressWarnings("serial") /** * * AppEngine servlet to demonstrate that Drive IS performing an insert despite throwing a 403 rate limit exception. * * All it does is create a folder, then loop to create x files. Any 403 rate limit exceptions are counted. * At the end, compare the expected number of file (attempted - 403) vs. the actual. * In a run of 100 files, I consistently see between 1 and 3 more files than expected, ie. despite throwing a 403 rate limit, * Drive *sometimes* creates the file anyway. * * To run this, you will need to ... * 1) enter an APPNAME above * 2) enter a google user id above * 3) Have a valid stored credential for that user * * (2) and (3) can be replaced by a manually constructed Credential * * Your test must generate rate limit errors, so if you have a very slow connection, you might need to run 2 or 3 in parallel. * I run the test on a medium speed connection and I see 403 rate limits after 30 or so inserts. * Creating 100 files consistently exposes the problem. * */ public class Hack extends HttpServlet { private final String APPNAME = "MyApp"; // ENTER YOUR APP NAME private final String GOOGLE_USER_ID_TO_FETCH_CREDENTIAL = "11222222222222222222222"; //ENTER YOUR GOOGLE USER ID @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { /* * set up the counters */ // I run this as a servlet, so I get the number of files from the request URL int numFiles = Integer.parseInt(request.getParameter("numfiles")); int fileCount = 0; int ratelimitCount = 0; /* * Load the Credential */ CredentialMediatorB cmb = null; try { cmb = new CredentialMediatorB(request); } catch (InvalidClientSecretsException e) { e.printStackTrace(); } // this fetches a stored credential, you might choose to construct one manually Credential credential = cmb.getStoredCredential(GOOGLE_USER_ID_TO_FETCH_CREDENTIAL); /* * Use the credential to create a drive service */ Drive driveService = new Drive.Builder(new NetHttpTransport(), new JacksonFactory(), credential).setApplicationName(APPNAME).build(); /* * make a parent folder to make it easier to count the files and delete them after the test */ File folderParent = new File(); folderParent.setTitle("403parentfolder-" + numFiles); folderParent.setMimeType("application/vnd.google-apps.folder"); folderParent.setParents(Arrays.asList(new ParentReference().setId("root"))); folderParent.setLabels(new Labels().setHidden(false)); driveService.files().list().execute(); folderParent = driveService.files().insert(folderParent).execute(); System.out.println("folder made with id = " + folderParent.getId()); /* * store the parent folder id in a parent array for use by each child file */ List parents = new ArrayList(); parents.add(new ParentReference().setId(folderParent.getId())); /* * loop for each file */ for (fileCount = 0; fileCount < numFiles; fileCount++) { /* * make a File object for the insert */ File file = new File(); file.setTitle("testfile-" + (fileCount+1)); file.setParents(parents); file.setDescription("description"); file.setMimeType("text/html"); try { System.out.println("making file "+fileCount + " of "+numFiles); // call the drive service insert execute method driveService.files().insert(file).setConvert(false).execute(); } catch (GoogleJsonResponseException e) { GoogleJsonError error = e.getDetails(); // look for rate errors and count them. Normally one would expo-backoff here, but this is to demonstrate that despite // the 403, the file DID get created if (error.getCode() == 403 && error.getMessage().toLowerCase().contains("rate limit")) { System.out.println("rate limit exception on file " + fileCount + " of "+numFiles); // increment a count of rate limit errors ratelimitCount++; } else { // just in case there is a different exception thrown System.out.println("[DbSA465] Error message: " + error.getCode() + " " + error.getMessage()); } } } /* * all done. get the children of the folder to see how many files were actually created */ ChildList children = driveService.children().list(folderParent.getId()).execute(); /* * and the winner is ... */ System.out.println("\nSummary..."); System.out.println("File insert attempts (a) = " + numFiles); System.out.println("rate limit errors (b) = " + ratelimitCount); System.out.println("expected number of files (ab) = " + (numFiles - ratelimitCount)); System.out.println("Actual number of files = " + children.getItems().size() + " NB. There is a limit of 100 children in a single page, so if you're expecting more than 100, need to follow nextPageToken"); } } 

Supongo que estás tratando de hacer descargas paralelas …

Puede que esta no sea la respuesta que estás buscando, pero esto es lo que he experimentado en mis interacciones con la API de Google. Uso C #, por lo que es un poco diferente, pero tal vez ayude.

Tuve que establecer una cantidad específica de hilos para ejecutar a la vez. Si dejo que mi progtwig ejecute las 100 entradas a la vez como hilos separados, me toparé también con el error de límite de velocidad.

No sé para nada, pero en mi progtwig C #, ejecuto 3 hilos (definibles por el usuario, 3 es el predeterminado)

 opts = new ParallelOptions { MaxDegreeOfParallelism = 3 }; var checkforfinished = Parallel.ForEach(lstBackupUsers.Items.Cast(), opts, name => { { // my logic code here } 

Hice una búsqueda rápida y descubrí que Java 8 (no estoy seguro si eso es lo que estás usando) admite Parallel (). ForEach (), quizás eso te ayude. El recurso que encontré para esto está en: http://radar.oreilly.com/2015/02/java-8-streams-api-and-parallelism.html

¡Espero que esto ayude, tomando turnos tratando de ayudar a otros en SO, ya que la gente me ha ayudado!