Tres técnicas sencillas para aislar sus scripts de AdWords y mantenerlos funcionando sin problemas

No importa lo buenos que sean sus scripts, siguen siendo vulnerables cuando otras cosas salen mal. El columnista Russell Savage explica cómo proteger su trabajo.

google-adwords-rojo-1920

El mundo puede ser un lugar aterrador. Escribe cuidadosamente su increíble código de secuencia de comandos de AdWords y, como un pajarito que aprende a volar, lo envía al mundo y espera lo mejor.

Pero su código realiza llamadas a otras API que pueden fallar o desaparecer sin previo aviso. También se basa en una hoja de cálculo que puede romperse o tener problemas en cualquier momento.

No puede controlar estos eventos externos, pero puede crear salvaguardas para aislarse de ellos. Y cuando sucede algo malo, se le puede notificar rápidamente.

Una de las cosas interesantes de JavaScript (y muchos otros lenguajes) es la capacidad de asignar funciones a variables. Eso le permite pasar fácilmente funciones a otras funciones según sea necesario. Aquí hay un ejemplo simple:

function main() {
  function theExecutor(theFunctionToExecute) {
    return theFunctionToExecute();
  }

  function test1() { Logger.log('This is test function 1'); }
  function test2() { Logger.log('This is test function 2'); }
  theExecutor(test1);
  theExecutor(test2);
}

El código anterior ilustra que puede enviar funciones (prueba1, prueba2) a otras funciones (el Ejecutor) como variables y ejecutarlas simplemente agregando paréntesis. Usaremos esta técnica para crear envoltorios para nuestra lógica principal, lo que nos ayudará a aislarnos de los eventos externos.

Encontrar código lento

Cada segundo que se está ejecutando su código es un segundo más que algo externo a su script puede causar un problema. Para acelerar algo, debes medirlo. Aquí es donde entra en juego la creación de perfiles de código.

La creación de perfiles es el acto de medir el rendimiento de varias partes de su código para identificar dónde se utilizan la mayoría de los recursos. En el caso de los scripts de AdWords, lo único que nos preocupa es el tiempo, por lo que podemos crear un generador de perfiles simple para medir el tiempo de ejecución de nuestro código.

/**
 * Simple code profiler to measure time
 * @param funcToProfile - the function to execute (required)
 * @param storeIn - a hash to store the results in (optional)
 *                  If not passed in, logs results to console
 * @returns {*} value returned by funcToProfile
 * @author Russ Savage @russellsavage
 */
function profiler(funcToProfile,storeIn) {
  if(!funcToProfile) { return; }
  var start,end,diff,retVal;
  start = new Date().getTime();
  retVal = funcToProfile();
  end = new Date().getTime();
  diff = end - start;
  if(storeIn != null) { 
    if(!storeIn[funcToProfile.name]) { 
      storeIn[funcToProfile.name] = diff; 
    } else { 
      storeIn[funcToProfile.name] += diff; 
    }
  } else { 
    Logger.log(['Ran function ',funcToProfile.name,' in ',diff,'ms.'].join('')); 
  }
  return retVal;
}

La función del generador de perfiles se puede colocar alrededor de cualquier parte de su código que le gustaría medir para determinar el tiempo de ejecución. Aquí hay un ejemplo:

function main() {
  profiler(function testing_profiler() {
    Utilities.sleep(5000);
  });
}

El código anterior debería imprimir algo como «Ran function testing_profiler in 5008ms». a los troncos. Si desea agregar la cantidad total de tiempo que se está ejecutando una acción en particular dentro de un bucle, puede enviar un argumento hash opcional y luego imprimir ese valor. Aquí hay un ejemplo:

function main() {
  var profilerLog = {}; // This is where I store the execution time
  for(var i = 0; i < 10; i++) {
    profiler(function testing_profiler_loop() {
      Utilities.sleep(1000);
    },profilerLog); // Make sure to pass it to the function
  }
  printProfilerLog(profilerLog);
}

/**
  * A simple function to print the profile log
  */
function printProfilerLog(log) {
  Logger.log('');
  Logger.log('PROFILER RESULTS');
  Logger.log('------------------------');
  for(var key in log) {
    Logger.log(['function ',key,' totaled ',log[key],'ms.'].join(''));
  }
  Logger.log('------------------------');
}

Observe cómo paso el profilerLog a la función del generador de perfiles en cada llamada. Eso me permite agregar las llamadas a cada función nombrada e imprimir los resultados usando printProfilerLog. Los resultados se verían así:

PROFILER RESULTS
------------------------
function testing_profiler_loop totaled 10016ms.
------------------------

Esto debería ayudarlo a descubrir dónde están las partes lentas de su código para que pueda optimizar. Solo asegúrese de eliminar cualquier código de creación de perfiles antes de comenzar a funcionar para que no ralentice las cosas.

Reintentos de API

Otro lugar que resulta útil es ayudar con la lógica de reintento para llamar a API externas. A veces las cosas fallan y, la mayoría de las veces, el mejor curso de acción es simplemente esperar un segundo y volver a intentar la llamada. Usando una técnica similar a la anterior, podemos implementar una función de reintento genérica que se puede usar con cualquier llamada externa.

/**
 * Simple code to retry a given function
 * @param theFuncToRetry - the function to retry (required)
 * @param retryTimes - the number of times to retry (optional)
 * @returns {*} value returned by theFuncToRetry
 * @author Russ Savage @russellsavage
 */
function retry(theFuncToRetry,retryTimes) {
  var times = retryTimes || 3;
  while(times > 0) {
    try {
      return theFuncToRetry();
    } catch(e) {
      Logger.log(e);
      times--;
      if(times > 0) {
        Logger.log('Retrying '+times+' more times...');
        Utilities.sleep(1000);
      } else {
        Logger.log('Out of retries...');
        throw e;
      }
    }
  }
}

Con esto, puede reintentar limpiamente sus llamadas a la API unas cuantas veces si fallan sin saturar su código con un montón de bucles «while».

Observe la llamada a la función Utilities.sleep () en la lógica de reintento. Por lo general, es una buena práctica esperar un tiempo antes de volver a intentar la llamada para que el servidor tenga la oportunidad de recuperarse. Un ejemplo para usar este código sería el siguiente:

function main() {
  var resp = retry(function() {
    return UrlFetchApp.fetch('http://www.example.com/api.json');
  }/*,10*/); //uncomment this to retry 10 times instead of 3
}

También puede enviar la cantidad de veces que desea volver a intentarlo; de lo contrario, el valor predeterminado es tres. Ahora no tiene que preocuparse por problemas intermitentes del servidor con sus API.

Notifíquese cuando las cosas vayan mal

La aplicación final que encuentro extremadamente útil es notificarte cuando tu script comienza a lanzar excepciones.

Muchas veces, la API externa cambia o incluso los scripts de AdWords cambian y sus llamadas comenzarán a fallar. El envolver toda su función principal le permite ser notificado fácilmente cuando su secuencia de comandos comience a fallar.

/**
 * Sends an email when there is an error in theFunction
 * @param theFunction - the function to execute (required)
 * @returns {*} value returned by theFunction
 * @author Russ Savage @russellsavage
 */
function notifyOnError(theFunction) {
  var TO_NOTIFY = ['you+error@example.com'];
  try {
    return theFunction();
  } catch(e) {
    Logger.log(e);
    var subject="[SCRIPT FAILURE] "+theFunction.name;
    var body = 'The script '+theFunction.name+' has failed with the following error: '+e;
    for(var i in TO_NOTIFY) {
      MailApp.sendEmail(TO_NOTIFY[i],subject,body);
    }
  }
}

Esta sencilla función se puede colocar al principio de su función main () para avisarle automáticamente por correo electrónico si su código comienza a fallar. Ahora, no solo verá el mensaje en los registros, sino que también recibirá un correo electrónico con el mensaje de error.

function main() {
  notifyOnError(function theNameOfTheScript() {
    var resp = retry(function() {
      return UrlFetchApp.fetch('http://www.example.com/api.json');
    });
  });
}

Conclusión

La capacidad de pasar funciones a otras funciones como argumentos puede ser muy útil para mantener sus scripts aislados del peligro del mundo exterior.

Pueden ayudarlo a perfilar su código para encontrar los puntos lentos y también hacer que sea más fácil de leer y mantener eliminando la lógica de reintentos y informes de errores de sus funciones principales.

Pero estos son solo algunos ejemplos. ¿Cómo utiliza esta técnica en sus secuencias de comandos de AdWords?


Las opiniones expresadas en este artículo pertenecen al autor invitado y no necesariamente a El Blog informatico. Los autores del personal se enumeran aquí.


Deja un comentario