Open.NAT – Tracing

Una de las deudas que suelen tener las apliaciones es que no cuentan con la capacidad de saber qué es lo que está sucediendo internamente en tiempo de ejecución y esto hace que muchas veces sean una caja negra de comportamiento misterioso. Entonces, cuando se encuentra algún problema en producción, estamos bien pero bien jodidos.

Open.NAT incorpora la capacidad de vigilar su comportamiento interno para detectar posibles problemas simplemente utilizando las capacidades propias de tracing de .NET y como verán abajo, no está nada mal. ¡A seguir trabajando! 

image

Open.WinKeyboardHook 1.0.7 RELEASED

NuGet version

Acabo de liberar Open.WinKeyboardHook, una librería que permite engancharse a los eventos del teclado a bajo nivel y capturar las techas que se presionan a nivel global en todas las aplicaciones.

Existen muchas aplicaciones para esta librería, de hecho KeyPadawan utiliza este código. He escrito acerca de KeyPadawan  aquí.

Hace tiempo que desarrollé estó pero ahora lo he publicado en NuGet.org para que pueda consumirse desde el administrador de paquetes de Visual Studio. También puede descargarse el código fuente y compilarlo.

Screenshot_3

Espero les sea de utilidad.

Peer2NET 1.0.6 RELEASED

Acabo de liberar Peer2NET, una librería que permite desarrollar aplicaciones peer-to-peer abstrayendo muchas de las complejidades inherentes al manejo de sockets, manejo de memoria, throtting y trackeo de conexiones. Su fuerte se encuentra sin dudas en su alta performance y en el control fino de los buffers y su API supersimple.

Hace tiempo que escribí un artículo en este mismo blog acerca de esta librería pero ahora la he publicado en NuGet.org para que pueda consumirse desde el administrador de paquetes de Visual Studio.

También puede descargarse el código fuente y compilarlo ya que cuenta con un mini ejemplo de chat para LANs que da una idea de cómo utilizar la API.

Screenshot_2

Espero les sea de utilidad.

Peer2.net

Peer2.net (https://github.com/lontivero/peer2net) es una librería muy liviana y sencilla de usar orientada a la creación de aplicaciones peer to peer en .net utilizando el protocolo tcp. El punto es que la programación con sockets, y en particular con tcp, introduce algunas complejidades que bien pueden abstraerse y que enumero a continuación:

  • Desempeño: Actualmente existen tres alternativas para utilizar sockets en .net, cada uno con sus pros y contras:
    • Llamadas bloqueantes: Connect, Send, Receive son métodos bloqueantes, es decir que una vez invocados, detienen la ejecución del hilo y esperan que se complete la conexión, el envío o el recibo de datos. Por este motivo, si queremos mantener comunicación, enviando y recibiendo datos desde y hacia distintos peers, debemos utilizar threads y sincronizar los datos compartidos. La vida es mucho más sencilla cuando se evitan los threads pero además este modelo no escala porque 100 peers significan 100 thread y los constantes cambios de contexto y las esperas producidas por la exclusión mutua degradan la performance de la aplicación por lo que no es recomendable  en aplicaciones con muchas conexiones como lo son las pee to peer.
    • Llamadas asíncronas (Asynchronous Programming Model o APM): Este modelo está en .net desde siempre y es muy popular aún hoy, consta de los pares de métodos BeginXXX/EndXXX en los que un callback es invocado cuando la conexión se establece, se han enviado o recibido los datos. No bloquean el hilo de ejecución pero el callback es invocado desde un hilo distinto al que ha invocado al BeginXXX por lo que cierta sincronización es necesaria. Además, a partir de la versión 4 del framework .net, podemos simplificar su utilización con TaskFactory.FromAsync lo que lo vuelve más sencillo y elegante a la vez. Esta alternativa es muy buena y mucho proyectos de software  muy populares lo utilizan (RavenDb es un ejemplo). No obstante, el desempeño ha sido mejorado en la versión 3.5 con los sockets de alto desempeño.
    • Clase SocketAsyncEventArgs: El framework 3.5 introduce un nuevo patrón alternativo para el manejo de sockets especialmente diseñado para aplicación que requieren alto desempeño mediante la clase SocketAsyncEventArgs. Este patrón, si bien más sencillo de utilizar que los anteriores, no está libre de detalles que son buenos de abstraer. Como por ejemplo el manejo del pool de instancias de la clase SocketAsyncEventArgs y su reutilización. Peer2.Net utiliza este patrón.
  • Memoria:
    • Fragmentación: Cómo ya escribiera en mi artículo anterior creando nuestro propio administrador de memoria en c#, las continuas operaciones con sockets que requieren la asignación de buffers fragmentan la memoria (mucho más de lo usual) a la vez que fijan esos segmentos de memoria para evitar que el garbage collector los relocalice (puesto que el sistema operativo los está utilizando). Todo esto hace que el GC no pueda hacer su trabajo con la misma eficiencia que normalmente lo hace por lo que una ayuda por parte de los desarrolladores es bienvenida (aunque no necesaria). Peer2.Net implementa su propio administrador de memoria.
    • Limitar el uso: muchas veces nos encontramos con escenarios en los que deseamos tener cierto control sobre el consumo de memoria, las razones pueden ser de distinta índole y suele ser complicado de implementar.  Peer2.Net es capaz de limitar el uso de los buffers de modo tal que si un socket necesita memoria para recibir datos pero no disponde un buffer libre lo suficientemente grande, postone la ejecución hasta que los otros sockets liberan tanta memoria como este necesita, y en tal caso ejecuta la acción.
    • Reuso de objetos: en un entorno en donde potencialmen cientos de sockets realizan cientos de operaciones por segundo, es importante reducir la creación de nuevos instancias de aquellos objetos que se utilizan con mayor frecuencia. Si bien .net es extremadamente eficiente en este punto, es decir, es capaz de crear objetos de manera sorprendentemente eficiente, existen dos consideraciones especiales por el que la utilización de pooles de objetos tiene sentido, la primera razón es que el nuevo patrón asíncrono de sockets para aplicaciones de alto desempeño tiene implicitamente el requerimiento de la reutilización de las instancias de la clase  SocketAsyncEventArgs. La segunda razón para reutilizar instancias es que, si bien la creación de objetos es realmente eficiente, su creación requiere de más memoria y trabajo extra de destrucción y relocalización por parte del garbage colector. Peer2.Net hace uso intensivo de la reutilización de algunos objetos.
  • Ancho de banda: una aplicación peer to peer que no hace control de ancho de banda, esto es, restringir cuantos bytes pueden enviar/recibir a/desde los peers por unidad de tiempo, puede terminar consumiendo la totalidad del ancho de banda disponible e impedir que el usuario continúe trabajando con normalidad.
    • Medición: Peer2.Net lleva la cuenta de cuantos bytes se han enviando/recibido desde que se estableció la conexión como así también la velocidad promedio y la velocidad actual de transferencia. Estos datos son además utilizados para controlar la velocidad.
    • Control: Peer2.Net permite controlar (limitar) la velocidad de transferencia para que esta se ubique en el valor deseado. Esto es posible hacerlo conexión por conexión y subida y bajada independientemente. Es decir, suponiendo que estamos conectados a dos peers (A y B), podemos establecer que queremos enviar a 10Kb/s al peer A y a 50Kb/s al peer B mientras que queremos recibir a 200kb/s desde el peer A y a 1Kb/s desde el peer B.
  • Transmission Control Protocol: Tcp introduce también cierta complejidad ya que no garantiza que una operación de envío por parte de un nodo requiera una operación de recibo en nodo receptor. Esto es, si enviamos ‘hola’ a otro nodo con una sola operación de envío, puede que al destinatario le llegue ‘hola’, ‘hol’, ‘ho’ u ‘h’ por lo que necesitamos realizar tantas operaciones de lectura como sean necesarias. Peer2.Net maneja esto de manera automática repitiendo la operación de recibir hasta obtener la cantidad de bytes solicitados.
  • API simplificada: el API de Peer2.Net consiste básicamente de los métodos Connect, Send y Disconnect para conectarse a otro nodo, enviar datos y desconectarse del mismo. Por otra parte, expone callback DataReceived y Disconnected para notificar sobre la llegada de datos o la desconexión de un nodo. Otros callback notifican de errores al intentar conectarse o enviar datos. Esto permite la creación de un chat en poco más de 100 LOC.
  • Thread: Peer2.Net invoca los callback ya mencionados arriba de manera sequencial por lo que no debemos preocuparnos de los problemas de concurrencia que típicamente dominan la programación multi-hilo. En realidad, Peer2.Net utiliza una cola para sincronizar los thread que maneja internamente con el thread del usuario.

Arquitectura

El siguiente diagrama muestra dos de los componentes centrales del proyecto con los que interactuan las aplicaciones:

CommunicationManager

  • Listener: es el encargado de escuchar las solicitudes de conexión entrantes. Cuando una nueva conexión es establecida este componente notifica mediante un evento (ConnectionRequested) a sus subscriptores sobre la nueva conexión de manera asíncrona (para poder reanudar inmediatamente la escucha).
  • ConnectionManager: es el encardado administrar las conexiones y las operaciones de conexión, desconexión, envio y recibo de datos como así también llevar las estadísticas de bytes enviados y recibidos y, la velocidad de envio/recibo de cada conexión como así también la global. Además es el dueño del hilo principal en el que se realizan las operaciones de lectura escritura.

 

El trabajo de más bajo nivel de coneción, envio y recepción de mensajes es realizado por la clase ConnectionIOActor (no es un actor model realmente). Esta es la que se encarga de la asignación de buffers, de limitar el ancho de banda, de los timeouts de las conexiones, de realizar las suscesivas operaciones de envio/recepción de mensajes mediante la administración de las colas de envio y recepción. 

BufferAllocator, por su parte, es el dueño del array de bytes que se utilizará como buffer y se encarga del asignamiento y liberación de segmentos del mismo para ser utilizado por los sockets.

ConnectionIo

El framework lleva varias métricas sobre lo que sucede con cada conexión y las pone a disposición mediante la clase Peer. El diagrama de abajo es bastante autoexplicativo.

peer

 

Ejemplo (Clásico chat)

Para ejemplificar el uso de Peer2.Net, he creado un pequeño chat (un ejemplo no muy original, lo sé) el cual requiere de tan solo 100 líneas de código y puede verse en la imagen de abajo.

chat

Finalizando

Peer2.Net simplifica notablemente el desarrollo de aplicaciones que necesitan conectarse de manera ‘igualitaria’ como lo son la aplicaciones peer to peer abstrayendo muchas de las complejidades que presentan el desarrollo con sockets, gracias a una interface ultrasencilla, a la vez que permite la transmición de bytes en crudo.

Peer2.Net está siendo desarrollado en mis momentos libres (que son escasos) por lo que cualquier feedback o ayuda es bienvenida 😉

Creando nuestro propio administrador de memoria en c#

Cuando comencé a programar en c# pensé que nunca más iba a volver a divertirme con algoritmos como los de administración de memoria, pero estaba equivocado! El hecho es que estoy trabajando en un proyecto ‘mascota’ en el que potencialmente cientos de sockets pueden estar enviando y recibiendo datos de manera asíncrona, potencialmente cientos o miles de veces por segundo. Posiblemente te preguntarás qué tiene que ver eso con la administración de la memoria, o más precisamente por qué necesitaría alguien administrar manualmente la memoria al viejo estilo (allocate/free) en un entorno administrado que cuenta con una maravilla como el Garbage Collector?

Por qué no dejarle esto al Garbage Collector?

Bien, la razón es que en este caso particular, el GC no puede hacerlo tan bien. Antes de poder avanzar en la explicación del por qué ‘este caso’ es especial, primero debo explicarles brevemente qué es lo que el GC hace cuando corre. Básicamente, en la siguiente imagen tenemos 15 bloques de memoria (por bloque entiéndase lo que vos quieras, bytes, words, o lo que sea) de los cuales 12 están asignados y 3 están disponibles, en un momento, el GC bloquea todos los threads de la aplicación e identifica aquellas porciones de memoria que han dejado de estar referenciadas y las libera (simplemente un tema administrativo), y así llegamos al paso 3. Ahora bien, se han liberado 4 bloques de memoria los que, sumados a los 3 restantes, nos da un total de 7 bloques libres, no obstante si la aplicación solicitara 4 bloques continuos (para alojar un array o una instancia de alguna clase) el sistema no sería capaz de asignarle nada ya que no dispone de 4 bloques continuos. En otras palabras, la memoria está disponible pero no te la puedo dar 😉 A este problema se le llama fragmentación.

Garbage Collector y la fragmentación

Para resolver ese problema el GC ejecuta la compactación de la memoria como se puede ver en el paso 4, pero no solo eso, también actualizar las referencias de todos los objetos! Es decir, por ejemplo, en el paso 3 podemos ver que en la posición 4 (base 0) hay un bloque de memoria asignado, supongamos que está referenciado por una variable X, entonces mientras que antes de la compactación teníamos referencia(X) = 4, luego tenemos que referencia(X) = 2. Está de más decir que si el GC no actualizara las referencias luego de mover los bloques de memoria, las referencias apuntarían a cualquier parte dejándolo todo un desastre.

image

Entonces, ¿por qué administrar nosotros mismos?

Ahora si, por fin vamos a lo que veníamos: responder a la pregunta ¿por qué administrar manualmente la memoria en .NET en este caso? Para ello veamos el siguiente código:

image

Ese array buffer es una variable administrada, no obstante, el método BeginReceive invoca una función del sistema operativo indicándole la dirección de memoria del buffer para que cuando arriben datos a ese socket el sistema los almacene en esa dirección de memoria. Ahora imaginate que el GC decide liberar la memoria no usada y compactarla #@$%!…. El sistema operativo terminaría escribiendo en la dirección de memoria que se le indicó, la cual estaba referenciada por la variable buffer, pero esos bloques se pueden haber movido y la referencia haberse actualizado!!! Obviamente el SO no sabe nada de eso y terminaría escribiendo (y sobreescribiendo) en una dirección incorrecta.

El problema de los objetos ‘fijos’

Para que esto no suceda, antes de pasarle una dirección de memoria al sistema operativo (u a otro software que requiera una dirección de memoria para leer/escribir) necesitamos fijar esa referencia. Esto es, avisarle al GC que esos bloques no pueden ser movidos durante una compactación. Pero esto trae consecuencias, veamos la siguiente imagen:

image

Como vemos, los bloques 9 y 11 están fijos (no se mueven durante la compactación) por lo que si bien ahora tenemos nuevamente 7 bloques libres, no disponemos de más de 3 bloques continuos, es decir, si nos piden 4, 5, 6 o 7 bloques continuos no podemos dárselos. Esto es incluso más complejo de lo que muestro en el gráfico ya que cuando existen bloques fijos, la compactación se dificulta muchísimo porque ya no basta con simplemente mover los bloques de manera que ocupen direcciones contiguas sino que además hay que calcular cuales conjuntos de bloques (imaginemos un objeto que ocupe varios bloques) pueden ser movidos a donde. Complejo, no!?

Bueno, ahora imaginemos una situación con cientos o miles de bloques fijados, tendríamos un muy serio problema en las manos por la alta fragmentación que generaría. Es entonces cuando debemos pensar la conveniencia de administrar nosotros mismos la memoria, y no estoy hablando de manejar puntero en código no seguro; claro que no.

Cómo lo resolvemos

Una vez que reconocemos que tenemos este problema (la aplicación misma, el cliente, o el profiler, nos lo hacen saber) debemos implementar nuestro propio manejador. No voy a escribir demasiado sobre esto porque puede encontrarse en cualquier libro de sistemas operativos (yo lo aprendí en su momento del libro de SO de Andrew Tanenbaum, dicho sea de paso: un groso absoluto) pero si voy a comentar el algoritmo que he implementado.

La idea básica consiste en solicitar un bloque de memoria, el cual será administrado por nosotros. ¿Cómo se hace esto? Muy fácil, si necesito 1Mb de RAM para administrarla yo mismo, lo hago así:

image

Ahora bien, esta memoria no es distinta a ninguna otra, es decir, puede ser movida por el GC ya que nada se lo impide, o no? ummm… veamos… si y no. Tal como lo vemos podría parecer que sí, que puede ser relocalizada sin más, pero existen algunas maneras de impedirlo, la primera es no haciendo nada en lo absoluto, porque un array como ese (10MB) se considera un objeto grande por lo que se aloja en el LOH (Large Objects Heap) que no se compacta ya que es sumamente costoso. Otra alternativa es continuar haciendo nada y esperar, a medida que el tiempo pasa sin que se pierda la referencia el objeto memory el GC va compactando la memoria y los objetos más antiguos van decantando de a poco, así luego de algún tiempo, nuestro objeto memory se encontrará en la parte baja de heap y no volverá a moverse en las sucesivas compactaciones (o lo hará con muchísima menor frecuencia). Por último, podemos simplemente fijarla nosotros como sigue:

image 

Mi recomendación es no hacer nada en lo absoluto.

¿Solo sirve para arrays de bytes?

No, la idea sirve para todo tipo de objetos solo que puesto que los arrays (de bytes en especial) son uno de los casos más comunes, me he centrado más en ellos pero si por ejemplo, tengo objetos que representan coordenadas (x,y) que se pasan a otro sistema (digamos OpenGL) y por lo tanto van a ser fijados por las llamadas PInvoke, entonces los que nos conviene es crear un pool de instancias y reutilizarlas. El punto es que al reutilizarlas no las dejamos morir, de modo que como ya dije, van a ir decantando hacia la parte baja del heap en donde las relocalizaciones son mucho menos frecuentes y puesto que la segmentación es menor.

Volvamos a los arrays

Ya sea porque no dejamos morir la referencia (scope), porque el array es muy grande (y por lo tanto se aloja en el LOH), o porque la fijamos nosotros mismos mediante GCHandle.Alloc, el punto es que tenemos un array de bytes disponible para nosotros. Ahora bien, cuando necesitamos un buffer para nuestro socket almacene los datos que recibe nosotros podemos simplemente asignarle una porción (segment) de ese gran array. Por ejemplo, si quisiésemos asignarle el primer 1KB a partir de la posición 512, lo haríamos así (La línea realmente importante es la primera):

image

Ahora bien, eso fue fácil, el problema que tenemos ahora es cómo saber cuales segmentos están disponibles y cuales están siendo utilizados como buffers por algún socket. Eso es simplemente un problema de administración y para ello existen varios algoritmos.

Administrar la memoria

La administración de memoria consiste básicamente en llevar un registro de los espacios ocupados y libres, esa es toda la ciencia, no obstante es un tema complejo y existen muchos algoritmos (y sus variantes; algunos mejores para ciertos escenarios que otros) y probablemente el más utilizado sea el de tener una lista enlazada en la que se registran los bloques usados y libres mediante sus direcciones { Begin, End }. La ventaja aquí es que cuando necesitamos un bloque de N bytes solo tenemos que recorrer la lista de espacios libres chequeando si tenemos un segmento lo suficientemente grande para asignar (existen diferentes estrategias para esto). Lo malo es que cuando se libera memoria, la carga administrativa es mayor puesto que podemos tener un espacio de N bytes contiguos disponibles pero los tenemos registrados como 2 segmentos de N/2 bytes por lo que cuando nos piden N bytes, no encontraremos un segmento lo suficientemente grande aunque exista (fragmentación lógica). Entonces es necesario mergear los bloques libres adyacentes y eso consume procesador (tengamos en cuenta que la asignación/liberación de memoria ocurren con una frecuencia altísima).

Mapa de Bits (a lo bruto)

La primera implementación del BufferAllocator fue utilizando un mapa de bits, pero a lo bruto (en lugar de utilizar bits utilicé chars). Esto es, supongamos que la unidad mínima de memoria que vamos a asiganr es de 128 bytes, así que por cada 128 bytes utilizamos un char con un valor ‘-’ indicando que el bloque está asignado o con un valor ‘f’ indicando que el bloque está libre y puede ser asignado. La siguienet image es bastante clara:

image                        

Entonces si nos piden 200 bytes, lo primero que hacemos es calcular cuantos bloques de 128 bytes necesitamos, en este caso sería dos. Luego buscamos en un en el mapa ese espacio y si está disponible, luego de marcarlo como asignado, lo retonamos. En código sería más o menos así:

image

Si bien a este código le faltan todos los tipos de validaciones, como el qué hacer cuando blockOffset es –1 (no se encontró el patron, es decir, no tenemos un segmento libre lo suficientemente grande para asignar), es solo ilustrativo.  hay que decir que la liberación de la memoria es incluso más sencilla. Veamos:

image

Aunque funciana, no deja de ser una implementación de juguete. La siguiente ímplementación fue con listas enlazadas, simplemente copiada de Minix: https://github.com/minix3/minix/blob/3399c2c966a57b6e50225330417f7577fd5d04ba/sys/lib/libsa/alloc.c

Binary Buddy System

La implementación actual que utiliza mi proyecto es Binary Buddy allocator este sistema se basa dividir la memoria (en nuestro caso sería, nuestro array de bytes) en 2 porciones y luego dividir cada uno de los sub bloques en 2, y así suscesivamente. Para ello, recurrimos a un arbol binario en el caul el valor de cada nodo representa la cantidad de bloques de memoria disponibles en su hijo con mayor capacidad. La imagen de abajo muestra un arbol para administrar un máximo de ocho bloques de memoria.

image

La búsqueda de un bloque se realiza recorriendo el arbol ‘in-order’ hasta encontrar un nodo del tamaño adecuado, así por ejemplo, si nuestros bloques son de 128 bytes y necesitamos 200 bytes de espacio, sabemos que serán necesarios 2 bloques y entonces recorremos el arbol ‘in-order’ hasta que Math.Pow(2, nodo.value) == blockCount. Por último, debemos eliminar ese nodo y actualizar hacia arriba los padres. Entonces, luego de asignar los dos nodos de memoria, nuestro arbol quedaría así:

image 

Luego, si pedimos 3 o 4 bloques, tendríamos lo siguiente:

image

Lo que me gusta de este algoritmo es su elegancia y velocidad, aunque desperdicia memoria por la fragmentación interna que genera, pero bueno, a mi me gusta y es la implementación que pienso dejar, o al menos hasta que encuentre la necesidad de cambiarlo. El código de esto puede verse aquí:

https://github.com/lontivero/P2PNet/blob/master/P2PNet/BufferManager/BuddyBufferAllocator.cs

Conclusión

Si bien el Garbage Collector del framework .NET es algo maravilloso, existen muchas ocasiones en las que por tener segmentos de memoria ‘fijos’, este no puede hacer su trabajo tan bien como lo haría si esos segmentos no existieran y, es ahí cuando nosotros podemos tomar acciones para facilitar el manejo de la memoria. Básicamente, lo más sencillo es crear un pool de objetos sencibles a ser fijados para reutilizarlos.

Cuando se trata de buffers de bytes e tamaño fijo, el pool suele ser la solución más adecuada también. No obstante, cuando no conocemos los tamanos de los buffers que nos va a solicitar la aplicación, lo mejor es implementar un buffer allocator como los que mostré. Otra posible alternativa es tener distintos pools (o pooles?) con buffers de distintos tamaños, por ejemplo, un pool de buffers chicos, otro con buffers medianos y uno con buffers grandes, y entonces cuando nos piden un buffer de X bytes podemos tomar un buffer del pool adecuado y retornárselo a la aplicación

 

Bitcoin: el oro del futuro

Afiche BITCOINS_MAIL

El próximo 27 de Junio a partir de las 18:30, estaremos dando un evento sobre Bitcoins. Algo que ya se está moviendo en el mundo desde hace tiempo, pero aún no ha llegado a estas tierras con tanta fuerza como debería.

Si querés enterarte de que se trata, te aconsejo que vayas al evento.

Introducción

En un mundo que avanza aceleradamente hacia la digitalización de todo cuanto es posible, Bitcoin es la evolución natural del dinero,  una moneda digital que está cobrando cada vez mayor relevancia en el mundo entero. La posibilidad de tener dinero en la nube para transferirla de manera inmediata, sin costo y de manera segura a cualquier persona, en cualquier parte del mundo introduce cambios en el comercio tal y cual lo conocemos hoy a la vez que promete transformar de manera radical todos aquellos elementos en los que el  dinero interviene.

Objetivos:

  • Difundir el conocimiento de esta moneda digital entre el público que desee iniciarse en su uso,
  • Explicar sus beneficios, potencialidad y desafios que introduce en el mundo globalizado,
  • Presentar los posibles futuros escenarios en el uso de la moneda.

Temas

  1. Presentación de bitcoin como moneda, historia, conceptos, su uso, tecnología, monedas alternativas, seguridad, mercados, valor actual y probable valor futuro.
  2. Mercado, qué se puede comprar con bitcoins, aspectos legales, regulación, valor intrínseco y mitos.
  3. Cambios en el comercio, en la sociedad, en el sistema bancario, inflación/deflación y prestamos.
  4. Evolución del bitcoin en el mundo
  5. Bitcoin en el contexto Argentino, volatilidad, trading, alternativa al dolar/oro como resguardo de valor.

El lugar elegido es, y gracias nuevamente, el SUM del colegio universitario IES, en Buenos Aires 563, Nueva Córdoba.

Para inscribirte, sigue el siguiente link: https://docs.google.com/forms/d/1lsuF7C-dZD3Xwtjam8_TDoSC-OnPnahcDmCXpRowVgM/viewform

del.icio.us Tags: ,

Bitcoin-hoy.com – Calcular el Precio del Bitcoin en argentina

Para todos aquellos que necesitan calcular constantemente para convertir Bitcoins –> Dolar Blue (y Oficial) –> Pesos Argentinos y viceversa, he puesto a disposición una calculadora online (está en alpha, o menos que eso… pero antes no hay nada…ummm). Espero que sea de utilidad, pronto estaré poniendo más servicios relacionados a esta cryptomoneda en el mismo dominio.

Visiten:
http://bitcoin-hoy.com/

Deberían los testers arreglar bugs–Parte I

Esa es la pregunta que lanzé en el sitio Software Quality Assurance and Testing de StackExchange.com en cuyas respuestas pude ver que existe cierto desacuerdo entre la comunidad de testers sobre ese tema y si bien yo tengo una idea clara al respecto, lo que en verdad quería conocer eran las argumentaciones de aquellos que piensan que sí deberían arreglar defectos (algunos defectos) y aquellos que piensan que no deberían para así hacer una síntesis.

No obstante, al poco tiempo otro usuario inicia otro hilo en el que pregunta cuales son las principales responsabilidades de un tester en las que expone una pequeña lista de responsabilidades:

1) Analyzing the Requirements from the client
2) Participating in preparing Test Plans
3) Preparing Test Scenarios,test cases
4) Defect Tracking
5) Preparing Suggestion Documents to improve the quality of the application
6) Communication with the Test Lead / Test Manager
7) Conducting Review Meetings within the Team

Hay tantas cosas con las que no estoy de acuerdo con lo que leo en ese foro que voy a dejar plasmado aquí mi pensamiento. Para comenzar tengo que decir que para mi, la responsabilidad de todos los miembros de un equipo es exactamente la misma: contribuir tanto como sea posible al éxito del proyecto. Entonces, si bien las actividades, especialidades y orientaciones pueden ser distintas, la responsabilidad es la misma.

Ahora bien, si aceptamos esto como válido deberíamos entonces definir qué es un proyecto exitoso y qué no lo es. Para todos aquellos medio entrado en años (de profesión) como yo de seguro les sonará el cuentito llamado OTOBOS que dice que un proyecto exitoso es un proyecto “on time, on budget, on scope” y la verdad es que en los tiempos del los modelos predictivos, en los que el SDLC era por lo general en cascada, mantener al proyecto a lo largo del tiempo on time, on budget y on scope era cosa de magos, se creía que la clave estaba en el *control*. Pues bien, puede que me equivoque pero voy a darles *mi* definición de lo que es un proyecto exitoso:

“Un proyecto exitoso es aquel que nos otorga un alto retorno sobre la inversión a la vez que nos permite crear una relación de confianza con el cliente”

La segunda parte de esta definición habla de la satisfacción del cliente, y puesto que lo que este recibe no es otra cosa que software, deberíamos aclarar qué es un software exitoso para el cliente. Veamos, nuestros clientes no nos encargan el desarrollo de una solución porque no se les ocurran mejores cosas en que despilfarrar el dinero, sino que lo invierten en una herramienta que les permita cumplir con sus metas de negocio. Ya sea optimizando o agilizando la gestión de la compañia, diferenciandose de la competencia, fidelizando clientes, brindando nuevos servicios online o alguna otra ventaja, las empresas *invierten* en software y esperan un retorno de esa inversión. Entonces:

“Un software exitoso (para el cliente) es aquel que le permite cumplir con sus metas de negocios y por tal, le otorga el esperado retorno sobre la inversión”

Y es que de eso se trata y siempre se ha tratado, de dinero. Por lo tanto, la responsabilidad de *todos los miembros del equipo* (sí, testers incluidos) es contribuir a este éxito. Y para esto hace falta mucho más que control y responsabilidades estrictas y predeterminadas.

Continuará…

Lo que espero de un buen desarrollador

El buen desarrollador, además de poseer conocimientos y experiencia superiores debe estar infectado por una pasión altamente contagiosa, de esa manera, tarde o temprano el equipo completo se verá contagiado en mayor o menor medida. Esto no es tan inusual, muchas veces nos encontramos con personas que se convierten en referentes no solo de sus equipos sino que también de otros equipos de la misma empresa y todos se benefician de sus conocimientos y su dedicación, especialmente la de enseñar, compartir conocimientos y experiencias… esas cosas que no se encuentran en los libros.

La mayor contribución de estos desarrolladores no suele plasmarse tanto en su código sino en la capacidad de hacer de otros, mejores desarrolladores. Y es que como la experiencia no se adquiere de los libros sino de los errores (no solo propios), es este tipo de desarrolladores el que puede salvar a los menos experimentados de cometerlos. Por último, y para ampliar el punto anterior, otra cualidad necesaria para lograr esto es el conocimiento profundo de la história del software, sus personalidades, sus aportes, sus pensamientos, sus aciertos y errores.

Cómo fallar en el desarrollo – distinguiendo lo importante de lo accesorio

Martin Fowler escribió un interesante artículo llamado TransMediaApplication en el cual explica un problema común que se presenta a la hora de desarrollar aplicaciones para distintos dispositivo. Creo que esta frase resume todo el artículo: A common mistake is to think of different apps for different delivery devices.

En mi experiencia este escenario no se presenta solo cuando se trata de aplicaciones que corren en distintos dispositivos sino que se presenta con mucha mayor frecuencia de lo que se cree. El caso es que lo que Martin Fowler describe es un caso muy particular de un problema mayor: no comprender de que se trata en parte la arquitectura.

Lo voy a explicar con un ejemplo. Imaginemos que una empresa de transporte de carga nos pide desarrollar un sistema de seguimiento de sus vehículos. Estos vehículos tienen rutas específicas por las que deben transitar y tienen además una velocidad máxima autorizada. Nuestro cliente quiere conocer la ubicación de cada uno de sus camiones y sus velocidades (entre otras miles de cosas) prácticamente en tiempo real o con una demora no mayor a 1 minuto.

Para esto, cada vehículo tiene incorporado algún tipo de dispositivo móvil con GPS (o alguna variante). Nuestro cliente quiere visualizar la posición de sus vehículos en un mapa (en principio, mapas de google) y quiere poder hacerlo desde su dispositivo móvil.

Ahora bien, si quien lee esto fuese el responsable del desarrollo de este sistema… ¿por donde comenzaría?. Muchas personas comienzan por interiorizarse sobre los distintos modelos de dispositivos móviles incorporados a los vehículos, comienzan a interiorizarse por los diferentes sistemas de coordenadas que manejan, por los distintos mecanismos de despacho de esas coordenadas, esto es, si tienen conexión a internet continua, si soportan mensajes SMS, si http, tcp o udp, por los servicios que soportan en cada uno de sus puertos, etc. Luego, continúan por estudiar las apis de google maps, los lenguajes en los que esas apis están disponibles y demás consideraciones sobre cómo trabajar con esos mapas. Por último posiblemente, querrán conocer desde que tipo de dispositivo móvil es que el cliente quiere poder consultar la información.

Todo lo anterior está mal! No quiero decir que no deba hacerse ya que tarde o temprano conocer estas cosas será muy necesario, pero está mal porque denota una falta de análisis reflexivo sobre qué es lo que se debe desarrollar y sobre qué es lo que el cliente (negocio) necesita. Veamos, en la descripción del problema existen varios elementos que debemos notar y sobre los que tenemos que reflexionar, estos son: seguimiento, rutas, posición, velocidad, vehículo, dispositivo móvil, GPS y mapa. Existen otros elementos mencionados directa o indirectamente, pero quiero focalizarme solo estos.

A estos elementos debemos agruparlos en dos categorías: esenciales y accidentales. Así, posición, ruta, velocidad y seguimiento son esenciales mientras que dispositivo móvil, GPS y mapa son accidentales. Vehículo, por su parte, es un sinsentido. Si bien por alguna razón, las descripciones de los problemas que nos proveen los clientes suelen abundar en elementos accidentales, es nuestra responsabilidad el identificarlos y catalogarlos como tales en primera instancia.

El sistema a desarrollar debe, en principio, ser totalmente independiente de consideraciones superfluas como el protocolo de comunicación por medio del cual recibirá la posición de los vehículos. Si lo hace mediante TCP, UDP, por mensajes SMS o por correo twitter no es relevante en principio. Tampoco lo es la manera en que la información será presentada, ni si los clientes corren en dispositivos móviles o en PCs de escritorio, sobre un web browser o una aplicación nativa. Todas estas cosas deberán abordarse en su momento pero es importante comprender que se debe esperar a que llegue ese momento.

¿Y mientras tanto qué? Bueno, mientras no llegue el momento de encarar los detalles debemos centrarnos en resolver el problema esencial: desarrollar el sistema de seguimiento. Esto es lo primero!

En el software como en la vida, no distinguir lo importante de lo accesorio es el camino seguro al fracaso.