m2eclipse: un plugin de Eclipse para proyectos de Maven

m2eclipse es un plugin de Eclipse para la integración con Maven. Permite usarlo para manejar, proyectos simples, multi-módulo, ejecutar builds de Maven utilizando la interfaz de Eclipse e interactuar con repositorios de Maven. El plugin ya está bastante maduro, y resulta muy cómodo para trabajar con este tipo de proyectos.

La instalación es bastante simple. Tenemos que agregar el update site, que es http://m2eclipse.sonatype.org/sites/m2e. Hay una única opción para instalar, llamada “Maven Integration for Eclipse (Required)“. Se instalará el core, el editor de POMs y la integración al repositorio de Maven.

En el sitio oficial hay links a videos bastante descriptivos acerca del proceso de instalación, creación de proyectos, etc.

m2eclipse trae un Maven embebido. Si ya tenemos una instalación de Maven configurada y un repositorio local con artefactos descargados podemos aprovecharla, configurando m2eclipse para que haga uso de ella. Para agregar instalaciones de Maven a m2clipse, debemos acceder al menú de preferencias de Eclipse, agregar la instalación seleccionando el directorio en el cual está ubicado y marcarla como la instalación activa:
Configurar instalaciones de maven

El plugin cuenta con bastante documentación. Es posible descargar la referencia de m2eclipse en PDF desde éste link (es necesario llenar algunos datos) o leer la documentación on-line aquí. Tiene una FAQ bastante completa, y el blog de m2eclipse es éste.

La página principal del plug-in es http://m2eclipse.sonatype.org/index.html

Tests parametrizados con TestNG

En el post anterior vimos cómo podíamos implementar tests parametrizados utilizando JUnit 4. TestNG es un framework similar a JUnit, compartiendo entre ellos casi la totalidad de sus features.

TestNG propone un enfoque un poco diferente a la implementación de tests parametrizados. Me pareció más flexible y prolija, por lo que creo que vale la pena continuar el post anterior con el ejemplo implementado utilizando este framework.

En TestNG, los pasos que debemos seguir para implementar un test parametrizado son los siguientes:

  • Escribir un método anotado con @DataProvider(name = “[nombre]”), donde [nombre] es el nombre por el cual se referenciará a este DataProvider en los métodos de test. Si los juegos de datos tienen más de una variable, deberá retornar un array
    multidimensional. Si es necesario -generalmente lo es-, podría contener además el resultado esperado para cada juego de datos de entrada.
  • Escribir un método anotado con @Test(dataProvider = “[nombre]”), que reciba tantos parámetros como cantidad de datos se tenga en cada item del array que retorna el método anotado con @DataProvider.

No hay necesidad de aislar los tests parametrizados de los tests que no lo son. Se puede inclusive tener varios DataProviders en una misma clase, asociando métodos de tests diferentes a cada uno.

Veamos un ejemplo de la case de test utilizando TestNG (el proyecto está disponible para bajar aquí):

<br />
package com.josearrarte;</p>
<p>import static org.junit.Assert.assertEquals;</p>
<p>import org.testng.annotations.DataProvider;<br />
import org.testng.annotations.Test;</p>
<p>public class ParametrizedTestNGTest {</p>
<p>	@DataProvider(name=&quot;test1&quot;)<br />
	public<br />
Object[][] createData1() {<br />
		return new Object[][] {<br />
				{ DateFormatProvider.OPERATION_TYPE_1, &quot;MMYY&quot; },<br />
				{ DateFormatProvider.OPERATION_TYPE_2, &quot;YYMM&quot; },<br />
				{ DateFormatProvider.OPERATION_TYPE_3, &quot;YYYYMM&quot; } };<br />
	}</p>
<p>	@Test(dataProvider=&quot;test1&quot;)<br />
	public void verifyDateFormats(int operationType, String dateFormat)<br />
		throws Exception {<br />
		DateFormatProvider provider = new DateFormatProvider();</p>
<p>		String result = provider.getDateFormatFor(operationType);<br />
		assertEquals(dateFormat, result);<br />
	}</p>
<p>	@Test(expectedExceptions=IllegalArgumentException.class)<br />
	public void verifyDateFormats_ShouldThrowIllegalArgumentException()<br />
		throws Exception {<br />
		DateFormatProvider provider = new DateFormatProvider();</p>
<p>		provider.getDateFormatFor(-1);<br />
	}<br />
}<br />

Como vemos, la idea es exactamente la misma que en JUnit, pero está mejor implementada. Los tests parametrizados se diferencian mejor, y no es necesario tener miembros de la clase y constructores
comunes para almacenar los datos.

Además, el hecho de separar la creación de los tests (instanciación) con la generación de los juegos de datos (data providers) nos permite tener en una misma clase tests con y sin parámetros.

Tests parametrizados con JUnit 4

Una de las nuevas características de JUnit 4 es la posibilidad de correr tests parametrizados. Los tests parametrizados son una forma de escribir un test genérico y poder correrlo con juegos de datos diferentes.

Creando tests parametrizados

Para crear un test parametrizado con JUnit, necesitamos seguir los siguientes pasos:

  • Crear una clase de test anotada con @RunWith(Parameterized.class). El nombre completo del runner es org.junit.runners.Parameterized.
  • Agregar miembros de la clase que representen los parámetros de los tests.  Estos miembros no necesitan tener getters y setters, y pueden ser miembros privados.
  • Crear un constructor común de la clase, que reciba parámetros del tipo de los miembros y les asigne sus valores.
  • Crear un método estático que retorne una java.util.Collection.  Este
    método deberá estar anotado con @Parameters, y será el que define el juego de datos a utilizar.  Si los juegos de datos tienen más de una variable, deberá retornar una colección de arrays. Si es necesario -generalmente lo es-, podría contener además el resultado esperado para cada juego de datos de entrada.
  • Escribir un método anotado con @Test, en el cual se usen los miembros de la clase antes definidos.

Ejecución de tests parametrizados

La ejecución de un test parametrizado se realiza de forma idéntica a un test normal.

Al ejecutar el test, JUnit invocará al método anotado con @Parameters, construirá la clase de test tantas veces como juegos de datos haya, invocando cada vez al constructor con un juego diferente de datos. En cada una de esas instancias del tests ejecutará el test de forma normal, invocando -si existen- a los métodos anotados con @BeforeClass, @AfterClass, @Before, @After y al test en si. En resumen, la ejecución será:

  • @Parameters
  • @BeforeClass
  • @Before
  • @Test (juego de datos 1)
  • @After
  • @Before
  • @Test (juego de datos 2)
  • @After
  • @Before
  • @Test (juego de datos N)
  • @After
  • @AfterClass

Ejemplo

A modo de ejemplo imaginemos que tenemos una clase com.josearrarte.DateFormatProvider con un método getDateFormatFor(int operationType) y que, dependiendo del valor del operationType recibido, retorna un java.lang.Stringcon diferentes formatos de fecha (“MMYY”, “YYMM” o “MMYYYY”).

El código completo del ejemplo está disponible para bajar aquí.

<br />
package com.josearrarte;</p>
<p>public class DateFormatProvider {</p>
<p>	public<br />
static final int OPERATION_TYPE_1 = 1;</p>
<p>	public static final int OPERATION_TYPE_2 = 2;</p>
<p>	public static final int OPERATION_TYPE_3 = 3;</p>
<p>	public String getDateFormatFor(int operationType)<br />
		throws IllegalArgumentException {</p>
<p>		switch (operationType) {<br />
			case OPERATION_TYPE_1: {<br />
				return &quot;MMYY&quot;;<br />
			}<br />
			case OPERATION_TYPE_2: {<br />
				return &quot;YYMM&quot;;<br />
			}<br />
			case OPERATION_TYPE_3: {<br />
				return &quot;YYYYMM&quot;;<br />
			}<br />
			default: {<br />
				throw new IllegalArgumentException(&quot;Unknown operation type&quot;);<br />
			}<br />
		}<br />
	}<br />
}<br />

Podríamos generar una serie de tests parametrizados para los flujos básicos de ejecución y otro test para le caso que operationType no sea 1, 2 o 3. La clase de test podría ser como la siguiente;

<br />
package com.josearrarte;</p>
<p>import static org.junit.Assert.assertEquals;</p>
<p>import java.util.Arrays;<br />
import java.util.Collection;</p>
<p>import org.junit.Test;<br />
import org.junit.runner.RunWith;<br />
import org.junit.runners.<br />
Parameterized;<br />
import org.junit.runners.Parameterized.Parameters;</p>
<p>@RunWith(Parameterized.class)<br />
public class ParametrizedJunitTest {</p>
<p>	private int operationType;<br />
	private String dateFormat;</p>
<p>	public ParametrizedJunitTest(int operationType, String dateFormat) {<br />
		super();<br />
		this.operationType = operationType;<br />
		this.dateFormat = dateFormat;<br />
	}</p>
<p>	@Parameters<br />
	public static Collection operationTypeValues() {</p>
<p>		return Arrays.asList(new Object[][] {<br />
				{ DateFormatProvider.OPERATION_TYPE_1, &quot;MMYY&quot; },<br />
				{ DateFormatProvider.OPERATION_TYPE_2, &quot;YYMM&quot; },<br />
				{ DateFormatProvider.OPERATION_TYPE_3, &quot;YYYYMM&quot; }<br />
		});<br />
	}</p>
<p>	@Test<br />
	public void verifyDateFormats() throws Exception {<br />
		DateFormatProvider provider = new DateFormatProvider();</p>
<p>		String result = provider.getDateFormatFor(operationType);<br />
		assertEquals(dateFormat, result);<br />
	}</p>
<p>	@Test(expected=IllegalArgumentException.class)<br />
	public void verifyDateFormats_<br />
ShouldThrowIllegalArgumentException()<br />
		throws Exception {</p>
<p>		DateFormatProvider provider = new DateFormatProvider();</p>
<p>		provider.getDateFormatFor(-1);<br />
	}<br />
}<br />

Dejo planteado un problema que no supe resolver con JUnit. Si escribo un test parametrizado y otro que no es parametrizado en una misma clase, JUnit correrá a ambos como si fueran parametrizados, tantas veces como el tamaño de la colección de los datos. No encontré forma de configurar JUnit para que uno de los métodos (verifyDateFormats_ShouldThrowIllegalArgumentException() en el ejemplo) se ejecute como un test común y corriente. En éste post muestro cómo se puede resolver este tema utilizando TestNG en lugar de JUnit 4.

En resumen

Los tests parametrizados nos simplifican la tarea de escribir varios tests cuando su implementación es similar entre ellas.
Generalmente aporta claridad al código de la clase de test, ya que no se escribe tanto código repetido.

Sin embargo, si utilizamos JUnit debemos escribir los tests parametrizados en una clase separada de los tests comunes, ya que no es posible decirle a JUnit la forma de ejecutar un método de test y otro. Si no queremos tener este problema podemos utilizar TestNG, otro framework de testing unitario. En éste post muestro detalles de cómo hacerlo.

Quinta edición del CSS Naked Day

Este 9 de abril y por quinto año consecutivo se celebra el CSS Naked Day. La idea detrás de este evento es promocionar los estándares web. Durante 48 horas (tiempo durante el cual es 9 de abril en algún lugar del mundo) los interesados en apoyar la iniciativa “desnudan” sus sitios mostrándolos sin CSS.

Según la página oficial del CSS Naked Day

La idea detrás del CSS Naked Day (Día de CSS Desnudo) es promover los estándares web.
Esto incluye uso correcto de (X)HTML, markup semántico, una buena estructura jerárquica y por supuesto, un juego de palabras. Es hora de mostrar tu cuerpo ()

Para apoyar esta iniciativa, instalé este plugin de WordPress que automatiza esto de sacar los CSS del blog. El plugin provee una función PHP is_naked_day() para facilitar las cosas, pero le modifiqué la
implementación según el código de la página oficial:

<br />
function is_naked_day() {<br />
  $start = date('U', mktime(-12, 0, 0, 04, 9, date('Y')));<br />
  $end = date('U', mktime(36, 0, 0, 04, 9, date('Y')));<br />
  $z = date('Z') * -1;<br />
  $now = time() + $z;<br />
  if ( $now &gt;= $start &amp;&amp; $now &lt;= $end ) {<br />
    return true;<br />
  }<br />
  return false;<br />
}<br />

Saludos, y todos a disfrutar de la desnudez.

To RAID or not to RAID? Instalando un RAID-0 en mi PC

Introducción

Para mi cumpleaños me regalé un disco igual al que tiene mi computadora de escritorio, para dejarlos en configuración RAID-0, y de esta forma poder mejorar la performance de la computadora, atacando el que considero el cuello de botella más significativo en esta PC.

Antes de instalar todo y comprobar si la PC mejoró su rendimiento, me interesé en hacer algunas mediciones para no quedarme sólo con una sensación y poder comparar en cifras la performance del disco que ya tenía, la del que compré estos últimos días y la de ambos funcionando juntos. Además, me interesaba conocer la diferencia de rendimiento del RAID controlados por software (con Windows XP) y por hardware.

¿Qué es RAID?

RAID es un acrónimo en inglés que significa Redundant Array of Independent Disks; hace referencia a un sistema de almacenamiento que usa múltiples discos duros entre los que distribuye o replica los datos. Dependiendo de su
configuración (llamados niveles), los beneficios de un RAID respecto a un único disco son uno o varios de los siguientes: mayor integridad, mayor tolerancia a fallos, mayor throughput (rendimiento) y mayor capacidad.

Se necesita un controlador RAID para manejar la forma de guardar la información que se lee y escribe en cada disco. Este controlador puede ser un dispositivo de hardware (ya sea como parte integral del chipset del motherboard o como un dispositivo físico dedicado a dicha función) o puede estar implementado por software, como parte del sistema operativo.

¿Qué es RAID-0?

El RAID de nivel 0 es también conocido como data striping, y no tiene nada de redundant. La idea es dividir los datos de forma homogénea en cada uno de los volúmenes. Como consecuencia de esta división de los datos, se mejora el rendimiento tanto de escritura como de lectura, dado que los pedidos que se realizan a los dispositivos se pueden paralelizar y atender simultáneamente. En
el mejor de los casos, la performance crecerá de forma lineal a la cantidad de discos del RAID.

(Fuente: Wikipedia)

Esta configuración no es redundante ni tolerante a fallos, ya que los datos no están replicados entre los discos, ni se utilizan algoritmos de corrección o detección de errores. Esto quiere decir que una falla del RAID nivel cero ocurrirá cuando cualquiera de los discos duros falle (y la probabilidad de que esto suceda será mayor cuanto más discos tenga el RAID). Esta medida en horas se llama MTBF, mean time between failures.

La capacidad total del RAID-0 será la capacidad del disco menor,
multiplicado por la cantidad de discos. Si los discos son de tamaños diferentes, quedará espacio inutilizado los discos más grandes.

¿Qué es RAID-1?

El RAID de nivel 1 es también conocido como data mirroring. En esta configuración, cada disco actúa como una copia exacta (espejo) de los datos. El rendimiento de lectura se ve incrementado porque, como cada disco tiene una copia de los datos, la lectura se puede paralelizar. Sin embargo, la escritura será tan lenta como el más lento de los discos (esto es así porque se necesita tener toda la información replicada en cada uno de ellos).

(Fuente: Wikipedia)

La probabilidad de falla
de esta configuración disminuye cuanto más discos se tengan. Para que se pierda la información, deberán fallar todos y cada uno de los discos.

La capacidad total del RAID será igual a la capacidad del menor de los discos. Al igual que con un RAID-0, si los discos son de tamaños diferentes, quedará espacio sin utilizar en los discos de mayor tamaño.

Otros tipos de RAID

Hice una descripción más detallada de RAID-0 y RAID-1 porque son las opciones que estoy considerando para mi computadora. Sin embargo, configuraciones como RAID-5, RAID-6 o RAID-10 son mucho más utilizadas en servidores. Existe mucha información en Internet sobre estos y otros tipos de RAID, pero de todas formas repasemos algunas de sus características:

RAID-5

RAID-5 necesita por lo menos 3 discos para funcionar. En lugar de espejar todos los datos como hace RAID-1, RAID-5 trabaja con bloques (stripes) de datos, dividiendo la información entre N-1 discos, y utilizando el último disco para guardar información de
paridad del stripe. Esta información de paridad puede ser utilizada si se ocasiona un error de lectura, siendo posible reconstruir la información del bloque que sufrió el fallo. La infomación de paridad no se guarda en un único disco, sino que se alterna para cada bloque. Esto es debido a que en cada escritura, es necesario recalcular y escribir la información de paridad. Si esta información estuviera concentrada en sólo un disco para todos los bloques, sería necesario escribir en el mismo cada vez que se escribe un bloque, ocasionando un cuello de botella.

(Fuente: Wikipedia)

Una configuración de RAID-5 puede funcionar con un disco
fallido. Si fallan 2 o más, se pierden datos.

La capacidad de un RAID-5 es (N-1)xT, en donde N es la cantidad discos, y T es el tamaño del menor de los discos.

El rendimiento de lectura es similar a un RAID-0 para (N-1) discos. El rendimiento de escritura es pobre, especialmente si son escrituras no secuenciales y de tamaños menores que un stripe, ya que para cada escritura es necesario recalcular y escribir la infomación de paridad.

RAID-6

RAID-6 es similar a RAID-5, con la diferencia que usa dos discos para almacenar la información de paridad de cada stripe.

(Fuente: Wikipedia)

RAID-6 puede operar con fallas en 2 discos.

Otra ventaja con
respecto a RAID-5 es que el sistema no queda tan vulnerabla ante la falla de un segundo disco. Con discos duros cada vez más grandes, el tiempo de rearmado de un RAID que falló es también cada vez mayor, y por ende, también se agranda la ventana de tiempo en la cual al sistema tiene un punto único de falla.

El rendimiento de un RAID-6 puede ser similar a un RAID-5, dependiendo mucho de la implementación del cálculo de paridad de los stripes.

La capacidad de un RAID-6 es (N-2)xT, en donde N es la cantidad discos, y T es el tamaño del menor de los discos.

RAID-10 (llamado también RAID 1+0)

Un RAID-10 es RAID-0 de varios RAID-1. Es decir, se implementan varios RAID-1, y se utilizan funcionando como RAID-0.

(Fuente: Wikipedia)

Un RAID-10 podría seguir funcionando siempre y cuando al menos un disco de cada RAID-1 funcione.

En la mayoría de los casos, la performance de un RAID-10 es la más rápida de todas, siendo superada solamente por un RAID-0. Es la configuración elegida para escenarios de alta concurrencia de acceso a los discos como ser bases de datos, servidores de mail, etc.

Configuración, pruebas y resultados

Vamos a la práctica. La idea, como comentaba al principio, es hacer unas pruebas antes y después de configurar un RAID en mi computadora personal.

Configuración

Para configurar el RAID por hardware en mi computadora fue necesario hacer unos cambios en el BIOS. Más concretamente, cambié el parámetro Onboard SATA Type de “SATA disk” a “RAID controller“. En mi BIOS, está ubicado en Onboard device configuration ? South Onchip PCI device ? Onboard SATA Type.

nLuego de este cambio, al iniciar la PC y antes de bootear desde la lista de dispositivos configurados (CD, USB, discos duros, etc.) se presenta por un instante la posibilidad de apretar las teclas Ctrl+F, y acceder a FastBuild, un aplicativo para configurar distintos tipos de RAID, asignarle discos físicos, etc.

FastBuild

Por supuesto que esto será diferente para cada marca y modelo de motherboard, por lo que esto es simplemente una referencia.

Plan de pruebas

Hay infinidad de sitios que hacen pruebas de performance mucho más profesionales que éstas (por ejemplo Tom’s Hardware, PC perspective, Anantech, entre otros). De todas formas, me interesaba ejecutar las siguientes pruebas:

  • duplicar un archivo grande
  • duplicar un directorio con muchos archivos pequeños
  • descomprimir un archivo grande
  • ejecutar el benchmark de h2benchw
  • ejecutar el benchmark de IOMeter

Las especificaciones de mi PC relevantes a este post son las siguientes:

En este post detallaré los resultados de un RAID-0 por hardware, utilizando el chipset de mi motherboard en lugar del sistema operativo. Quizá más adelante pruebe con otras configuraciones de RAID por software o con otros niveles, en cuyo caso, complementaré la información con otro post.

Repetí las pruebas por lo menos 2 veces cada una para asegurarme que los resultados obtenidos estuvieran bien tomados (no fueran outliers).

Resultados

Para medir los tiempos de copia utilicé el programa TeraCopy, un sustituto a la copia de Windows muy recomendable.

Para la prueba de descomprimir un archivo utilicé el programa 7-Zip, también recomendable por tener la posiblidad de utilizar el excelente formato de compresión 7z, pero con la usabilidad un poco recortada si lo comparamos con WinRAR o WinZip.

Duplicar un archivo grande

Para esta prueba utilizamos un ISO de Ubuntu (más específicamente ubuntu-9.04-desktop-i386.iso, con MD5=66FA77789C7B8FF63130E5D5A272D67B) que pesa 698Mb.

Los resultados fueron los siguientes:

Duplicar un archivo grande - disco viejo

Duplicar un archivo grande - disco nuevo

Duplicar un archivo grande - RAID-0

El disco nuevo fue muy bueno duplicando el archivo grande, igual de bueno que el RAID-0. La diferencia con respecto al disco viejo es muy apreciable.

Duplicar un directorio con muchos archivos pequeños

Para esta prueba utilicé un directorio con más de 35.000 archivos, unos 1.500 directorios y una distribución de tamaños como se detalla en la siguiente figura:

Distribución
de tamaño de archivos

La duplicación del directorio terminó en 6’47” en el disco viejo, en 4’39” en el disco nuevo y en 4’15” en el RAID-0:

Duplicar muchos archivos pequeños - disco viejo

Duplicar muchos archivos pequeños - disco nuevo

Duplicar muchos archivos pequeños - RAID-0

Al igual que para el test anterior, el disco nuevo se comportó muy bien, sólo un poco más lento que la configuración en RAID. El disco viejo nuevamente quedó atrás.

Descompresión de un archivo comprimido

El archivo comprimido que utilicé contiene los más de 35.000 archivos y los 1.500 directorios utilizados en la prueba
anterior. El archivo ocupa 18.9Mb, y se expande a 319Mb luego de descomprimido.

Los resultados intermedios del proceso de descrompresión fueron los siguientes:

Descompresión de un archivo comprimido - disco viejo

Descompresión de un archivo comprimido - disco nuevo

Descompresión de un archivo comprimido - RAID-0

h2benchw

Para esta prueba ejecuté h2benchw con los siguientes parámetros:

h2benchw -english -s -p [0|1]

En donde:

  • -english: configura el idioma
  • -s: activa la medición de tiemops de acceso (seek time)
  • -p: activa la medición de perfiles de aplicación (swapping, installing, Word, Photoshop, copying y F-Prot)
  • 0 o 1: es el disco que quiero testear. En el caso del RAID-0, Windows ve un único volumen.

A continuación están los resultados para de cada volumen.

Disco viejo

Capacity: 
312576705 sectors=152625 MByte, CHS=(19457/255/63)
Checking timer for 10 seconds (Win32) ............. Ok.
timer resolution: 0.000 µs, 2399.960 MHz
timer statistics: 27237051 calls, min 0.15 µs, average 0.15 µs, max 70.00 µs
Reading some sectors to warm up... done.
Measuring random access time (whole disk):
reading... 13.78 ms  (min. 4.43 ms, max. 38.71 ms)
random access time in lower 504 MByte
reading... 7.30 ms  (min. 0.10 ms, max. 12.67 ms)
Running application profile `swapping' ...13158.4 KByte/s
Running application profile `installing' ...15769.4 KByte/s
Running application profile `Word' ...22180.3 KByte/s
Running application profile `Photoshop' ...18933.4 KByte/s
Running application profile `copying' ...28393.2 KByte/s
Running application profile `F-Prot' ...10606.0 KByte/s
Result: application index = 16.8
!!! WARNING: application profiles 
inaccurate since measured read-only

Disco nuevo

Capacity: 312576705 sectors=152625 MByte, CHS=(19457/255/63)
Checking timer for 10 seconds (Win32) ............. Ok.
timer resolution: 0.000 µs, 2399.960 MHz
timer statistics: 27513991 calls, min 0.14 µs, average 0.15 µs, max 148.74 µs
Reading some sectors to warm up... done.
Measuring random access time (whole disk):
reading... 13.17 ms  (min. 3.02 ms, max. 27.00 ms)
random access time in lower 504 MByte
reading... 6.72 ms  (min. -0.96 ms, max. 20.66 ms)
Running application profile `swapping' ...14424.6 KByte/s
Running application profile `installing' ...16695.8 KByte/s
Running application profile `Word' ...25281.3 KByte/s
Running application profile `Photoshop' ...23506.0 KByte/s
Running application profile `copying' ...35727.1 KByte/s
Running application profile `F-Prot' ...11728.3 KByte/s
Result: application index = 19.3

RAID-0

Capacity: 621088965 sectors=303266 MByte, CHS=(38661/255/63)
Checking timer for 10 seconds (Win32) ............. Ok.
timer resolution: 0.279 µs, 3.580 MHz
timer statistics: 3472778 calls, min 1.12 µs, average 1.40 µs, max 87.16 µs
Reading some sectors to warm up... done.
Measuring random access time (whole disk):
reading... 13.26 ms  (min. 3.22 ms, max. 33.53 ms)
random access time in lower 504 MByte
reading... 6.62 ms  (min. 0.12 ms, max. 15.13 ms)
Running application profile `swapping' ...14135.7 KByte/s
Running application profile `installing' ...16908.4 KByte/s
Running application profile `Word' ...29537.2 KByte/s
Running application profile `Photoshop' ...26825.3 KByte/s
Running application profile `copying' ...35556.3 KByte/s
Running application profile `F-Prot' ...
10469.2 KByte/s
Result: application index = 19.8

Al igual que en los tests anteriores, la configuración en RAID es la mejor de todas las opciones, siendo seguida de cerca por el disco nuevo.

IOMeter

IOMeter es  una herramienta para medición de performance de systemas de I/O (discos, red, etc.). Funciona ejecutando un profile predefinido, que determina el tipo de acceso al disco (escrituras, lecturas, secuenciales o aleatorias). El perfil utilizado está disponible para descargar haciendo click aquí. Esta prueba se ejecuta haciendo un 67% de pedidos de lectura, 33% de pedidos de escritura, repartidas de forma aleatoria en todo el espacio del disco duro. Además, cada pedido de lectura o de escritura es de a bloques de 4Kbytes. Se hacen 4 ejecuciones de 5 minutos cada una, con 1, 2, 4 y 8 pedidos de I/O en la cola de
invocaciones (outstanding queue depth) respectivamente.

Datos de la ejecución del benchmark en el disco viejo:

Queue depthRead IO/sWrite IO/s% CPUMBpsRead MBpsWrite MBps
166,532,50,310,380,260,13
266,832,10,380,390,260,13
467,733,70,310,400,260,13
874,636,70,470,430,290,14

Datos de la ejecución del benchmark en el disco nuevo:

Queue depthRead IO/sWrite IO/s% CPUMBpsRead MBpsWrite MBps
179,438,90,550,460,310,15
279,138,50,420,460,310,15
480,240,10,460,470,310,16
888,142,50,430,510,340,17

Datos de la ejecución del benchmark en el RAID-0:

Queue depthRead IO/sWrite IO/s% CPUMBpsRead MBpsWrite MBps
1248,8123,71,421,450,970,48
2290,8142,22,441,691,140,56
4306,2151,21,461,791,200,59
8308,8153,11,541,801,210,60

Mejoras del RAID-0 con respecto a los discos separados:

Disco viejoDisco nuevoRAID-0 vs viejoRAID-0 vs nuevo
Read IO/s100%119%419%353%
Write IO/s100%119%423%356%
% CPU100%127%468%370%
MBps100%119%421%354%
Read MBps100%119%421%353%
Write MBps100%119%423%356%

Los resultados obtenidos fueron un poco extraños, ya que el RAID-0 dio resultados mucho mejores (3 o 4 veces mejores) que cada uno de los discos por separado. No tengo claro si esto es normal y está bien, o las medidas individuales de cada disco estuvieron mal tomadas.

Conclusiones

Si bien tenía muy claro que agregarle otro disco a mi PC y ponerlos en RAID-0 no iba a ser la panacea, esperaba que el cambio se sintiera más en el uso práctico de la PC. Por supuesto que el sistema se nota bastante más ágil para responder, pero no se cuánto se lo puedo atribuir al RAID, y cuánto al tener una instalación limpia del sistema operativo. Al tratar con archivos grandes (de más de un megabyte) las velocidades son mejores, pero el uso que yo le doy a la PC está más asociado a leer y escribir cantidad de archivos chicos.

Las comparaciones entre los dos discos individuales fueron bastante contundentes: el disco nuevo, a pesar de ser de la misma
marca y de la misma capacidad, es bastante mejor que el disco viejo. Esto se puede deber a mejoras del hardware en sí, o quizá tenga que ver el contenido de ambos discos: estaban ambos bien desfragmentados, pero el viejo contenía la instalación del sistema operativo. Lo más correcto debería haber hecho cada conjunto de pruebas partiendo de una instalación limpia del sistema operativo, pero no podía darme el lujo de gastar todo el tiempo que esa tarea iba a insumir.

Sería interesante además, que las pruebas hubiesen sido realizadas utilizando dos discos del mismo modelo. De esta forma podríamos descartar diferencias de hardware, y focalizarnos en la diferencia entre tener un disco o un RAID de varios discos. Lamentablemente, no encontré en el mercado un modelo idéntico al disco que ya tenía.

Es notorio que este tipo de configuraciones e instalaciones no son normales para un usuario común. Llego a esta conclusión basándome en la dificultad para encontrar buena documentación práctica de configuración.

nDe todas formas, el contar con 160GB más de espacio es un avance, y si bien el precio del GB de disco es cada vez más barato, estas pruebas valieron mucho la pena, especialmente por lo aprendido durante el proceso.

Empiezo la colecta para un disco SSD, todo sea por seguir aprendiendo 🙂

Referencias

http://es.wikipedia.org/wiki/RAID

http://en.wikipedia.org/wiki/RAID

http://en.wikipedia.org/wiki/Standard_RAID_levels

http://en.wikipedia.org/wiki/Nested_RAID_levels

TeraCopy

7-Zip

Información sobre el formato de compresión 7z

IOMeter

h2benchw

Ejecutar aplicaciones gráficas de X11 (Linux y Unix) en Windows

Introducción

A veces necesitamos ejecutar aplicaciones de un servidor Linux -o Unix- remoto estando conectados a través de SSH, pero estas aplicaciones tienen una interfaz gráfica. Una solución para este problema puede ser iniciar una sesión de VNC (un escritorio remoto de Linux) y ejecutar desde este ambiente nuestra aplicacion. Otra opción que veremos en este post es utilizando X11 tunneling en nuestra sesión SSH, con la ventaja de que agregamos encriptación sobre el canal de comunicaciones.

¿Qué es X11?

X11 es un software creado en los años 80 por el MIT (Massachusetts Institute of Technology) con el propósito de ser una interfaz gráfica para los sistemas Unix de la época.

Tiene una arquitectura cliente-servidor, proveyendo servicios para acceder a la pantalla y los dispositivos de interacción con el ususario, y permitiendo independizar lo que se quiere mostrar (servidor) de la forma de cómo mostrarlo (cliente). Esto permite
también que el cliente de ventanas no esté corriendo en la misma computadora que el servidor, siendo posible ejecutar aplicaciones gráficas de forma remota.

La comunicación entre el cliente y el servidor se realiza utilizando un protocolo determinado (Xprotocol). El hecho de tener tanto un estándar como un protocolo bien definidos tiene como consecuencia una gran portabilidad del código que utiliza X11.

Wikipedia, como ya es de cosumbre tiene información más detallada y bastante clara sobre el sistema de ventanas X, están los links en las referencias.

Sistema de ventanas X en Windows

El tener una definición estándar del manejo de ventanas X permite implementar clientes para Windows. Uno de estos clientes es XMing, una alternativa gratis que funciona muy bien junto a clientes SSH como PuTTY.

Para poder utilizarlo debemos descargar, instalarlo y ejecutarlo. Aparecerá un ícono con una X en el tray, como se ve en la imagen:
n

Luego, al abrir una sesión SSH, debemos activar la opción para poder hacer tunnelling de X11 a través de esa conexión SSH. En PuTTY, esa opcion está en Connection -> SSH -> X11 -> Enable X11 forwarding:

Luego de tener activado el forwarding, siempre que ejecutemos un comando que necesite un ambiente gráfico en esa sesión SSH, se visualizará en nuestro Windows, como si fuera un proceso local.

En mi caso, el X11 forwarding
sirvió para poder utilizar el IBM Webshphere MQ Explorer (comando strmqcfg). Estoy seguro que lo voy a utilizar en alguna otra oportunidad, espero que a ustedes también les sirva.

Referencias

X11

XMing

De vuelta en el aire

Después de un par de meses sin actividad en el blog, vuelvo a escribir y ponerme al día con varias cosas pendientes.

Desde el último post han pasado varias cosas: cambié el lugar donde trabajo, estuve un mes de vacaciones (conociendo el norte Argentino, Bolivia y Perú), empecé a trabajar con nuevas tecnologías del mundo Java y en proyectos un poco diferentes a los que estaba acostumbrado.

Espero que todo este cambio haya sido para bien, y que se llegue a reflejar en la calidad de los próximos posts.

“Unmappable character for encoding UTF-8” al compilar proyectos Java con Maven

Si al compilar un proyecto no da errores del tipo: /ruta/al/proyecto/src/main/java/paquete/Clase.java:[48,30] unmappable character for encoding UTF8 y en esa línea tenemos caracteres con tilde, ñ u otros caracteres “problemáticos”, es muy posible que sea necesario cambiar la configuración del encoding a ISO-8859-1 en la sección de maven-compiler-plugin del archivo pom.xml de nuestro proyecto:

<br />
&lt;project&gt;<br />
	...<br />
	&lt;build&gt;<br />
		&lt;plugins&gt;<br />
			...<br />
			&lt;plugin&gt;<br />
				&lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;<br />
				&lt;artifactId&gt;maven-compiler-plugin&lt;/artifactId&gt;<br />
				&lt;configuration&gt;<br />
					...<br />
					&lt;encoding&gt;ISO-8859-1&lt;/encoding&gt;<br />
				&lt;/configuration&gt;<br />
			&lt;/plugin&gt;<br />
		&lt;/plugins&gt;<br />
	&lt;/build&gt;<br />
	...<br />
&lt;/project&gt;<br />

Otra opción es sacar esos caracteres del código fuente, totalmente viable si estamos hablando de comentarios, o utilizar
secuencias de escape para representar los caracteres que necesitamos (por ejemplo, ‘u00F6’ en lugar de ‘ö’)

Espero que a alguien más le sea útil este tip.

Configurar log4j desde beans de Spring Framework

Si queremos inicializar log4j al inicializar un contexto de Spring, el framework nos provee la clase org.springframework.util.Log4jConfigurer. Para configurar un bean utilizamos la clase org.springframework.beans.factory.config.MethodInvokingFactoryBean para invocar el método estático initLogging.

Este método recibe como parámetro un String que contiene la localización del archivo de configuración de log4j. Tiene otra sobrecarga que recibe además un long que representa el período en milisegundos en la que un thread verificará si el archivo tuvo cambios, y en caso
afirmativo, recargará la configuración de log4j.

El bean de Spring framework se define de la siguiente forma:

<br />
&lt;bean id='log4jInitializer' class='org.springframework.beans.factory.config.MethodInvokingFactoryBean'&gt;<br />
   &lt;property name='staticMethod' value='org.springframework.util.Log4jConfigurer.initLogging' /&gt;<br />
   &lt;property name='arguments'&gt;<br />
      &lt;list&gt;<br />
         &lt;value&gt;configuracion_log4j&lt;/value&gt;<br />
      &lt;/list&gt;<br />
   &lt;/property&gt;<br />
&lt;/bean&gt;<br />

El parámetro configuracion_log4j apunta a un recurso que puede estar dentro del classpath (por ejemplo, “classpath:log4j.properties“) o ser un archivo del sistema (por ejemplo “file:/home/user/sample/log4j.properties“).

Además, el archivo puede ser la configuración en forma de archivo properties o de archivo XML. Dependiendo de la extensión del archivo, el método initLogging seleccionará la
forma de inicializar la configuración (utilizando DOMConfigurator o PropertyConfigurator).

Evitar la ejecución de tests o ignorar resultados al utilizar Maven

Si por alguna razón queremos evitar que Maven ejecute los tests unitarios al invocarlo, podemos agregar el parámetro -Dmaven.test.skip=true o -DskipTests=true al invocarlo, por ejemplo,

<br />
mvn jar -Dmaven.test.skip=true<br />

Si queremos que ejecute los tests pero que ignore los resultados, es decir, que no cancele la ejecución si algún test falla, podemos agregar el parámetro “-Dmaven.test.failure.ignore=true” a la invocación a Maven.

Si tenemos tests unitarios implementados, obviamente no es lo ideal evitar su ejecución, o ignorar los fallos que tengan, pero de tanto en tanto puede ser útil.

Para los tests de integración, que por concepto quizá tengan que interactuar con otros sistemas, tenemos la opción de configurar la exclusión de ciertos archivos, y que los tests contenidos en ellos no se ejecuten automáticamente al correr los goals de test de Maven. Esto se configura en el POM del proyecto, al configurar
las opciones del plugin Surefire (ejecución de tests):

<br />
&lt;build&gt;<br />
	&lt;plugins&gt;<br />
		[...]<br />
		&lt;plugin&gt;<br />
			&lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;<br />
			&lt;artifactId&gt;maven-surefire-plugin&lt;/artifactId&gt;<br />
			&lt;configuration&gt;<br />
				&lt;includes&gt;<br />
					&lt;include&gt;**/*.java&lt;/include&gt;<br />
				&lt;/includes&gt;<br />
				&lt;excludes&gt;<br />
					&lt;exclude&gt;integrationTests&lt;/exclude&gt;<br />
				&lt;/excludes&gt;<br />
				&lt;forkMode&gt;never&lt;/forkMode&gt;<br />
			&lt;/configuration&gt;<br />
		&lt;/plugin&gt;<br />
		[...]<br />
	&lt;/plugins&gt;<br />
&lt;/build&gt;<br />

Para este ejempo planteado, ejecutará todos los tests del directorio correspondiente, excluyendo los que están dentro del directorio integrationTests y sus subdirectorios.

Referencias

http://maven.apache.org/general.html#skip-test
http://maven.apache.org/plugins/maven-surefire-plugin/examples/skipping-test.html
http://maven.apache.org/plugins/maven-surefire-plugin/examples/inclusion-exclusion.html