VMware issues when copying virtual machines

11 01 2012

We at Codice Software use internally VMware for testing purposes; specifically we use VMware Server 2 to run automated tests (both smoke, GUI and integration tests) and VMware Workstation for manual testing, support, test new oportunities, platforms, applications and so on.

We’ve been using this software for several years and we are very happy with it. It performs great, fast, secure and flexible. Nevertheless, sometimes we’ve encountered some problems. My work as builder of the company includes setting up new testing virtual machines and maintenance, so I’m working with VMware almost everyday.

Recently I created a cloned virtual machine from a snapshot in a VMware Workstation 7. After setting it up I moved it to our automated testing hosts (FYI, we have seven hosts that handles between 5 and 20 virtual machines each, all of them for testing purposes). When I tried to start up the virtual machine in VMware Server 2 I got a crash in the web application that I only could solve by restarting the service in the host machine!

Finally I workarounded the problem doing the following:

  1. First, I created a new virtual machine in VMware server with the same hardware features as the original one.
  2. Edit the .vmx file of the new virtual machine changing it to use the disk files (.vmdk) of the virtual machine I want to register in VMware Server. It’s possible to do this from the “New virtual machine” wizard of VMware server or after that by editing the vmx file.
  3. Finally, start the new virtual machine.

This ensures the compatibility with VMware Server.

I hope you’ll find this helpful in the future.

Keep in touch.





Make it small and simple, please

11 06 2011

Here I’ll talk about usability: how can software applications be more useful, easy-to-use for users. I recently read a book, Rocket Surgery made easy (Steve Krug), and I really liked it; I found it very interesting and there was several hints that are very useful and should be taken into account when developing software applications, specially web applications, specially when designing graphical interfaces, or generally speaking, human interfaces.

So, I’d recommend all of you to read this book, but in this small article my aim is to explain how to fix those usability bugs detected by a user, a client, a developer, a tester or whoever. What’s the best way to fix usability bugs?

When it’s all about fixing usability issues, Steve Krug recommends to do the least you can do.

Yes, you read it right. Read it again if you thing you didn’t got it.

Sounds good now? Not yet? Okay, let me explain this, then. When fixing usability bugs, think about the smallest, simplest change that makes the problem not noticeable. Since usability ‘bugs’ are not usual bugs (there is nothing broken or nothing that is not working really), there is no point in doing a big effort in fixing them. It’s a matter of keeping the essentials of the application immutable but easier to use. Does this make sense to you?

In spite of this, there is some resistance to apply this idea. Reasons:

- “If we’re going to fix it, let’s do it right.” Instead of making things better for users right now, let’s obliterate the problem from the design, let’s spend more money and time and after a lot of time, if users still want to use our application they’ll feel happy to see this little thing fixed.

- “It’s a design problem. It’s not easy to fix it”. Come on, I’m sure that there is something that you can do to make it look better, don’t you?

- “Well, we’ll release a completely new version soon, so we can live with it by the moment”. Here Krug tells us that he had ‘temporary ‘countertops in his kitchen for ten years, just because “they were going to redesign the kitchen soon”.

- “I don’t want to cover my application with patches everywhere”. Ok, you’re right to some extent. But a patch is better than a hole, isn’t it?

- “We have a lot of work right now. We can’t fix it now”. Ok, you don’t have time for a perfect solution, but you have time to do something acceptable, don’t you?

Two key rules to fix usability things:

- Tweak, don’t redesign. Because a tweak costs less, requires less work, can be made sooner, is more likely to happen…

- Take something away. Often, the best way to fix a usability problem in a feature is just hide that feature. So, just remove it.

That’s all, folks. If you found this interesting, I’ll suggest you to read the whole book. It’s about one hundred pages and the author writes in a very easy-to-read way, so it can be read in a whole day. Cheers!


References:

Rocket surgery made easy, Steve Krug





Estamos hasta los huevos

21 05 2011

Se veía venir. Sólo era cuestión de tiempo. No podía ser que con toda la que está cayendo de paro, crisis económica, corrupción, mentiras y capitalismo brutal la gente no se echara a la calle. Ya estábamos tardando. Más vale tarde que nunca, ¿verdad? Un año después de que franceses e ingleses hicieran lo propio, ya era el turno de los jóvenes españoles.

Y no me extraña. Mañana hay elecciones y el panorama que se presenta es desolador: dos partidos que prometen preocuparse por los españoles pero que en cuanto te despistas ya están a la gresca el uno con el otro, como los hermanos cuando no les miran sus padres. No saben más que criticarse en lugar de ocuparse realmente de los problemas del país, de colaborar, de pensar en ideas… ¡pero cómo va a pensar esta gente, por el amor de Dios! Si precisamente se meten en política para no tener que pensar. Siempre fui de la opinión de que un buen profesional, del sector que sea, nunca se metería en política. Aquél arquitecto, abogado, constructor o empresario que se mete en política es porque no es buen arquitecto, abogado, constructor o empresario y busca la política como forma de ganarse las lentejas. Un inútil, vaya. Siempre hay excepciones, como el entrañable Punset, pero aquellos eran otros tiempos. Eran los albores de la democracia, la gente estaba ilusionada con algo tan bonito como el derecho, el poder de decidir su propio futuro, sus propios gobernantes. Sonaba como algo maravilloso.

Pero el ser humano es ruin y villano, y con el tiempo se corrompe. Volvemos a cometer los mismos errores, nuestro oscuro pasajero nos guía por la senda de la corrupción, la avaricia y el egoísmo, y nos dejamos llevar. Está en nuestra naturaleza, la historia nos demuestra una y otra vez que aprendemos a base de golpes, pero encima es que aprendemos poco y despacio. Cualquiera diría que somos una raza inteligente. Tampoco parece que hayamos avanzado mucho desde que nos dio por bajar de los árboles.

Y sin embargo, en ocasiones el ser humano puede ser realmente maravilloso. Me emociona ver lo que ha ocurrido esta semana de forma espontánea, apolítica y agnóstica en todas las ciudades españolas. No importan izquierdas ni derechas, no importan católicos o budistas, no importan madridistas o barcelonistas, carne o pescao, playa o montaña.

Se trata de democracia, señores. Democracia: esa pobre mujer ultrajada, maltratada, arrinconada en una esquina, con las manos cubriéndose los ojos. Ella es todo berrinche, todo vergüenza, es una puta violada.

Esto es lo que hay, amigos. El poder económico domina el mundo, los bancos manejan a los gobiernos cuales marionetas, y los gobiernos hacen lo propio con nosotros. Es decir, en la cadena alimenticia a nosotros nos da por culo todo Dios. Pero sí que es verdad que algo ha cambiado con respecto a otras épocas. Un detalle bastante importante que conviene tener muy en cuenta: ahora tenemos educación, tenemos cultura y tenemos más juicio. Ya no somos los pobres analfabetos a los que se puede engañar y manipular cuales corderos. Es por esto que ahora cuanto más presionas a una sociedad, antes o después se rebela.

Por eso la educación es fundamental, por eso es tan importante que la gente estudie y se enriquezca mentalmente. Ya no para que no te engañen en la tienda, como nos decían nuestros padres cuando éramos pequeños, sino para que no te engañen en la vida. Ésta es, señores, nuestra mejor arma. Y diré más: por eso es tan valioso Internet, y por eso es tan importante cuidarlo, mimarlo y mantenerlo por siempre como lo que es hoy en día: la anarquía absoluta, la libertad de contenidos, la información sin fronteras, la comunicación entre personas. Éste es nuestro nicho de poder, porque los banqueros y los poderosos no pueden controlarlo.

Así que mañana iremos a votar, porque amamos a nuestra ausente democracia; pero votaremos por un cambio, lucharemos por un cambio profundo en la sociedad. Pero un cambio de verdad. Un cambio de concepto, no de forma. Un cambio de raíz, no superficial. No es imposible, mirad lo que está ocurriendo en Oriente Medio. Tenemos el germen, sólo hay que dejarlo crecer y entonces se puede convertir en algo imparable, quién sabe. Quiero pensar que sí.

Señores políticos: no miren para otro lado con lo que ha pasado durante esta semana. Analicen el por qué y reflexionen. Es tiempo de que piensen en el pueblo. Es tiempo de que se preocupen por su gente, de que sean conscientes de la enorme responsabilidad que tienen entre manos. Pueden hacerlo mejor o peor, pero al menos sean honestos y honrados. Ya está bien de corrupción. Ya está bien de mentiras. Estamos hasta los huevos.





LOIC explained

30 04 2011

I read in a Spanish magazine several months ago an article related to hacking. Specifically, it talked about the coordinated attack that a group of hackers did to PayPal, in response to the Julian Assange’s (Wikileaks) issue, you know, clasified documents, FBI, gobernments, international reports and all the mess it caused.

The article mentioned a tool, LOIC (Low Orbit Ion Cannon), which was used by these hackers to perform a DOS (Denegation Of Service) attack against PayPal. That caught my attention, so when I went home I searched on the Internet to get more information about that tool, and, if possible, get the source code, just to get a better idea of what it does behind the scenes.

So the code is available at sourceforge (http://sourceforge.net/), and, surprisingly, it’s written in .NET (.NET 3.5 actually); that made me very happy. The next is a summary of what this tool does. It’s a very simple and small application, but powerful at the same time. Those of you that expect strange and advanced tricks will possibly get dissapointed. Well, hacking is not always that beautiful, arcane and magic.

The application

This application performs a DOS attack by sending a lot of packages to a certain server. This is an old trick, but if you coordinate the attack with dozens or hundreds of people, you can make that a server becomes crazy! As it usually happens with lots of hacking tools, this one can be used conveniently to test the number of requests that a server can process, testing the workload and simulating a stress situation, probably before deployment.

Two kinds of attack are available: HTTP flooding and TCP/UDP flooding; this way you can try with a generic type of server or with a web server.

Hands on the code

The code is pretty simple. I noticed that it’s written in .NET 3.5 because of some LinQ pieces of code. It doesn’t matter, the real stuff is the XXPFlooder, which performs the TCP/UDP flooding, and the HTTPFlooder, which implements the HTTP flooding. Both classes implement the IFlooder interface:

interface IFlooder
{
int Delay { get; set; }

bool IsFlooding { get; set; }

void Start();
}

Basically it defines a state (IsFlooding) and a method to start flooding. Now the real stuff:

Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) { Blocking = Resp };
socket.Connect(RHost);
try
{
    while (this.IsFlooding)
    {
        FloodCount++;
        socket.Send(buf);
        if (Delay > 0) System.Threading.Thread.Sleep(Delay);
    }
}
catch { }

This defines a TCP socket and, while the attack is beeing performed (IsFlooding; an event raised when the user interrupts the attack by clicking on a button determines the end condition to stop) LOIC sends a package to the server.

The UDP and HTTP flooding are quite similar to the TCP flooding, so I won’t post them.

Ok, pretty simple, isn’t it? Not very effective; well, it sends a package after other, but lots of servers can support such a stress situation. Let’s take a look at something more interesting:

var bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerAsync();

Ok, so the flooding is performed in a thread, launched in background. Sounds good. A thread which sends a package and then other packages until the user decides to stop it. But the trick here is that you can define several (let’s say, 100) of threads. Also the delay between each package that will be sent can be customized. So, you can perform a flooding attack sending 1000 packages using 1000 threads every 0,05 secs. simulteanously. THAT is fun. Now, think about dozens or hundreds of machines doing the same. THAT is gorgeous.

Finally the application contains a cool input validation, I’m pretty sure that you’ll find it very fun and the main form is created with WPF.

So, the code is pretty simple, basically it allows you to send packages to a server from different threads. By coordinating the attack with many people, it can become a serious problem. But we cool guys don’t do that. We use LOIC to test our production servers under stress situations before deploy a new application, right?

Enjoy ;-) .





Gestión de la memoria en .NET y garbage collector (II)

25 03 2011

En este artículo terminaremos de revisar algunos de los conceptos más importantes que atañen al GC en .NET: Finalización, Generaciones y manipulación del GC.

Finalización

Nótese que decimos finalizadores y no destructores, como en terminología C++. Esto se debe a que no se corresponde al mismo concepto, ya que el montículo se gestiona de una forma muy diferente y la finalización no es determinística.

Existen objetos que requieren de ciertas tareas de limpieza previamente a la liberación por parte del GC. Esto es cierto en contadas y muy concretas ocasiones, pues en la mayoría de los casos la liberación de un objeto en memoria es perfectamente posible sin intervención explícita por parte del usuario.

Para esto existen métodos de finalización como Finalize() y Dispose() (o Close()). No explicaré en detalle lo que hacen estos métodos; simplemente apuntaré que la diferencia entre el primero y el segundo es que no sabemos con certeza en qué momento se invocará un Finalize() (cuando el GC libere memoria), mientras que un Dispose() fuerza a liberar el recurso referido. Además, el uso de Dispose implica que el objeto referido debe implementar la interfaz IDisposable.

La diferencia entre Dispose() y Close() es más conceptual que funcional. Se suele utilizar Dispose  cuando el objeto no se va a utilizar más definitivamente. Un ejemplo muy común: los elementos gráficos tales como controles y formularios WinForms); mientras que un Close indica que el objeto puede volver a utilizarse pero de momento se libera. Un ejemplo serían las conexiones a bases de datos o las conexiones de red.

Existe una sentencia en C# muy útil que permite utilizar un recurso en un ámbito muy concreto y liberarlo al final. Ésta es la sentencia using:

using (MyResource res = new MyResource())
{
//uso del recurso
}

Cuando el recurso sale del ámbito de la sentencia using, automáticamente se invoca su método Dispose que libera el recurso. Esto implica que el objeto debe implementar la interfaz IDisposable, y por tanto implementar un método Dispose. La sentencia using equivale al uso de un try-finally como el siguiente:

MyResource res;
try
{
   res = new MyResource();
   //uso del recurso
}
finally
{
  res.Dispose();
}

Sin embargo, hay que anotar que aunque se invoque explícitamente el método Dispose de un objeto, éste puede no ser liberado inmediatamente. Como dijimos en el artículo anterior, la liberación de memoria en .NET no es determinística.

También existe un fenómeno curioso denominado “resurrección” y que consiste en que un objeto puede volver a memoria después de haberse recolectado. Para más información al respecto, les remito a las referencias que incluyo al final de este artículo.

Generaciones

En la práctica, los objetos se almacenan en el montículo según ciertas “categorías”. Estas categorías se basan en las siguientes premisas, que en la mayoríade los programas se cumplen:

  1. Cuanto más nuevo es un objeto, más corta será su vida.
  2. Cuanto más viejo es un objeto, más larga será su vida.
  3. Recolectar una porción del montículo es más rápido que recolectar todo el montículo.

Cuando se inicializa el montículo, todos los objetos que se van añadiendo a dicha estructura conforman lo que se conoce como generación 0. El CLR inicializa dicha generación con un tamaño determinado; digamos que ese tamaño es 256Kb.

Cuando el montículo se queda sin espacio para albergar más objetos, se ejecuta el GC. Los objetos que superan la criba del GC pasan a formar parte de la generación 1. Este sector (por llamarlo de alguna manera) del montículo tiene un tamaño más grande; digamos que de 2Mb.

Los nuevos objetos que se crean a partir de este momento se almacenan en la generación 0. Cuando la generación 0 se vuelve a llenar, el GC vuelve a eliminar lo que no es necesario y los objetos que sobreviven pasan a formar parte de la generación 1, quedando nuevamente la generación 0 vacía.

Llegará un momento en el cual la generación 1 también se llene y el GC tendrá que actuar nuevamente. Es interesante fijarse en que, puesto que esta generación tiene un espacio más grande, tardará más en llenarse y el GC tardará más en actuar aquí. Además, si suponemos la premisa 1 como cierta entonces liberaremos aquellos objetos nuevos transitorios rápidamente y eficientemente recorriendo secciones bien pequeñas.

Como decíamos, llegará un momento en que la generación 1 se llene. En ese momento el GC recorrerá la generación 1 y todos aquellos objetos que sobrevivan a dicha recolección pasarán a la generación 2, que será aún más grande. Y vuelta a empezar. Normalmente las implementaciones de montículo de .NET soportan hasta 3 generaciones: 0, 1 y 2, con lo cual aquí hemos terminado.

Como vemos, se cumplen las tres premisas, porque:

  1. Los nuevos objetos se almacenan en la generación 0, que es la más pequeña.
  2. Los objetos más viejos se almacenan en generaciones más grandes, que tardan más en llenarse y por tanto en limpiarse.
  3. Casi nunca se recorre todo el montículo, sino solamente sectores (generaciones). Solamente se recorre todo el montículo cuando se intenta instanciar en memoria un nuevo objeto y tanto la generación 0 como la generación 1 y la 2 están llenas, lo cual es realmente muy poco frecuente.

Manipulación del GC

Cuando dije que la liberación de recursos en .NET es automática no fui totalmente sincero. Es posible forzar una pasada del GC, pero esto solamente es conveniente realizarlo cuando sabemos a ciencia cierta que un conjunto de recursos costosos que se han estado utilizando no se van a utilizar más y por tanto deberían ser liberados. El método a utilizar para forzar al GC a liberar recursos es System.GC.Collect().

La clase System.GC es la que implementa el recolector de basura. Algunos métodos útiles son:

- GC.MaxGeneration(), que devuelve el numeral de la máxima generación en el GC soportada por el montículo.
- GC.WaitForPendingFinalizers(): Este método suspende el hilo hasta que se procesen los finalizadores pendientes.
- GC.GetGeneration(): Que permite manipular las distintas generaciones del GC.

Algunas sencillas prácticas que permiten sacar partido del GC

A pesar de que la gestión de la memoria es un proceso bastante automático en .NET, es muy conveniente entender cómo funciona entre bastidores porque sí que hay ciertas prácticas que permiten sacar provecho de su funcionamiento. Por ejemplo:

  • Limitar el uso de objetos estáticos, ya que constituyen raíces que no se pueden recolectar.
  • Utilizar la localidad de referencia en la medida de lo posible, es decir: declarar los objetos lo más cerca posible de su primer uso. Esto evitará la creación de objetos mucho antes de su uso.
  • Tratar de concentrar las operaciones sobre un objeto en una porción pequeña de código, pues el GC se basa en la premisa de que el uso de un objeto está concentrado en unas pocas líneas de código y después no se vuelve a utilizar (nuevamente, localidad de referencia).
  • Usar convenientemente Close() y Dispose() para liberar recursos como conexiones, buffers, streams, etc. que ya no se van a utilizar, invocándolos lo antes posible, una vez que los objetos no van a ser utilizados más.
  • A la hora de implementar un destructor, primero: plantéese si realmente es necesario hacerlo y segundo: no codifique pensando en el orden que van a seguir las operaciones a la hora de liberar recursos, recuerde: la función del GC es no determinística. Para los que vengan de un lenguaje C++, recuerden que el uso de destructores es mucho menos frecuente en .NET que en C++, sencillamente porque el manejo de la memoria es muy diferente. Básicamente es automático.

Esto es todo. Espero que esta serie de dos artículos les haya parecido útil o al menos interesante a muchos programadores de .NET.


Referencias:

Applied Microsoft .NET Framework programming, Jeffrey Richter
CLR via C#, Jeffrey Richter
Professional C#, Simon Robinson et al.





Gestión de la memoria en .NET y garbage collector

18 03 2011

La tecnología .NET consta de una gestión automática de memoria que permite liberar segmentos de memoria que no se utilizan. Este sistema de liberación de memoria se conoce como recolector de basura o garbage collector (en adelante GC), y a excepción de Java (y algún otro lenguaje más) esta gestión automática no se encuentra en otros lenguajes de programación. Esta gestión automática permite un uso mucho más eficiente y cómodo de los recursos por parte del usuario (programador).

Aunque existe el concepto de destructor en .NET y es posible tocar ciertos parámetros de la gestión de la memoria, no es recomendable hacerlo si no se sabe bien lo que se está haciendo; los algoritmos de gestión de memoria y  recolección de basura están suficientemente optimizados como para tener que tocar nada al respecto. Sin embargo sí que existen algunas buenas prácticas de programación que permiten aumentar la eficiencia del recolector de basura.

En este artículo veremos cómo .NET organiza la memoria de un programa y cómo trabaja el GC. En el siguiente artículo veremos el concepto de “generación” del GC, que permite promocionar objetos que se usan con más frecuencia por parte de la aplicación.

Fundamentos y necesidad

Todos los programas utilizan recursos de la máquina, sean éstos ficheros, búffers de memoria, espacio en pantalla, conexiones de red o de base de datos. Una vez que ya no se usan estos recursos, es necesario liberarlos o la aplicación puede, en algún momento dado, dejar sin recursos a la máquina para otros programas. La cuestión es saber cuándo no se van a usar más dichos recursos. En algunas ocasiones es relativamente sencillo, como por ejemplo una conexión
de red, de base de datos o un fichero: se crea la conexión en un bloque try y se libera en un bloque finally. O mejor aún: si está escribiendo código en C# utilice la sentencia using (veremos el uso de esta sentencia en el siguiente artículo).

Sin embargo, en otras ocasiones no es tan sencillo, como por ejemplo la gestión de la memoria.  ¿Cuándo deja de ser útil un objeto en memoria?

Montículo y pila

Aquellos que conozcan mínimamente la tecnología .NET sabrán que las “variables” en .NET tienen dos tipos fundamentales: valores tipo y valores referencia.

Los valores tipo son los que comunmente conocemos como tipos simples (int, char, long, byte…) y en memoria se almacenan en una estructura de pila.

Los valores referencia son los que comunmente conocemos como objetos (además de strings y arrays), que tienen cierta complejidad y en memoria se almacenan en una estructura de montículo.

El GC actúa sobre el montículo y no sobre la pila porque los segmentos almacenados en la pila son pequeños y de un tamaño controlado, mientras que en el montículo se almacenan bloques de memoria de distinta índole y tamaño.

Gestión básica de la memoria

Si representamos el montículo como una lista, los objetos se van almacenando en dicho montículo de forma lineal, existiendo un puntero, al que denominaremos NextObjPtr que apunta a la primera dirección libre de memoria reservada para la aplicación en ejecución.

Cada vez que se crea un nuevo objeto, se calcula su tamaño, se añade al montículo y se desplaza el puntero tanto como tamaño tenga el nuevo objeto. Es decir, toda la gestión de memoria se realiza mediante un puntero. Sencillo y muy eficiente, ¿verdad? ¿Cómo es que a nadie se le ha ocurrido antes? Bueno, es que esta aproximación tiene una pega importante, y es que está presuponiendo una cantidad de memoria infinita…

El recolector de basura (GC)

¿Qué ocurre en un entorno real en el que la memoria es limitada? Existe un algoritmo que permite determinar cuándo es necesario liberar memoria y qué es lo que hay que liberar. Esto es el GC.

¿Cómo determina el GC qué se puede liberar? Aquí entra en juego el concepto de raíz o referencia fuerte. Una raíz es: una variable gobal, un objeto estático, una variable local o un registro de CPU (aquí estamos hablando desde el punto de vista del lenguaje intermedio (IL) de .NET, no de un lenguaje de alto nivel). Una raíz no se puede eliminar de memoria, porque se entiende que se puede utilizar en cualquier momento.

Inicialmente, el GC asume que todo es basura y susceptible de ser recolectado. Enconces recorre el montículo buscando raíces. Estas raíces no se eliminarán en la próxima pasada del GC.

Con ello, tendremos un grafo con elementos directamente accesibles desde el código y otros que no. Ahora, el GC introduce el concepto de “alcanzable”, que son aquellos objetos accesibles a través de una raíz. Dichos objetos tampoco son eliminables. Se trata de variables que son accesibles desde el ámbito de uso de las raíces. Puesto que las raíces no son eliminables, los alcanzables tampoco.

Una vez completado este grafo, existirán objetos que no están apuntados por nadie. Éstos objetos son candidatos a ser eliminados en la próxima pasada del GC.
Esto ocurrirá cuando se detecte que la memoria se está acabando. Cuando esto suceda, los objetos que no se usan serán eliminados, los bloques de memoria utilizada serán compactados y se actualizará el NextObjPtr para apuntar a la nueva primera dirección de memoria libre para asignar.
Un detalle importante a tener en cuenta es que la eliminación de objetos no es determinística: un usuario no sabe realmente cuándo un elemento se va a eliminar; un objeto se eliminará… cuando se determine que no se va a usar más y cuando sea necesario liberar memoria.

En el próximo artículo explicaré los destructores (más propiamente llamados en terminología .NET finalizadores) y las generaciones del GC y cómo controlarlas desde un programa. También apuntaré algunas prácticas que pueden ayudar a sacar provecho al GC. Nos leemos.


Applied Microsoft .NET Framework programming, Jeffrey Richter
CLR via C#, Jeffrey Richter





Introducción a GIT

15 01 2011

GIT es un sistema distribuido de control de versiones, gratuito y que en los últimos años está incrementando su cuota de usuarios considerablemente. Para aquellos que hayan oído hablar alguna vez de este sistema y que estén considerando un posible cambio desde, presumiblemente, SVN, CVS o incluso Source Safe he escrito este pequeño artículo. Espero que lo encuentren útil.

GIT nace como una necesidad para el desarrollo del kernel de Linux, desarrollado inicialmente por el propio Linus Torvalds, que deseaba un sistema rápido, eficiente y distribuido. Así nace GIT como un conjunto de scripts escritos en perl y shell scripts, que posteriormente han ido siendo migrados a C.

GIT no es un sistema de control de versiones, sino algo más general: es más bien un sistema de ficheros. De hecho no constituye una única aplicación, sino un conjunto de herramientas (scripts).

Características internas del diseño de GIT

Quizá la característica más importante de GIT es que cuando se crea una nueva versión no se crean deltas de diferencias, sino un nuevo árbol. Cuando se diferencian dos versiones se traen ambos árboles y se comparan. Esto es eficiente porque solamente se almacenan los ficheros y directorios idénticos una sola vez, y se transfieren los contenidos de forma comprimida.

Favorece el uso de ramas y su posterior integración; es eficiente ya que la mayoría de las operaciones son locales (y casi instantáneas, por tanto), y los repositorios ocupan muy poco; además se transfiere solamente lo que ha cambiado: si dos ficheros tienen el mismo contenido, éste se transfiere una sola vez.

GIT consta de dos tipos de comandos: de porcelana, típicos de un usuario habitual, y de fontanería: comandos avanzados de bajo nivel. No vamos a entrar en la lista de comandos porque esto se puede encontrar muy bien explicado aquí, por ejemplo.

GIT maneja cuatro tipos de objetos fundamentales en la base de datos. Atención, chicos, que aquí viene el meollo de la cuestión; entender esto es prácticamente entender todo GIT, así que no se me distraigan:

BLOB: Contenidos de los ficheros. Solamente los contenidos, no los metadatos. Si hay dos ficheros con distinto nombre e igual contenido, solamente se almacena 1 blob.

TREE: Estructura de directorios. Contiene, para cada directorio, qué ficheros (blobs) y subtrees contiene. Es, por tanto, una estructura arbórea y recursiva.

COMMIT: Puntero a un árbol (del cual se hizo commit). Contiene autor, committer, message y parent commits que lo preceden, de manera que cada commit apunta al commit que lo precede.

TAG: Es algo así como el alias del commit, con información extra: objeto commit al que apunta, tipo, comentario… Este objeto es probablemente el de menor trascendencia de los cuatro explicados.

Podemos entender la estructura de un repositorio GIT como un grafo acíclico, en el cual un commit apunta a otros commits o trees y un tree apunta a otros trees o blobs. Y aquí viene un maravilloso boceto hecho con Paint que, aunque cutre a más no poder, aclarará la estructura:

Además de los objetos GIT, existe otro concepto que son las referencias GIT. Estos elementos (evitaré denominarlos objetos para con confundirlos con los primeros) no residen en la base de datos del repositorio y pueden cambiar, a diferencia de los objetos, que son inmutables. Esto significa que cada vez que se hace commit se crea un nuevo objeto commit que apunta a un nuevo tree, que apunta a los objetos blob apropiados.

Una rama en GIT es una referencia, un fichero en .git/refs/heads que apunta al último commit de dicha rama. El directorio .git está en la raíz del directorio de trabajo. Este directorio contiene subdirectorios muy interesantes:

  • Ramas: .git/refs/heads
  • Etiquetas: .git/refs/tags
  • Remotes: .git/refs/remotes
  • Objetos: .git/objects (blobs, trees y commits).
  • Configuración: .git/config
  • HEAD: .git/HEAD.
  • Eventos pre y post commit: .git/hooks

Los remotes son punteros a ramas de otros usuarios del mismo repositorio, posiblemente en otra máquina.

HEAD identifica la rama a la que se está apuntando en el instante actual.

La forma que tiene GIT de identificar objetos es mediante claves SHA-1. Puesto que estas claves constan de ristras de caracteres y números considerablemente largas, GIT permite referenciar dichas claves mediante prefijos, siempre y cuando dichos prefijos que identifican objetos sean identificados como únicos en la base de datos.

Vamos a dejarlo aquí, con la esperanza de que haya sido suficiente para que hayan entendido los fundamentos de GIT. Si tienen alguna consulta no duden en escribir. Podrán encontrar más información en la siguiente referencia:

GIT Internals, Source Code Control and beyond

Scott Chacon





Los números de 2010

2 01 2011

Los duendes de estadísticas de WordPress.com han analizado el desempeño de este blog en 2010 y te presentan un resumen de alto nivel de la salud de tu blog:

Healthy blog!

El Blog-Health-o-Meter™ indica: ¡Este blog está en fuego!.

Números crujientes

Imagen destacada

Un barco de contenedores puede llevar a unos 4.500 contenedores. Este blog fue visto cerca de 18,000 veces en 2010. Si cada cada visita fuera un contenedor, tu blog llenaria 4 barcos.

 

En 2010, publicaste 16 entradas nueva, haciendo crecer el arquivo para 113 entradas. Subiste 5 imágenes, ocupando un total de 585kb.

Tu día más ocupado del año fue el 24 de febrero con 155 visitas. La entrada más popular de ese día fue ¿De qué vive el Software Libre?.

¿De dónde vienen?

Los sitios de referencia más populares en 2010 fueran es.debugmodeon.com, search.conduit.com, google.es, meneame.net y espana.barrapunto.com.

Algunos visitantes buscan tu blog, sobre todo por malditos bastardos, moby dick, herman melville, quique san francisco y telecinco.

Lugares de interés en 2010

Estas son las entradas y páginas con más visitas en 2010.

1

¿De qué vive el Software Libre? febrero, 2010
1 comentario

2

Moby Dick enero, 2010
10 comentários

3

Configuración de RAID0 o RAID1 agosto, 2008
10 comentários

4

Senderismo en Babia septiembre, 2008
2 comentários

5

Guía rápida para usuarios de Crystal Reports (4ª parte) (reeditado) junio, 2008
3 comentários





Manejo de excepciones: Consejos

6 11 2010

En lenguajes de programación orientados a objetos (O-O) existe un mecanismo que permite que las aplicaciones manejen situaciones no esperadas de una forma apropiada; esto significa que la aplicación puede intentar recuperar el estado que tenía antes de darse dicha excepción en el mejor de los casos; en el peor, si la recuperación no es posible, abortar la operación o liberar recursos y cerrar la aplicación de forma ordenada. El manejo de excepciones constituye, pues, una potente herramienta para lograr robustez y estabilidad en nuestros programas.

A pesar de las ventajas de esta herramienta, no es cuestión de poner bloques try-catch-finally por doquier y ante cualquier situación; se trata de un asunto de diseño detallado decidir qué nivel de la aplicación es capaz de gestionar una excepción adecuadamente, y qué decisión tomar una vez que se ha dado una excepción: si es posible recuperarse o por el contrario hay que abortar.

En las siguientes líneas centraremos el discurso en el caso particular de .NET. Además, supondremos que el lector tiene conocimientos básicos sobre las excepciones: sintaxis y uso de las mismas a un nivel básico. A pesar de que el texto está centrado en .NET, lo que aquí se trata es válido para cualquier lenguaje de programación O-O, pues veremos aspectos de diseño detallado.

Jeffrey Richter, en el libro que abajo menciono, incluye un completo capítulo sobre Excepciones y su tratamiento, incluyendo una serie de interesantes consejos que, de tenerlos en cuenta, nos permitirán un uso inteligente y apropiado de las excepciones, sacando el máximo partido de las mismas.

En primer lugar, una excepción no sirve solamente para manejar errores y/o situaciones inesperadas. Una excepción debe ser lanzada cuando se detecta una situación en la cual un método no puede realizar su función adecuadamente. Es decir, es una buena idea utilizar excepciones como sustitutas de aquello de retornar códigos de error, mucho menos mantenibles y entendibles por quien lee el código. Si la operación terminó es que fue bien; si no, se lanza una excepción con un mensaje adecuado que el receptor debe saber tratar.

Además, en el caso de .NET, es conveniente evitar en la medida de lo posible System.Exception; en su lugar es mejor utilizar algo más específico, porque ello nos permite utilizar bloques catch específicos para tratar cada tipo de excepción de forma más concreta y controlada. Si la jerarquía de excepciones de System.Exception no se adecúa a tus intereses, crea la tuya propia, haciendo que hereden directamente de System.Exception. Diseña cuidadosamente dicha jerarquía pensando en quién va a usar tu código. Además, si vas a definir tus propias excepciones, es una buena idea hacerlas serializables para poderlas guardar en un log, base de datos, etc.

Valida los argumentos de tus métodos; utiliza el manejo de excepciones para implementar programación bajo contrato: si no se cumplen las precondiciones del método, lanza una excepción. De este modo es más probable que el método termine correctamente, una vez satisfechas las precondiciones. Puedes utilizar una ArgumentException o cualquier tipo hijo de éste, o crear tu propia excepción como se indicó más arriba.

Recuerda utilizar bloques finally para liberar recursos allí donde fuere necesario: cerrar buffers de lectura o escritura, conexiones a bases de datos, etc. El bloque finally es un mecanismo muy elegante para dejar el entorno estable, ocurra lo que ocurra en la ejecución del método. Una nota importante aquí es que evites introducir código que pueda lanzar una excepción dentro de un bloque finally, porque si salta una excepción en dicho bloque se enmascarará una posible excepción que nos llevó al bloque finally con la nueva y perderemos la información relativa a aquella excepción.

No captures todo siempre: un método debe capturar aquellas excepciones para las cuales esté preparado, no “tragarse” todo. Las excepciones deben tratarse en el nivel adecuado. Es decir: evita a toda costa utilizar bloques catch vacíos. Si el nivel actual no está preparado para manejar la excepción, relánzala con throw.

A tenor de lo indicado antes, intenta ser específico al capturar excepciones, distingue entre los tipos de excepciones que te puedan llegar y trátalos en consecuencia de forma específica, en distintos bloques catch.

Espero que hayáis encontrado útiles estos consejos. Os recomiendo el libro del que he extraido estas notas; tiene algunos capítulos realmente interesantes, como el mencionado de las excepciones y el del recolector de basura, del que es posible que dedique algunas líneas aquí próximamente. Además trata aspectos del CLR de .NET en profundidad, explicando cómo funcionan las cosas desde el núcleo del framework.

Hasta la próxima.


CLR via C#
Jeffrey Richter

 





Agile Retrospectives(IV): Actividades (II)

13 10 2010

Actividades para generar reflexiones

Brainstorming / filtering

Generar tantas ideas como sea posible; filtrar después aquellas que sean aplicables a la situación actual. Este filtro se puede evaluar mediante 4-8 valores, consensuados por el grupo, que cada idea debe cumplir.

Force field analysis

Dividir en grupos; cada grupo piensa en factores que pueden conducir a un cambio deseado. Después, lo mismo pero pensando en factores que pueden inhibir dicho cambio.

Five whys

En pequeños grupos, preguntarse entre sí 5 veces por qué. La idea es: dada una situación determinada,tratar de encontrar el porqué de esa situación (cómo se ha llegado a ella y qué ha conducido a dicha situación). A estilo de los niños de 5 años:

A. ¿Por qué…*1?

B. Porque… *2.

A. Y por qué… *2?

B. Porque… *3.

A. etc.

Se trata de llegar al fondo de la cuestión, comprender las causas de la situación actual, sea esta favorable o desfavorable. Algo muy importante que se debe extraer de esto: lecciones aprendidas.

Fishbone

Identificar factores problemáticos y sus causas. Analizar después qué cambios se pueden hacer para influir en dichos factores. Dibujar un gráfico como la espina dorsal de un pez, donde la cabeza es el problema y de la espina principal salen 5 espinas: qué, quién, cuándo, dónde y por qué. Categorías de problemas (ejemplo): métodos, máquinas, materiales, recursos humanos, lugar, procedimiento, gente, políticas, entorno, proveedores, sistemas, habilidades…

Patterns and shifts

Con el Timeline delante o el mad-sad-glad, identificar patrones en las distintas fases del sprint. Identificar puntos de inflexión y cambios. Extraer información de los datos obtenidos en la fase anterior.

Prioritize with dots

Priorizar ideas obtenidas, propuestas, sugerencias. Criterios: según importancia, resultados, retorno de la inversión, etc.

Report out with synthesis

Cada grupo resume sus actividades en el sprint pasado y lo presenta al resto. Al final, extraer conclusiones y decidir con qué se desea continuar en el nuevo sprint.

Identify themes

Tras realizar la actividad locate strenghts, formar grupos e identificar lo que se ha aprendido de la persona entrevistada. Extraer conclusiones y recopilar ideas.

Learning matrix

Los miembros analizan 4 perspectivas en los datos extraídos y realizan una tormenta de ideas para decidir una lista de temas rápidamente. Categorías: ¿Qué hicimos bien y queremos que continúe? ¿Qué queremos cambiar? ¿Nuevas ideas? ¿Agradecimientos?

Actividades para decidir qué hacer

Retrospective planning game

Cada miembro, por su cuenta o por parejas, anota las tareas necesarias para completar un expermiento, una mejora o una recomendación. Poner en común, eliminando redundancias y rellenando huecos; a continuación se asigna a cada miembro algunas de las tareas identificadas.

Smart goals

Centrarse en desarrollar metas que sean Specific, Measurable, Attainable, Relevant y Timely. Los objetivos que tienen estas características deben tener mayor prioridad porque son más fáciles de cumplir.

Circle of questions

Los miembros se encuentran sentados en un círculo. El jefe de proyecto introduce la discusión preguntando a alguien: ¿para tí, que es lo que más prioridad tiene en el próximo sprint? Éste contesta, y al final de su turno, formula una pregunta que responderá quien esté a su izquierda, quien a su vez, responderá y desarrollará el tema o cambiará a otro. Rotar el círculo al menos 2 veces.

Short subjects

Brainstorm de acciones a llevar a cabo, según los siguientes temas (a elegir, porque todos proponen la misma dicotomía de categorías):

  • Qué fue bien / Qué cambiar en el siguiente sprint.
  • Mantener / Quitar / Añadir
  • Dejar de hacer / Empezar a hacer / Seguir haciendo
  • Empezar / Parar / Continuar
  • … (aquí cabe el mad-sad-glad descrito en esta serie).

Actividades para cerrar el retorspective

+ / Delta

Identificar fuerzas (cosas que se desea mantener, identificadas con ‘+’) y  cambios (deltas) para el próximo sprint. Dibujar una tabla comparativa con ambas listas de elementos.

Appreciations

Entre los miembros del equipo, agradecerse los unos a los otros por la ayuda prestada durante el sprint.

Temperature reading

Cada miembro expresa lo que ocurre en el equipo en su opinión y lo que quieren para el equipo.

Helped, hindered, hypothesis

El gestor obtiene feedback de los miembros mediante la expresión de estos últimos de qué les ayudó durante la reunión, qué les dificultó y qué les gustaría hacer en el siguiente retrospective. Así haremos mejores retrospectives.

Return on time invested (ROTI)

Los miembros valoran en una escala de 0 a 4 el tiempo invertido durante el retrospective.

Y con esto…

Cerramos la serie. Espero que hayan aprendido algo a pesar de que todo está muy resumido. Hasta la próxima.


Referencias:

 

Agile Retrospectives – Making good teams great

Esther Derby, Diana Larsen

Serie:

Agile Retrospectives: cómo mejorar

Agile Retrospectives (II): Fases

Agile Retrospectives (III): Actividades (I)

Agile Retrospectives(IV): Actividades (II)








Seguir

Get every new post delivered to your Inbox.