¿Cuándo se ve realmente obligado a usar UUID como parte del diseño?

123

Realmente no veo el punto de UUID . Sé que la probabilidad de una colisión es efectivamente nula , pero efectivamente nula ni siquiera es casi imposible.

¿Alguien puede dar un ejemplo en el que no tenga más remedio que usar UUID? De todos los usos que he visto, puedo ver un diseño alternativo sin UUID. Claro que el diseño puede ser un poco más complicado, pero al menos no tiene una probabilidad de falla distinta de cero.

UUID huele a variables globales para mí. Hay muchas formas en que las variables globales hacen que el diseño sea más simple, pero es solo un diseño vago.

Pirolista
fuente
23
Todo tiene una probabilidad de fracaso distinta de cero. Me concentraría en problemas mucho más probables (es decir, casi cualquier cosa que se te ocurra) que la colisión de UUID
DanSingerman
16
En realidad, "efectivamente nulo" es casi imposible.
mqp
21
No, en realidad es infinitamente lejos de imposible
Pyrolistical
32
@Pyrolistic cuando comienzas a lanzar palabras como "infinito", has dejado el mundo del desarrollo de software. La teoría de la informática es una discusión completamente diferente a escribir software real.
Rex M
2
Cerraré

Respuestas:

617

Escribí el generador / analizador UUID para Ruby, por lo que me considero razonablemente bien informado sobre el tema. Hay cuatro versiones principales de UUID:

Los UUID de la versión 4 son esencialmente solo 16 bytes de aleatoriedad extraídos de un generador de números aleatorios criptográficamente seguro, con algunos cambios de bits para identificar la versión y variante del UUID. Es extremadamente improbable que estos choquen, pero podría suceder si se usa un PRNG o si resulta que tienes mucha, mucha, mucha, mucha, mucha mala suerte.

Los UUID de la Versión 5 y la Versión 3 usan las funciones hash SHA1 y MD5 respectivamente, para combinar un espacio de nombres con una pieza de datos ya únicos para generar un UUID. Esto, por ejemplo, le permitirá producir un UUID desde una URL. Las colisiones aquí solo son posibles si la función hash subyacente también tiene una colisión.

Los UUID de la versión 1 son los más comunes. Utilizan la dirección MAC de la tarjeta de red (que, a menos que sea falsa, debe ser única), más una marca de tiempo, más el giro de bits habitual para generar el UUID. En el caso de una máquina que no tiene una dirección MAC, los bytes de 6 nodos se generan con un generador de números aleatorios criptográficamente seguro. Si se generan dos UUID en secuencia lo suficientemente rápido como para que la marca de tiempo coincida con el UUID anterior, la marca de tiempo se incrementa en 1. Las colisiones no deberían ocurrir a menos que ocurra una de las siguientes situaciones: la dirección MAC es falsa; Una máquina que ejecuta dos aplicaciones generadoras de UUID diferentes produce UUID en el mismo momento; Dos máquinas sin una tarjeta de red o sin acceso de nivel de usuario a la dirección MAC reciben la misma secuencia de nodo aleatorio y generan UUID en el mismo momento exacto;

Siendo realistas, ninguno de estos eventos ocurre por accidente dentro del espacio de identificación de una sola aplicación. A menos que acepte identificaciones en, por ejemplo, una escala de Internet, o con un entorno no confiable en el que las personas malintencionadas puedan hacer algo malo en el caso de una colisión de identificaciones, simplemente no es algo de lo que deba preocuparse. Es fundamental comprender que si genera la misma versión 4 UUID que yo, en la mayoría de los casos, no importa. He generado la ID en un espacio de ID completamente diferente al tuyo. Mi aplicación nunca sabrá sobre la colisión, por lo que la colisión no importa. Francamente, en un solo espacio de aplicación sin actores maliciosos, la extinción de toda la vida en la tierra ocurrirá mucho antes de que tenga una colisión, incluso en un UUID de versión 4, incluso si usted '

Además, 2 ^ 64 * 16 son 256 exabytes. Como en, necesitaría almacenar 256 exabytes de ID antes de tener una probabilidad del 50% de una colisión de ID en un solo espacio de aplicación.

Bob Aman
fuente
8
Esta es, con mucho, la mejor explicación. No sé por qué esto no se vota a la cima. Felicitaciones a usted Sporkmonger.
Brad Barker
1
@Chamnap Escribí UUIDTools. Los UUID se pueden convertir a un entero o su forma de byte sin procesar, y serían sustancialmente más pequeños como binarios.
Bob Aman
1
@Chamnap uuid.rawte dará la cadena de bytes. El hashmétodo no es útil para ti. Se utiliza para tablas hash y operaciones de comparación internamente dentro de Ruby. Todos los métodos para convertir ay desde varias representaciones de UUID se definen como métodos de clase y deben tener como prefijo "parse".
Bob Aman
3
@BobAman en 1990 Tuve 12 colisiones UUID en un sistema Aegis, resultó ser una FPU defectuosa, pero pensé que podría hacerle saber que puede suceder (aunque no ha sucedido más que eso en los últimos 30 años de programación) . Buena explicación, por cierto, esta es mi publicación de referencia UUID de facto para dar a la gente :)
GMasucci
2
@kqr Tienes toda la razón de que es el problema del cumpleaños, sin embargo, para un código de n bits, el problema de la paradoja del cumpleaños se reduce a 2 ^ (n / 2), que en este caso es 2 ^ 64, como se indica en mi respuesta .
Bob Aman
69

Lo que le compran los UUID que es muy difícil de hacer de otra manera es obtener un identificador único sin tener que consultar o coordinar con una autoridad central . El problema general de poder obtener tal cosa sin algún tipo de infraestructura administrada es el problema que resuelven los UUID.

He leído que, según la paradoja del cumpleaños, la posibilidad de que se produzca una colisión UUID es del 50% una vez que se han generado 2 ^ 64 UUID. Ahora 2 ^ 64 es un número bastante grande, pero un 50% de posibilidades de colisión parece demasiado arriesgado (por ejemplo, cuántos UUID deben existir antes de que haya un 5% de posibilidades de colisión, incluso eso parece una probabilidad demasiado grande) .

El problema con ese análisis es doble:

  1. Los UUID no son completamente al azar: hay componentes principales del UUID que se basan en el tiempo y / o la ubicación. Por lo tanto, para tener una posibilidad real de colisión, los UUID en colisión deben generarse al mismo tiempo desde diferentes generadores de UUID. Yo diría que si bien existe una posibilidad razonable de que se puedan generar varios UUID al mismo tiempo, hay suficiente cantidad de datos (incluida información de ubicación o bits aleatorios) para hacer casi imposible la posibilidad de una colisión entre este conjunto muy pequeño de UUID .

  2. estrictamente hablando, los UUID solo necesitan ser únicos entre el conjunto de otros UUID con los que podrían compararse. Si está generando un UUID para usarlo como clave de base de datos, no importa si en otro lugar en un universo alternativo malvado se está utilizando el mismo UUID para identificar una interfaz COM. Al igual que no causará confusión si hay alguien (o algo) llamado "Michael Burr" en Alpha-Centauri.

Michael Burr
fuente
1
¿Ejemplo concreto? UUID COM / DCE: no hay autoridad para asignarlos, y nadie quería asumir la responsabilidad y / o nadie quería que hubiera una autoridad. Bases de datos distribuidas que no tienen enlaces confiables ni maestros.
Michael Burr el
3
Ejemplo más concreto: una aplicación bancaria. Se instala múltiples centros de datos, uno para cada país, con cada centro de datos con una base de datos. Las múltiples instalaciones están ahí para obedecer diferentes regulaciones. Solo puede haber un registro de cliente en todo el conjunto para cada cliente .....
Vineet Reynolds
(Continuación del comentario anterior) Debe tener un servidor central para generar la identificación del cliente con fines generales de informes y seguimiento (en todas las instalaciones) o hacer que las instalaciones individuales generen UUID para que sirvan como identificación del cliente (obviamente, los UUID no pueden usarse como en en informes).
Vineet Reynolds
Para cuando tengas un 50% de posibilidades de duplicación, ya te estás ahogando. Alguien señala el volumen requerido para llegar al 0.0000001% de probabilidad. Múltiples bases de datos de incremento automático que comienzan en 1 a n y aumentan en n cada vez resuelve el mismo problema de manera efectiva.
Gordon
2
Las probabilidades de obtener un duplicado son MUCHO, MUCHO menores que las probabilidades de que la autoridad central falle de alguna manera crítica para la misión
std''OrgnlDave
33

Todo tiene una probabilidad de fracaso distinta de cero. Me concentraría en problemas mucho más probables (es decir, casi cualquier cosa que se te ocurra) que la colisión de UUID

DanSingerman
fuente
Agregado como respuesta a solicitud de
Pyrolistical
16

Un énfasis en "razonablemente" o, como lo dice, "efectivamente": lo suficientemente bueno es cómo funciona el mundo real. La cantidad de trabajo computacional involucrado en cubrir esa brecha entre "prácticamente único" y "verdaderamente único" es enorme. La unicidad es una curva con rendimientos decrecientes. En algún punto de esa curva, hay una línea entre donde "lo suficientemente único" todavía es asequible, y luego nos curvamos MUY abruptamente. El costo de agregar más singularidad se vuelve bastante grande. La unicidad infinita tiene un costo infinito.

UUID / GUID es, en términos relativos, una manera computacionalmente rápida y fácil de generar una ID que se puede suponer razonablemente que es universalmente única. Esto es muy importante en muchos sistemas que necesitan integrar datos de sistemas previamente desconectados. Por ejemplo: si tiene un sistema de gestión de contenido que se ejecuta en dos plataformas diferentes, pero en algún momento necesita importar el contenido de un sistema a otro. No desea que las ID cambien, por lo que sus referencias entre los datos del sistema A permanecen intactas, pero no desea ninguna colisión con los datos creados en el sistema B. Un UUID resuelve esto.

Rex M
fuente
Solución. No seas perezoso y actualiza las referencias. Hazlo bien.
Pyrolistic
8
Esto no tiene nada que ver con la pereza: si la política es que un ID para un artículo se considera permanente e inmutable, entonces el ID no cambia. Por lo tanto, desea que las ID sean únicas desde el principio, y desea hacerlo sin requerir que todos los sistemas estén conectados de alguna manera desde el principio.
Michael Burr el
Necesitas contexto entonces. Si tiene dos grupos de identificadores únicos que pueden entrar en conflicto, necesita un alto nivel de contexto para separarlos
Pyrolistical
23
O bien, podría construir el sistema para usar UUID y enviarlo, venderlo, ganar un millón de dólares y nunca escuchar una sola queja de que dos ID chocaron porque no sucederá.
Rex M
16

Nunca es absolutamente necesario crear un UUID. Sin embargo, es conveniente tener un estándar donde los usuarios sin conexión puedan generar una clave para algo con una probabilidad muy baja de colisión.

Esto puede ayudar en la resolución de replicación de la base de datos, etc.

Sería fácil para los usuarios en línea generar claves únicas para algo sin la sobrecarga o la posibilidad de colisión, pero eso no es para lo que son los UUID.

De todos modos, una palabra sobre la probabilidad de colisión, tomada de Wikipedia:

Para poner estos números en perspectiva, se estima que el riesgo anual de ser golpeado por un meteorito es una posibilidad en 17 mil millones, lo que equivale a las probabilidades de crear unas pocas decenas de billones de UUID en un año y tener un duplicado. En otras palabras, solo después de generar mil millones de UUID por segundo durante los próximos 100 años, la probabilidad de crear un solo duplicado sería de aproximadamente el 50%.

John Gietzen
fuente
44
Simple, no permita que los usuarios sin conexión generen claves. Haga que se asignen las claves temporales hasta que el sistema se conecte para que se puedan generar las claves reales.
Pyrolistic
Esta es una respuesta muy útil en mi opinión ... iba a ofrecer algún tipo de analogía a la probabilidad yo mismo, ya que parecía que el OP no comprendió su significado, pero parece que lo has hecho.
Noldorin
Entiendo que la probabilidad es efectivamente nula. Para mí, el uso de UUID es el diseño perezoso, y yo sólo quería ver si siempre se puede evitarlo
Pyrolistical
Eso es lo suficientemente justo, siempre y cuando veas que la baja probabilidad debe considerarse incluso en las circunstancias más extremas, como ahora supongo que lo haces.
Noldorin
13

Un ejemplo clásico es cuando está replicando entre dos bases de datos.

DB (A) inserta un registro con ID 10 y al mismo tiempo DB (B) crea un registro con ID 10. Esto es una colisión.

Con UUID esto no sucederá ya que no coincidirán. (casi seguro)

Johnno Nolan
fuente
1
Ok, entonces haga que DB A use ID par y DB B use ID impares. Hecho, sin UUID.
Pyrolistic
2
Con tres DB's, use 3 múltiples LOL
Jhonny D. Cano -Leftware-
20
Si usa los múltiplos 2/3 / lo que sea, ¿qué sucede cuando agrega un nuevo servidor a la mezcla más tarde? Debe coordinar un interruptor para que esté usando n + 1 múltiplos en el nuevo servidor, y mover todos los servidores antiguos al nuevo algoritmo, y debe apagar todo mientras lo hace para evitar colisiones durante El cambio de algoritmo. O ... podría usar UUID como TODOS LOS MÁS.
Bob Aman el
3
Es incluso peor que eso, porque ¿cómo diferenciarías entre múltiplos de 2 y múltiplos de 4? ¿O múltiplos de 3 frente a múltiplos de 6? De hecho, tendrías que seguir con múltiplos de números primos. Blech! Solo usa UUID, funciona. Microsoft, Apple y muchos otros confían en ellos y confían en ellos.
sidewinderguy
2
@sidewinderguy, en GUID confiamos! :)
Ron Klein
13

También hay una probabilidad distinta de cero de que cada partícula en su cuerpo haga un túnel simultáneamente a través de la silla en la que está sentado y de repente se encontrará sentado en el piso.

¿Te preocupa eso?

usuario21714
fuente
77
Por supuesto que no, eso no es algo que pueda controlar, sino diseños que sí puedo.
Pirolístico 01 de
44
@Pirolística ¿Es eso realmente, quiero decir REALMENTE la razón por la que no te preocupas por eso? Entonces eres bastante extraño. Y además, no tienes razón. Usted puede controlarlo. Si aumenta algunas libras, disminuye significativamente la probabilidad de tal evento. ¿Considera que debería aumentar de peso, entonces? :-)
Veky
8

Tengo un esquema para evitar UUID. Configure un servidor en algún lugar y téngalo de modo que cada vez que una pieza de software quiera un identificador universalmente único, se comunique con ese servidor y lo entregue. ¡Sencillo!

Excepto que hay algunos problemas prácticos reales con esto, incluso si ignoramos la malicia absoluta. En particular, ese servidor puede fallar o no ser accesible desde una parte de Internet. Lidiar con la falla del servidor requiere replicación, y eso es muy difícil de corregir (consulte la literatura sobre el algoritmo de Paxos para saber por qué la creación de consenso es incómoda) y también es bastante lenta. Además, si no se puede acceder a todos los servidores desde una parte particular de la red, ninguno de los clientes conectados a esa subred podrá hacer nada porque todos estarán esperando nuevas identificaciones.

Entonces ... use un algoritmo probabilístico simple para generarlos que es poco probable que falle durante la vida útil de la Tierra, o (financie y) construya una infraestructura importante que será un PITA de despliegue y tenga fallas frecuentes. Sé cuál elegiría.

Compañeros de Donal
fuente
2
En realidad, el objetivo de la invención de UUID era evitar su enfoque. Si investiga la historia de los UUID, verá que se deriva de los primeros experimentos en la creación de redes de computadoras sofisticadas y significativas. Sabían que las redes son inherentemente poco confiables y complicadas. Los UUID fueron la respuesta a la pregunta de cómo coordinar datos entre computadoras cuando se sabía que no podían estar en comunicación constante.
Basil Bourque
77
@BasilBourque Estaba usando sarcasmo en ese primer párrafo, en caso de que no fuera obvio.
Donal Fellows
5

No entiendo todo sobre la probabilidad de colisión. No me importa la colisión. Aunque me importa el rendimiento.

https://dba.stackexchange.com/a/119129/33649

Los UUID son un desastre de rendimiento para tablas muy grandes. (200K filas no son "muy grandes").

Su # 3 es realmente malo cuando el CHARCTER SET es utf8 - ¡CHAR (36) ocupa 108 bytes!

Los UUID (GUID) son muy "aleatorios". Usarlos como clave ÚNICA o PRIMARIA en tablas grandes es muy ineficiente. Esto se debe a que tiene que saltar alrededor de la tabla / índice cada vez que INSERTE un nuevo UUID o SELECCIONE por UUID. Cuando la tabla / índice es demasiado grande para caber en la memoria caché (vea innodb_buffer_pool_size, que debe ser más pequeño que la RAM, generalmente 70%), el 'siguiente' UUID no se puede almacenar en caché, por lo tanto, un golpe de disco lento. Cuando la tabla / índice es 20 veces más grande que la memoria caché, solo se almacena en caché 1/20 (5%) de los aciertos; usted está vinculado a E / S.

Por lo tanto, no use UUID a menos que

tiene tablas "pequeñas" o realmente las necesita debido a la generación de identificadores únicos desde diferentes lugares (y no ha descubierto otra forma de hacerlo). Más sobre UUID: http://mysql.rjweb.org/doc.php/uuid (Incluye funciones para convertir entre UUID estándar de 36 caracteres y BINARY (16)).

Tener un AUTO_INCREMENTO ÚNICO y un UUID ÚNICO en la misma tabla es un desperdicio.

Cuando ocurre un INSERT, todas las claves únicas / primarias deben ser verificadas por duplicados. Cualquier clave única es suficiente para el requisito de InnoDB de tener una CLAVE PRIMARIA. BINARY (16) (16 bytes) es algo voluminoso (un argumento en contra de convertirlo en PK), pero no está tan mal. El volumen es importante cuando tienes claves secundarias. InnoDB pega silenciosamente la PK al final de cada clave secundaria. La lección principal aquí es minimizar el número de claves secundarias, especialmente para tablas muy grandes. Para comparar: INT UNSIGNED es de 4 bytes con un rango de 0..4 mil millones. BIGINT es de 8 bytes.

Toskan
fuente
4

Si solo mira las alternativas, por ejemplo, para una aplicación de base de datos simple, para tener que consultar la base de datos cada vez antes de crear un nuevo objeto, pronto descubrirá que usar UUID puede reducir efectivamente la complejidad de su sistema. De acuerdo: si usa las teclas int, son de 32 bits, que se almacenarán en una cuarta parte del UUID de 128 bits. Concedido: los algoritmos de generación de UUID requieren más potencia computacional que simplemente incrementar un número. ¿Pero a quién le importa? La sobrecarga de administrar una "autoridad" para asignar números que de otro modo serían únicos supera fácilmente eso por órdenes de magnitud, dependiendo de su espacio de identificación de unicidad previsto.

Mirko Klemm
fuente
3

En UUID == diseño perezoso

No estoy de acuerdo, se trata de elegir tus peleas. Si un UUID duplicado es estadísticamente imposible y se prueban las matemáticas, ¿por qué preocuparse? Pasar tiempo diseñando alrededor de su pequeño sistema generador de N UUID no es práctico, siempre hay una docena de otras formas en que puede mejorar su sistema.

Johnno Nolan
fuente
1

En mi último trabajo, recibíamos objetos de terceros que estaban identificados de forma exclusiva con UUID. Puse una tabla de búsqueda de entero largo UUID-> y utilicé entero largo como mis claves principales porque era mucho más rápido de esa manera.

Paul Tomblin
fuente
Sí, claro, un tercero que te obliga a usar UUID es otro problema en el que no quiero entrar. Suponiendo que tiene control para usar UUID o no.
Pyrolistic
Bueno, un "entero largo" (128 bits) es en realidad lo que es un UUID. Simplemente se muestra como una cadena para el consumo humano. A veces puede transmitirse de esa manera, pero para el almacenamiento y la indexación, sin duda, será más rápido en forma de enteros como lo encontró.
Nicole
1

Usando el algoritmo de la versión 1 parece que es imposible una colisión bajo la restricción de que se generan menos de 10 UUID por milisegundo a partir de la misma dirección MAC

Conceptualmente, el esquema de generación original (versión 1) para UUID era concatenar la versión UUID con la dirección MAC de la computadora que genera el UUID y con el número de intervalos de 100 nanosegundos desde la adopción del calendario gregoriano en Occidente . En la práctica, el algoritmo real es más complicado. Este esquema ha sido criticado porque no es suficientemente 'opaco'; revela tanto la identidad de la computadora que generó el UUID como el momento en que lo hizo.

Alguien me corrige si malinterpreto cómo funciona

Davy8
fuente
Hay muchas versiones, y muchos sistemas de software (Java, por ejemplo) no pueden usar la versión 1, ya que no tiene una forma pura de Java para acceder a la dirección mac.
Pyrolistic
Con respecto a la incapacidad de Java para obtener la dirección MAC: no es del todo cierto. Hay soluciones para esto. Puede configurar manualmente la dirección MAC utilizada por el generador a través de un archivo de configuración. También puede llamar a ifconfig y analizar la salida. El generador de Ruby UUID que escribí usa ambos enfoques.
Bob Aman el
Además, como se menciona en mi respuesta, si no puede obtener una dirección MAC para un UUID de la versión 1, utilice 6 bytes aleatorios, según la sección 4.5 de RFC 4122. Por lo tanto, incluso si no desea utilizar ninguno de Con las dos soluciones para Java, aún puede generar un UUID de versión 1 válido.
Bob Aman el
Los GUID de MS son solo números aleatorios. Ya no tienen ninguna parte MAC, porque eso hizo posible realizar ingeniería inversa de la dirección MAC del servidor (lo que resultó ser muy peligroso).
Stefan Steiger
1

Para aquellos que dicen que los UUID son un mal diseño porque podrían (con una probabilidad ridículamente pequeña) colisionar, mientras que sus claves generadas por DB no ... saben la posibilidad de que un error humano provoque una colisión en sus claves generadas por DB debido a algunos -la necesidad prevista es MUCHO MUCHO MUCHO mayor que la posibilidad de colisión UUID4. Nos saber que si se vuelve a crear la base de datos se iniciará en los identificadores de 1 de nuevo, y cómo muchos de nosotros hemos tenido que volver a crear una mesa cuando estábamos seguros de que nunca necesitaríamos? Pondría mi dinero en la seguridad de UUID cuando las cosas comienzan a salir mal con incógnitas desconocidas cualquier día.

Iain Duncan
fuente
0

Además de los casos en los que tiene que usar la API de otra persona que exige un UUID, por supuesto, siempre hay otra solución. ¿Pero esas alternativas resolverán todos los problemas que hacen los UUID? ¿Terminará agregando más capas de hacks, cada una para resolver un problema diferente, cuando podría haber resuelto todos a la vez?

Sí, teóricamente es posible que los UUID choquen. Como otros han señalado, es ridículamente improbable hasta el punto de que simplemente no valga la pena considerarlo. Nunca ha sucedido hasta la fecha y probablemente nunca lo hará. Olvídalo.

La forma más "obvia" de evitar colisiones es dejar que un solo servidor genere ID únicos en cada inserción, lo que obviamente crea serios problemas de rendimiento y no resuelve el problema de generación fuera de línea. Ups

La otra solución "obvia" es una autoridad central que entrega bloques de números únicos por adelantado, que es esencialmente lo que hace UUID V1 al usar la dirección MAC de la máquina generadora (a través de IEEE OUI). Pero las direcciones MAC duplicadas suceden porque eventualmente todas las autoridades centrales se equivocan, por lo que en la práctica esto es mucho más probable que una colisión UUID V4. Ups

El mejor argumento contra el uso de UUID es que son "demasiado grandes", pero un esquema (significativamente) más pequeño inevitablemente no resolverá los problemas más interesantes; El tamaño de los UUID es un efecto secundario inherente de su utilidad para resolver esos mismos problemas.

Es posible que su problema no sea lo suficientemente grande como para necesitar lo que ofrecen los UUID y, en ese caso, no dude en usar otra cosa. Pero si su problema crece inesperadamente (y la mayoría lo hace), terminará cambiando más tarde, y se pateará por no usarlos en primer lugar. ¿Por qué diseñar para el fracaso cuando es tan fácil diseñar para el éxito?

StephenS
fuente
-10

Los UUID incorporan todas las malas prácticas de codificación asociadas con las variables globales, solo que peor, ya que son variables superglobales que pueden distribuirse en diferentes piezas del kit.

Recientemente tuve un problema con el reemplazo de una impresora con un modelo de reemplazo exacto, y descubrí que ninguno de los software del cliente funcionaría.

John Doe
fuente
2
Me alegra que vivamos en una sociedad que todavía se enfoca en hechos en lugar de opiniones aleatorias, de lo contrario, todos nosotros en desbordamiento de pila estaríamos sin trabajo. :)
Makarand