sábado, 17 de octubre de 2015

EIG - Abonando a un ladrón de varias cabezas



Hace algunos meses realicé la adquisición de un servicio de alojamiento con un proveedor llamado Arvixe, no puedo quejarme de esos primeros meses, el servicio muy bueno, la atención por parte del personal de soporte muy buena, y el rendimiento del servidor bastante bueno.

Después de un buen tiempo, hubo un cambio radical en sus servicios, y puedo decir que en toda su gama por la cantidad de tickets y quejas que llueven en twitter, a lo que nuestros amigos siempre se limitan a responder algo así:
  1. "Gracias por proporcionar el id del ticket, te responderé en él".
  2. "¿Podrías proporcionar el id del ticket?"
Obviamente tienen un programa muy sencillo donde reciben sus tweets y apretando un botón, se realiza la respuesta del mismo.    ¿El esfuerzo?, elegir entre la respuesta 1 o la respuesta 2, de ahí en fuera, nada.

Después de esperar por más de 3 horas y media con el chat abierto, finalmente una persona atiende la conversación, después de comentar la situación y mencionar que el 99.99% que GARANTIZAN en su página de inicio no se estaba cumpliendo, esta personita comentaba que como era un problema grave ellos no podían dar ese 99.99%.

En este momento pensé, vamos 2-0 y la atención es inexistente.    Procedí a levantar un ticket, después de esperar 6 horas, levanté otro... y así siguió el ciclo, el cual no interrumpí para verificar en qué momento o qué tipo de ticket es el que responden.

¡Finalmente respondieron!, después de 48 horas con el servidor totalmente caído y sin respuesta por ningún lado, pude ver en la lista que uno de tantos estaba respondido, ¿qué podría ser?, entre emoción y coraje abrí el ticket, ¡wow!, la respuesta fue increíble: "olvidó colocar los últimos 4 caracteres de la contraseña de su usuario de facturación, para poder atender su ticket, necesitamos esa información".

Este ir y venir con esta gente lo voy a hacer a un lado, y no por falta de más cosas qué decir, sino que me llamaba la atención que la calidad en el servicio decayera de una forma tan estrepitosa y en tan poco tiempo.

Después de investigar un poco, supe que un corporativo con las siglas EIG (Endurance International Group) había adquidido a Arvixe, y ahí comencé a darle al clavo.

Sucede que estos amigos de EIG son dueños de muchos (pero muchos) proveedores de alojamiento, ¿qué tienen en común aparte del dueño?, que sus primeros meses de servicio son "aceptables", posteriormente comienzan los problemas y niegan cualquier posibilidad de devolución de dinero para sus clientes, ¿qué sucede?, el cliente termina yéndose a otra compañía.

Encontrarán muchas entradas de blog donde te recomiendan pasarte del proveedor X al proveedor Y, que con el proveedor Y están súper contentos y que el servicio es espectacular.    Si le buscamos un poco, ¡el proveedor Y pertenece también a EIG!, ¿resultado?, basta con buscar un poco en los foros de soporte y cuentas de redes sociales del proveedor Y, para darte cuenta que es igual de asqueroso que el proveedor X.

Al final, en todos estos blogs encuentras que te recomiendan ir saltando entre proveedores que pertenecen a esta empresa EIG, al final le caerá dinero a los mismos ladrones y tu servicio seguirá siendo de pésima calidad.

Quiero finalizar esta entrada de blog con una recomendación sumamente simple, cuando vayas a contratar algún servicio de alojamiento, ¡verifica que no sea una empresa que forme parte del grupo EIG!, te ahorrarás muchos dolores de cabeza y pérdidas económicas.

martes, 5 de mayo de 2015

Incorrect syntax near 'go'



El día de hoy me encontré con que un compañero estaba sufriendo bastante para crear un programa en .NET (aplicación de escritorio) que instalara una base de datos, desde su aprovisionamiento hasta la inicialización.

Al revisar el código que estaba utilizando, me encontré que los comandos en el archivo de recursos incluían la palabra GO.

Antes de abordar una solución simple, me gustaría explicar para qué sirve la palabra reservada GO en una sentencia SQL.

Vamos a abrir la página de opciones del SQL Server Management Studio:


Notemos que la opción "Batch separator" tiene el valor GO.    De ello podemos deducir que es una opción de configuración para SQL, de manera tal que en un sólo script podamos meter varias sentencias sin que una interfiera con la otra.    La palabra reservada GO no hace más que decir que ya terminó la sentencia y que la sentencia posterior es totalmente independiente a la anterior.

Vamos a probar esto con un ejemplo muy sencillo:

DECLARE @CurrentTime DATETIME = GETDATE()
PRINT @CurrentTime
GO
PRINT @CurrentTime


El resultado será el siguiente:


La primera sentencia PRINT sí se está ejecutando de forma exitosa, pero la segunda está marcando un error, y esto se debe a que la palabra reservada GO hace un "borrón y cuenta nueva", de manera tal que la variable @CurrentTime ya no existe en la segunda sentencia PRINT.

Debido a esto, el query que está ocupando mi compañero, está marcando el mensaje de error "Incorrect syntax near 'GO'"

Lo que menos se quería era tener que modificar el script para poder instalar la base de datos, por lo que encontramos una solución muy sencilla, crear una función que recibiera el script completo (con todo y GO) y lo dividiera en varios scripts tal como hipotéticamente se hace en SQL Server cuando se ejecuta, y el resultado fue el siguiente:

static void ExecuteCommand(string sqlCommandText, SqlConnection conn)
{
  SqlCommand comm = new SqlCommand("", conn);
  foreach (string sqlCommand in sqlCommandText.Split(new string[] { "GO\r\n" }, StringSplitOptions.RemoveEmptyEntries))
  {
    comm.CommandText = sqlCommand;
    comm.ExecuteNonQuery();
  }
}


Nuestra función aparte recibe una conexión abierta y disponible para ser utilizada.

Podemos notar que lo único que se está haciendo es dividir el script completo en pedazos ejecutables desde .NET.

La solución es sumamente simple, no hubo que ajustar nada en el código del script y la instalación de la base de datos ya tiene un resultado exitoso.

Espero te haya resultado de utilidad, nos vemos la siguiente entrada.

jueves, 30 de abril de 2015

Covering indexes



Los índices de cobertura, son aquellos que nos ayudan a mejorar el rendimiento de ciertas consultas que utilizan un índice y que cuyo resultado incluye un mismo conjunto de columnas.

Tal como en las anteriores entradas, vamos a aterrizar esto en un ejercicio para que se vea la utilidad de estos elementos.

Tomaremos la base de datos AdventureWorks como ejemplo y partiremos del siguiente query:

SELECT ProductID, Name, ProductNumber, MakeFlag
FROM Production.Product
WHERE Color = 'White'


Si revisamos el plan de ejecución, notaremos que se está llevando a cabo un barrido del CLUSTERED INDEX de la tabla Production.Product:


Con un costo estimado de 0.0127253, el cual ciertamente es bastante bajo, pero esto se debe a la poca cantidad de elementos que tiene nuestro conjunto.

¿Qué es lo primero que haríamos para mejorar el rendimiento de esta consulta?, claro, irnos a la parte del WHERE y analizar el predicado.    Después de revisarlo podemos proponer un índice sobre la columna Color de la tabla Production.Product.

CREATE INDEX ixColor
ON Production.Product (Color)


Vamos a revisar nuevamente nuestro plan de ejecución:


Notaremos que hay un cambio importante, ahora se está realizando una búsqueda en el índice ixColor, justamente el que acabamos de crear.    Notemos también que se está llevando a cabo una tarea Key Lookup que sirve para encontrar las columnas que nos faltan para poder dar el resultado, las cuales son ProductID, Name, ProductNumber y MakeFlag.

El costo de este query cambió de 0.0127253 a 0.0126079, lo cual significa una variación menor al 1%, en verdad que no es nada impresionante si lo comparamos contra el costo que tendrá para la base de datos el mantener al día este nuevo índice respecto a los cambios que sufra la tabla a la que apunta.

Pero bueno, tomemos en cuenta que tenemos sólo 504 filas en la tabla de productos, también es por ello que la variación es tan pequeña.

Ahora, vamos a ahondar un poco más en el Key Lookup:


Nuestro índice ixColor, sólo guarda los valores del Color y una liga hacia la tabla Production.Product donde se almacena la fila a la que pertenece, de ahí que se tenga que hacer ese Key Lookup para encontrar los datos que le hacen falta al query para devolver el resultado completo.     Notemos que en la imagen aparecen en Output List Name, ProductNumber y MakeFlag.

¿Qué pasa si siempre regresamos las mismas columnas?, ¿qué pasa si este query regresa un subconjunto de columnas que habitualmente se ocupan?    Si este es el caso, estamos ante la perfecta opción de utilizar un índice de cobertura (covering index)

¿Qué es un covering index?, es aquel que almacena también los valores que forman parte del resultado, es decir, que ya no debe de ir a la tabla original por ellos dado que tiene una copia actualizada en él.

Vamos a crearlo:

CREATE INDEX ixColorCovering
ON Production.Product (Color)
INCLUDE (Name, ProductNumber, MakeFlag)


Y analicemos nuevamente el plan de ejecución para ver los cambios:


Podemos notar que ahora se está utilizando únicamente nuestro índice ixColorCovering.    ¿Qué costo tiene actualmente el query?, el costo es de 0.0032864, lo cual significa una mejora del 74.17%.

¿Qué tal?, ahora sí impresiona ¿no?, y eso que estamos hablando de una tabla con 504 filas, ahora imagina que estuviéramos trabajando en una tabla con miles o millones de filas, verás dos impactos mayúsculos:
  1. Tiempo de respuesta muchísimo menor.
  2. Menor uso de memoria de SQL Server para poder despachar el resultado.
Antes de dar por finalizada esta entrada, quisiera remarcar lo siguiente: cuidado con los índices, recuerda que cada vez que generas un nuevo índice, SQL debe actualizar su valor si es que el valor original en la tabla fue alterado, esto significa que una simple escritura se puede traducir en muchas más.

Espero te haya resultado de utilidad esta entrada, nos vemos la siguiente.