Weblogs Código

Blog Bitix

Decidido a comprar un Intel NUC Bean Canyon entre las opciones que he evaluado

junio 24, 2018 07:30

Intel

El ordenador portátil que tenía sigue siendo perfectamente útil, sin embargo, en algunos casos concretos de mi uso la cantidad de memoria que tenía se me estaba quedando corta y cuando me pasa esto suelo aprovechar si alguien de mi entorno necesita ordenador traspasarlo y yo comprarme otro nuevo.

El ordenador que tenía era un Sony VAIO de finales del 2012 con un procesador Intel i3 3210M con gráfica integrada HD 4000, 8 GiB de memoria, un disco duro de estado sólido Samsung 840 EVO de 256 GiB con interfaz SATA y una pantalla de resolución 1600x900. Durante todo este tiempo me ha funcionado a la perfección, incluso con su gráfica me fue suficiente para un entorno de escritorio GNOME con GNU/Linux y he acabado muy contento con la marca Sony al contrario de mi anterior portátil Dell XPS 1530 de la que no pienso comprar nunca nada de esta marca, quizá tuve mala suerte pero el portátil Dell no fue barato, no acabé contento con la calidad del producto pasado el encantamiento inicial y al final acabó totalmente muerto sin ni siquiera encenderse.

Con lo que desde hace unos meses he empezado a barajar opciones para comprar equipo con los requisitos que tengo de ser lo más pequeño posible, silencioso, con 32 GiB de memoria, procesador con mínimo 4 cores y 8 hilos, no necesito tarjeta gráfica dedicada ya que no jugaré a juegos o una integrada con bajar calidad de detalles me servirá a modo ocasional. También quería una pantalla externa de 25” o 27” con resolución de 2K para mayor comodidad y evitar problemas por estar con la cabeza medio agachada durante bastante tiempo. Habiendo pasado 5 años desde mi anterior portátil Sony quiero que el ordenador sea un salto significativo en lo que tenía por eso el requerimiento de los 4 cores, los 32 GiB de memoria y la pantalla 2K. La memoria a pesar del caro precio de la DDR4 en el momento de este artículo. El SSD es la mejor inversión para mejorar el rendimiento que uno puede hacer, más que el procesador. El SSD probablemente elija uno en formato M.2 con interfaz NVMe que tienen una tasa de transferencia 3 veces superior a los SSD con interfaz SATA que ya son rápidos, los NVMe son algo más caros pero no mucho más.

He barajado varias opciones principalmente ordenadores en factor de forma pequeños como los Intel NUC, Gigabyte Brix, Zotac, MSI Cubi, Slimbook ONE, Slimbook PRO2 o AMD Ryzen montando un mini-ITX. Ya estoy casi decidido a lo que compraré y no quiero esperar mucho más a tener equipo propio de cuando se comercialicen y aunque estoy aguantando con un Mac del trabajo comentando Tu con tu macOS yo con mi GNU/Linux. Será con mucha probabilidad un Intel NUC Bean Canyon que se lanzarán en la segunda mitad del 2018.

A pesar de saber que todos los procesadores Intel de la última década está afectados por los importantes problemas de seguridad Meltdown y Spectre que se están mitigando por software, de momento no se ha descubierto una forma de aprovecharlos pero quizá en un futuro se pueda y afecte de forma importante al rendimiento para corregirlos si es que se puede. Pero es que el resto de opciones he ido descartando por otros motivos.

Meltdown Spectre Logotipos de Meltdown y Spectre

El precio que calculo tendrá el Intel NUC Bean Canyon con procesador Intel i5 8250U (4 núcleos, 8 hilos, 6 MiB cache) estará en el momento de su salida entre los 350 y 450€ sin incluir la memoria y el SSD, respecto al modelo con el Intel i3 7100U (2 núcleos, 4 hilos, 3 MiB cache) que cuesta unos 250€ se puede dudar si esos dos núcleos adicionales y 3 MiB de caché más en el procesador merecen la pena la diferencia de precio. En mi caso estoy estoy dispuesto a pagarlo queriendo tener algo significativamente con mejor rendimiento teórico que lo que tenía.

He revisado algunos análisis de los NUC y me han gustado, en este por ejemplo se explica cómo instalar la memoria, el SSD, como es la BIOS/UEFI y sus opciones de configuración como por ejemplo el ring que puede mostrar la actividad del SSD y se pueden personalizar los colores, también muestra el rendimiento en algunos juegos. Y en otro la gráfica HD 620 es suficiente para jugar a Diablo 3 a 30 fotogramas por segundo en un resolución de 1080p bajando detalles, esta gráfica integrada equivale a una dedicada NVIDIA 920m o GeForce 730 que no está del todo mal para un jugador ocasional si no importa jugar bajando algunos detalles de calidad. En GNU/Linux es posible jugar a juegos destinados a Windows con PlayOnLinux.

El uso que le daré es el de programación, virtualización, contenedores Docker, navegación, ofimática, leer PDF, tal vez algún juego ocasional, quizá Diablo 3 si pudiese, y usando GNU/Linux con la distribución Arch Linux. En un futuro lo quiero como equipo servidor dedicado personal con por ejemplo Nextcloud.

Los Gigabyte Brix ya hay algunos modelos con los Intel i5 8250U a un precio de unos 440€, sin embargo, no me gusta que no tienen luz indicador de actividad de SSD y creo que su BIOS/UEFI es en modo texto. La luz de actividad de SSD me parece importante para saber si en algún momento el ordenador se queda bloqueado o por algún tipo de problema lee, o peor escribe, constantemente al disco duro cuando no debería. Los Zotac están dotados de una buena cantidad de puertos USB, incluso algunos tiene dos conexiones de red pero son algo caros y la mayoría de sus modelos están orientados a gamers, no son fáciles de encontrar ni siquiera en Amazon ni en PC Componentes que es donde compraré finalmente el equipo. De MSI Cubi no hay muchos modelos y aún no he visto ninguno anunciado con un Intel de 4 núcleos. Los Slimbook ONE me gustaban pero los modelos con los Intel de 4 núcleos solo tienen una ranura de memoria, se les pueden instalar 32 GiB… cuando haya módulos de memoria de 32 GiB, ahora me tendría que conformar con 16 GiB.

También he seguido los NUC con un Intel Core i3 7100U sin embargo tiene solo 2 cores y 4 hilos con 3 MiB de memoria será algo mejor que el Intel i3 3210M que tenía pero no me parece una mejora significativa habiendo pasado 5 años y a pesar de que el 7100U está fabricado a 14 nanómetros y el 3210M a 22 nanómetros, los NUC con Intel i7 8650U son más caros, sobre los 580€ y teniendo en cuenta que la memoria y SSD hay que comprarlos aparte. Los NUC más económicos sobre los 175€ con Intel Pentium J5005 solo admiten 8 gigas como máximo y me parece que están limitados artificialmente a ese tamaño máximo de memoria. En Aliexpress hay productos similares a los NUC con procesador i5 8250U pero a ver quien se la juega a importar desde China por aduanas y con la incertidumbre de lo que llega y que tal funciona, también he estado mirando placas ARM aunque no con el objetivo que sea mi equipo principal, 7+ computadoras baratas del tamaño de una tarjeta de crédito basadas en GNU/Linux y ARM, como la Rock64 que tiene 4 gigas pero siguiendo su desarrollo y foros no son pocos los problemas que la gente crea en su repositorio de Github con lo que me da que pensar que la experiencia podría ser algo frustrante, la arquitectura x86/x64 es mucho más compatible, en ARM cada placa es un mundo aparte de que su rendimiento es mucho más limitado.

Preferiría un microprocesador AMD ya que solo están afectados por algunas variantes de Spectre, pero AMD no tiene tanta diversidad de productos como tiene Intel, no tienen nada parecido a los Intel NUC y en portátiles prácticamente todos los modelos equipan un Intel, quizá cambia ahora algo con los Ryzen. Por rendimiento y precio posiblemente elegiría un AMD Ryzen 2400G pero de momento las placas con el chipset B450 para sacarle el máximo rendimiento aún no están disponibles, en la generación 1xxx de los Ryzen no hay modelos con gráfica integrada, tendría que ser un mini-ITX y el equipo me lo tendría que montar yo, no es difícil pero si tuviese algún imprevisto o incompatibilidad sería un problema por tener que devolver piezas y buscar otras, además no estoy seguro del ruido que haría, lo quiero muy silencioso al menos en tareas ofimáticas, navegación por internet y cosas simples, no estoy seguro de que lo sea con el ventilador de la CPU y fuente de alimentación. Ocupa más que un NUC para mi importante si en un futuro le quiero dar uso como ordenador secundario, servidor personal o quisiera traspasarlo. Pero por 400€ se tiene un ordenador muy decente en plataforma AMD Ryzen a falta de memoria y SSD.

La pantalla que estoy siguiendo es una 2K de BenQ PD2700Q en tamaño de 27”, 4K me parece excesiva resolución para ese tamaño de pantalla, a su máxima resolución en una 27” 4K las cosas ven muy pequeñas y tendría que escalarla a 2K, busco que sea IPS ya que los colores en este tipo de paneles se ven significativamente mejor que en un panel TN, uno al lado del otro la diferencia es clara. Apreciaré una mejora notable en la calidad de la pantalla de la que tenía en el portátil Sony. No es barata pero es algo que no necesitan renovarse tan a menudo como el ordenador seguramente pueda tener un uso durante una década si no presenta ningún problema de funcionamiento. Otros aspectos interesantes son ser regulable en altura y horizontalmente, colocable en posición vertical y puertos USB, HDMI y DisplayPort por otro lado una información en pantalla o OSD aceptable y de fácil configuración.

Entre otras cosas están el teclado y ratón que me conformo con un modelo básico y barato como el Logitech MK120 y una alfombrilla que cubra la mesa.

Cuando realmente compre el equipo publicaré varios artículos con el unboxing, instalación, análisis, uso y opinión personal de varios de ellos.

» Leer más, comentarios, etc...

Variable not found

Enlaces interesantes 326

junio 19, 2018 02:21

Enlaces interesantes
Ahí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)

.NET / .NET Core

ASP.NET / ASP.NET Core

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

Data

HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

Otros

Publicado en Variable not found.

» Leer más, comentarios, etc...

Variable not found

El nuevo tipo de retorno ActionResult<T> en ASP.NET Core 2.1

junio 19, 2018 08:26

ASP.NET Core MVC Como sabéis, por defecto las acciones MVC y Web API en ASP.NET Core retornan un IActionResult (o un Task<IActionResult> en caso de ser asíncronas). Este interfaz, cuya única misión es definir el método de ejecución asíncrona del resultado, ExecuteResultAsync(), es implementado por la gran variedad de tipos de resultado ofrecidos de serie por el framework, como ViewResult, RedirectResult o FileResult.

Sin embargo, no es esta la única opción disponible a la hora de especificar el resultado de una acción, como veremos a continuación.

¿Qué más puede retornar una acción en ASP.NET Core?

Pues además de una instancia de IActionResult, ASP.NET Core MVC ofrece otros tipos de resultado a la hora de implementar acciones.

Por ejemplo, el framework también incluye la clase abstracta ActionResult, definida como vemos más abajo:
public abstract class ActionResult : IActionResult
{
public virtual Task ExecuteResultAsync(ActionContext context)
{
ExecuteResult(context);
return Task.CompletedTask;
}

public virtual void ExecuteResult(ActionContext context) { }
}
Esta clase, que es la base de prácticamente todos los tipos de resultados que vienen de serie con el framework, establece un puente sencillo entre la ejecución asíncrona del resultado y su implementación síncrona, facilitando la construcción de IActionResult personalizados.

También existe la posibilidad de retornar directamente objetos desde las acciones, una solución bastante útil cuando implementamos una API como la siguiente:
[HttpGet("users/{id}")]
public User Get(int id)
{
var user = _userRepository.GetById(id);
return user;
}
Observa que la gran ventaja de este último enfoque es que la acción muestra claramente el tipo de objeto retornado, por lo que, además de aumentar su legibilidad, herramientas como Swagger podrían generar documentación y clientes muy específicos.

Y por último, hace poco hablábamos de que la llegada de ASP.NET Core 2.1 venía acompañada de un nuevo tipo de resultado para las acciones MVC: la clase ActionResult<T>. Básicamente, una acción definida con un retorno ActionResult<T> devolverá o bien una instancia de ActionResult o bien una instancia de T. Esto es muy interesante para solucionar escenarios como el siguiente, que fallarían en compilación:
// Ojo, no compila!
[HttpGet("users/{id}")]
public User Get(int id)
{
var user = _userRepository.GetById(id);

if (user == null)
return NotFound();
return user;
}
Obviamente, el código anterior podríamos solucionarlo muy fácilmente como se muestra a continuación, pero entonces no bastaría con observar la firma de la acción para saber qué tipo de objeto es el que podemos esperar de ella:
// Compila, pero la acción no da pistas sobre el tipo de objetos que retorna
[HttpGet("users/{id}")]
public ActionResult Get(int id)
{
var user = _userRepository.GetById(id);
if (user == null)
return NotFound();
return Ok(user);
}
Y ahí es donde entra en juego el nuevo ActionResult<T>. Utilizando esta clase, la cosa quedaría bastante mejor pues tenemos la posibilidad de retornar cualquier ActionResult, pero manteniendo la claridad y capacidad de autodocumentación del método de acción:
// Compila y la acción muestra el tipo de objetos que retorna
[HttpGet("users/{id}")]
public ActionResult<User> Get(int id)
{
var user = _userRepository.GetById(id);

if (user == null)
return NotFound();
return user;
}

¿Y cómo es posible que compile? ¿Es esto magia negra? 👹

Pues aunque a primera vista pudiera parecerlo, no lo es ;) El truco está en la utilización ingeniosa del operador implicit de C# para conseguir asimilar los tipos ActionResult y T con ActionResult<T> mediante conversores implícitos.

Para los curiosos, aquí os dejo el código fuente de ActionResult<T>, así os evito ir a Github a verlo:
public sealed class ActionResult<TValue> : IConvertToActionResult
{
public ActionResult(TValue value)
{
Value = value;
}

public ActionResult(ActionResult result)
{
Result = result ?? throw new ArgumentNullException(nameof(result));
}

public ActionResult Result { get; }
public TValue Value { get; }

public static implicit operator ActionResult<TValue>(TValue value)
{
return new ActionResult<TValue>(value);
}

public static implicit operator ActionResult<TValue>(ActionResult result)
{
return new ActionResult<TValue>(result);
}

IActionResult IConvertToActionResult.Convert()
{
return Result ?? new ObjectResult(Value)
{
DeclaredType = typeof(TValue),
};
}
}
Qué bueno, ¿eh? ;)

Publicado en Variable not found.

» Leer más, comentarios, etc...

Blog Bitix

Java Magazine, la publicación gratuita sobre Java

junio 15, 2018 09:00

Java

La revista Java Magazine es una publicación bimensual gratuita que contiene artículos técnicos sobre temas muy variados relacionados con Java. Las revistas pueden consultarse directamente en la web o de forma más cómoda descargándola a nuestro ordenador en formato de archivo PDF. También es posible suscribirse a una lista de distribución para recibir mediante un correo electrónico una notificación cuando se publique un nuevo número de la revista o estar atento y seguir al twitter de la revista @Oraclejavamag. Su longitud ronda algo menos de 100 páginas por número.

Estas son las portadas de la revista de los tres últimos años y medio, desde el 2015 hasta junio del 2018, en las que se incluye los principales temas tratados en cada revista. Durante este tiempo publicando por supuesto algún número dedicado a Java 9 y Java 10, Java EE 8, JSF, JavaMail, JAX-RS, Spring Boot, JVM, JUnit, IoT, HTTP/2, JavaFX, Gradle, Lombok, JCommander, Byte Buddy, Jsoup, WebSockets, JPA, CDI, Servlet 4.0, testing, miroservicios y contenedores y muchos más temas.

Java Magazine 2018
Java Magazine 2017
Java Magazine 2016
Java Magazine 2015

La mayoría de artículos que incluyen suelen ser muy interesantes y de muy buena calidad de los que casi siempre se aprende algo, algunos artículos están escritos por personas con muy altos conocimientos sobre Java, algunos han sido galardonados como Java Champion, y en algunos otros casos son los desarrolladores principales de algunas de la librerías más populares en el ecosistema Java.

Si estás interesado en Java seguir esta revista y leer sus artículos es una buena recomendación.

» Leer más, comentarios, etc...

Adrianistán

¿Cómo funcionan los sistemas basados en inodos?

junio 14, 2018 10:18

Después de ver como funcionan de forma genérica los sistemas FAT, saltamos a los sistemas de inodos. Estos se han usado tradicionalmente en sistemas UNIX (UFS, ext2), así que tradicionalmente ha existido una cierta rivalidad  en las redes entre FAT e inodos similar a la de Windows/Linux. Lo cierto es que a nivel técnico cada uno tiene fortalezas y debilidades.

Partición

Tomando la base de FAT, una partición de un sistema basado en inodos también contiene un sector de arranque y un superbloque con metadatos. También es necesario un bloque dedicado al directorio raíz presente en el disco. Además es necesario espacio para almacenar todos los inodos y un mapa de bits de espacio libre que en FAT no hacía falta, ya que la propia tabla nos indicaba que bloques del disco estaban libres.

Los inodos

¿Qué es inodo te podrás preguntar? Es una estructura de datos, su nombre proviene de index node y es que los inodos no son más que índices, que almacenan los números de bloque de las diferentes partes del archivo. Además, contienen metadatos como permisos, propietario, tamaño, fecha de modificación, referencias, tipo de fichero (directorio, archivo, enlace duro, enlace blando,…) salvo el nombre del propio archivo, que en ningún sitio del inodo aparece.

Este sistema tiene una ventaja de rendimiento respecto a FAT en cuanto al acceso aleatorio a los archivos, ya que es mucho más rápido de esta forma que con FAT. En FAT para hacer lo mismo tenemos que ir recorriendo la tabla arriba y abajo siguiendo los números de bloque hasta encontrar el bloque deseado.

Normalmente un inodo tiene un tamaño fijo, lo que implica que el índice no se puede alargar hasta el infinito. Esto hace que haya un número limitado de bloques que puede usar un archivo y por ende, que haya un tamaño máximo de archivo que no es muy elevado. Para solventar este problema hay varias soluciones. El enfoque de UFS y de ext2/3/4 consiste en múltiples niveles de indexado progresivo.

Esto significa que los primeros bloques son números de bloque directos pero los siguientes son números de bloque que llevan a tablas de inodo secundarias que ya sí, hacen referencia al archivo real. Más adelante los números de bloque hacen referencias a tablas de inodo secundarias que a su vez llaman a tablas de inodos terciarias.

Esto provoca algo que en principio puede parecer paradójico y es que es más lento leer una zona final de un archivo que una zona del principio, aunque en una lectura secuencial no se nota demasiado.

Otra solución a esto es enlazar los inodos a modo de lista enlazada añadiendo al final de cada inodo un número de inodo al que continuar la lectura de índices.

Localización de los inodos

Los inodos se crean cuando se formatea el disco y permanecen en un estado libre. No se pueden añadir más inodos ni quitar inodos y no puede haber más archivos y directorios que inodos por esta misma razón. Esto es una desventaja respecto a FAT, ya que en FAT puede haber tantos archivos como bloques haya/entradas tenga la tabla. En sistemas de inodos como ext2/3/4 puede ocurrir que no queden inodos libres pero haya todavía bloques libres, dejando todo ese espacio inutilizado (aunque en realidad lo podrían usar los archivos existentes si creciesen).

Los inodos se pueden ubicar de dos formas distintas. Un enfoque consiste en ponerlos al principio del disco todos juntos. Otro enfoque, el que sigue ext3, consiste en dividir el disco en 4 zonas y ubicar inodos en cada inicio de zona. La idea es que los inodos de esa zona usen bloques de esa zona y de esta forma reducir los desplazamientos de las cabezas lectoras del disco (en unidades SSD esto da completamente igual como podréis suponer).

Gestión del espacio libre

Una de las grandes ventajas de FAT era que la tabla podía mantener a la vez un listado de bloques libres, listos para ser usados. Con inodos no tenemos esa facilidad y tenemos que recurrir a otros tipos de estructura. Aquí hay muchos planteamientos siendo el más común el mapa de bits. El mapa de bits es una estructura que se compone de un listado de bits. Cada bit corresponde a un bloque y dependiendo de si el bit es 1 o 0 podemos saber si el bloque está libre u ocupado.

Como veis, los sistemas basados en inodos tienen que mantener varias estructuras de datos de forma paralela. Esto aumenta las probabilidades de inconsistencias en el sistema. Por este motivo, muchos sistemas más avanzados como ext4 mantienen un journal o diario.

Fragmentación

Veamos como se comportan los sistemas de inodos ante los tres tipos de fragmentación. Respecto a la fragmentación interna, que es aquella que sucede cuando asignamos espacio de más a archivos que no necesitan tanto, tiene los mismos valores que FAT, ya que los bloques siguen pudiendo pertenecer únicamente a un archivo. Realmente, para mejorar la fragmentación interna tenemos que saltar a sistemas como XFS o JFS.

La fragmentación externa de los sistemas basados en inodos es la misma que la de FAT, 0, ya que todo se asigna mediante bloques del mismo tamaño.

Respecto a la fragmentación de datos, históricamente las implementaciones como UFS o ext2/3 se han comportado relativamente bien, aunque a nivel teórico nada impide que se comporten igual de mal que FAT, no existen mecanismos preventivos. Ext4 por contra, sí que tiene mecanismos de prevención (delayed allocation).

Directorios y enlaces duros

En los sistemas basados en inodos los directorios son más sencillos que en FAT, ya que no tienen que almacenar los metadatos del archivo en cuestión. En un fichero de directorio simplemente se almacena el nombre y el número de inodo correspondiente.

Aquí surge un concepto muy interesante, el de enlaces duros. Un enlace duro no es más que otra entrada de directorio que apunta a un mismo inodo. Realmente desde el punto de vista del sistema de archivos no existe un fichero original y su enlace. Simplemente dos entradas diferentes apuntando al mismo inodo. ¿Y cómo reacciona esto a los borrados? Imagina la siguiente situación: tengo un fichero y creo un enlace duro. Borro el fichero original y su inodo se libera. Ahora creo otro archivo y reutiliza ese mismo inodo que estaba libre. ¡Ahora el enlace duro apunta a un contenido totalmente distinto sin darse cuenta! Para prevenir esto, los inodos no se liberan hasta que no quede ninguna entrada de directorio apuntando a ellos. Para eso sirve el campo referencias dentro del inodo, para llevar la cuenta de cuántas veces se hace referencia al inodo en el sistema de archivos. Cuando el valor de referencias llega a cero, se puede liberar sin problema.

Conclusiones

Los sistemas basados en inodos son conceptualmente algo más complejos que los basados en FAT. Comparten además limitaciones comunes, usan más espacio de disco, aunque suelen ser más rápidos.

Actualmente existen sistemas basados en inodos mucho más avanzados y complejos que UFS y ext2/3/4 y que mejoran este sistema de forma sustancial. Por ejemplo en XFS los inodos tienen una estructura totalmente distinta donde se indica un bloque de inicio y una longitud en número de bloques contiguos que pertenecen al archivo. Muchos sistemas además usan estructuras de árbol B+ como pueden ser ZFS o Btrfs.

La entrada ¿Cómo funcionan los sistemas basados en inodos? se publicó primero en Adrianistán.

» Leer más, comentarios, etc...

Variable not found

Stack traces asíncronas legibles, de serie en .NET Core 2.1 (¡y también en otros frameworks!)

junio 12, 2018 09:40

ASP.NET CoreAparte de las novedades que comentamos hace una semana, ASP.NET Core 2.1 incluye algunas sorpresas adicionales que, sin ser revolucionarias, nos harán más sencillo el día a día. En esta ocasión vamos a comentar una característica introducida en .NET Core 2.1 que aporta mejoras de legibilidad en las trazas de excepciones producidas en llamadas asíncronas, algo que obtendremos for free, por el mero hecho de utilizar la última revisión del framework.

Veamos muy rápidamente en qué consiste.

Un ejemplo de traza de excepción ilegible

Imaginemos el código de una aplicación de consola tan simple como la siguiente:
class Program
{
static async Task Main(string[] args)
{
try
{
await DoSomethingAsync();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}

static async Task DoSomethingAsync()
{
await DoAnotherThingAsync();
}

private static Task DoAnotherThingAsync()
{
throw new Exception();
}
}
Si compilamos esta aplicación en .NET Core 2.0 o anteriores, o incluso en .NET Framework, y la ejecutamos, la salida por consola será más o menos la siguiente:
System.Exception: Exception of type 'System.Exception' was thrown.
at ConsoleApp_20.Program.DoAnotherThingAsync() in C:\ConsoleApp-20\Program.cs:line 29
at ConsoleApp_20.Program.<DoSomethingAsync>d__1.MoveNext() in C:\ConsoleApp-20\Program.cs:line 24
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at ConsoleApp_20.Program.<Main>d__0.MoveNext() in C:\ConsoleApp-20\Program.cs:line 12
El problema con esta traza es el ruido y la cantidad de información que muestra, que hace que no sea fácil de leer e interpretar. Aunque la línea donde se ha producido el error se puede ver con relativa claridad (línea 29, en DoAnotherThingAsync()), a partir de ahí no es sencillo ver por dónde ha ido pasando el flujo de ejecución.

Si esto es así en una aplicación de consola de pocas líneas, seguro que ya habréis sufrido lo complicado que es leerla cuando se trata de una aplicación de mayor calado, además montada sobre un framework complejo y puramente asíncrono como ASP.NET Core.

Trazas de excepciones legibles en .NET Core 2.1

Probemos ahora exactamente el mismo código, pero esta vez compilándolo y ejecutándolo sobre .NET Core 2.1. Sin hacer nada más, en esta ocasión la traza que obtenemos en pantalla es la siguiente:
System.Exception: Exception of type 'System.Exception' was thrown.
at ConsoleApp_21.Program.DoAnotherThingAsync() in C:\ConsoleApp-21\Program.cs:line 29
at ConsoleApp_21.Program.DoSomethingAsync() in C:\ConsoleApp-21\Program.cs:line 24
at ConsoleApp_21.Program.Main(String[] args) in C:\ConsoleApp-21\Program.cs:line 12
Genial, ¿eh? Ahora sí que queda perfectamente claro dónde explotó la aplicación, y la traza completa de ejecución :)
Si estáis interesados, podéis leer más sobre esta feature en el post Stacktrace improvements in .NET Core 2.1 de uno de sus desarrolladores.

Hey, ¿y no podría conseguir lo mismo en .NET Framework o versiones anteriores de .NET Core?

Pues sí :) Afortunadamente, Ben Adams, Microsoft MVP y uno de los principales contributors de ASP.NET Core, además de trabajar en varios pull requests que implementaban esta característica en .NET Core 2.1, creó hace unos meses el proyecto Ben.Demystifier, que permite “desmitificar” los stack traces ilegibles dejándolos limpios de información no esencial, como en el ejemplo que vimos antes.

Para utilizarlo, basta con instalar el paquete desde Nuget, e invocar el método Demystify() para generar una excepción con las trazas retocadas:
try
{
await DoSomethingAsync();
}
catch (Exception ex)
{
Console.WriteLine(ex.Demystify());
}
Publicado en Variable not found.

» Leer más, comentarios, etc...

Adrianistán

¿Cómo funcionan los sistemas de archivos basados en FAT?

junio 11, 2018 06:18

Voy a dedicar unas entradas en el blog a hablar del funcionamiento de los sistemas de archivos, un componente fundamental en la gran mayoría de sistemas informáticos. Voy a empezar con los basados en FAT sin centrarme en ninguno en concreto (FAT16, FAT32, exFAT,…). Intentaré escribir también sobre inodos y mecanismos más raros y avanzados.

En esencia un sistema de archivos es un método ordenado que permite guardar datos sobre un soporte físico para luego poder acceder a ellos. Históricamente ha habido muchos enfoques a este problema: los sistemas más usados usan archivos, directorios y enlaces.

Bloques y sectores: la división del disco

Esta parte es común a muchos sistemas de archivos, tanto FAT como inodos, como otros. A nivel físico los dipositivos están divididos. En el caso del disco duro, el dispositivo de almacenamiento más común, los discos se dividen en sectores de N bytes, según parámetros de la fabricación. Estos sectores cuentan con código de control de errores incorporado y todo ello es manejado por la controladora de disco que opera ajena al sistema operativo. Los sistemas de archivos dividen a su vez el disco en bloques. Es importante dejar claro que bloque no es igual a sector. El bloque es una división que hace el sistema de archivos y los sectores los hace la controladora de disco. Usualmente un bloque equivale a varios sectores, aunque la equivalencia real depende del sistema de archivos en concreto.

Algunos bloques especiales: boot y superbloque y raíz

Antes de entrar en el mecanismo específico de FAT es necesario comentar que en un disco existirán dos bloques de vital importancia ajenos a FAT. El primero es el bloque de boot o arranque (también llamado MBR). Normalmente situado en el sector 0 del disco duro, contiene código para iniciar el sistema operativo. El superbloque suele ser el bloque siguiente, contiene metadatos del sistema de archivos (por ejemplo, puede decir que usamos FAT con bloques de 32KiB, etc). Además en FAT es necesario que exista un fichero siempre, el fichero del directorio raíz.

Directorios

Los directorios o carpetas son archivos como otros cualquiera, solamente que en sus metadatos se indica que es un directorio y no un fichero corriente. Los ficheros de directorios son los encargados de almacenar los metadatos de los ficheros (paras saber si son ficheros, otros directorios, su fecha de modificación, propietario y tamaño entre otros) que se encuentran en el directorio así como una referencia al bloque.

Un fichero en FAT

Vamos al asunto. Supongamos que en un determinado bloque N tenemos un fichero. Este bloque es de 64 KiB. Si un fichero ocupa menos de 64 KiB, perfecto, todos los datos entran en el bloque. Simplemente ajustamos los metadatos de tamaño con el valor correcto y el resto del espacio que queda en el bloque queda inutilizado.

Este espacio perdido se denomina fragmentación interna y dependiendo de los datos que se almacenen en un disco duro, el porcentaje de pérdida puede ser mayor o menor. Evidentemente si tenemos bloques muy grandes y ficheros muy pequeños perderemos mucho espacio debido a la fragmentación interna. Tener bloques muy pequeños y ficheros muy grandes también es problemático pero por otros motivos.

Tipos de fragmentación

En un sistema de archivos existen 3 tipos de fragmentación: interna, externa y de datos. La interna se refiere al espacio perdido en bloques asignados a ficheros que no están llenos por completo. La externa se refiere al espacio que perdemos por no tener un espacio libre contiguo lo suficientemente grande como para guardar el fichero allí. Ningún sistema FAT o de inodos tiene fragmentación externa al usar todos bloques de tamaño predefinido. Por último la fragmentación de datos, o fragmentación a secas, se refiere a que los bloques asignados estén contiguos o no. Esto tiene implicaciones a nivel de rendimiento pero no al número de bytes que se vuelven inútiles como los otros dos tipos de fragmentación.

¿Pero qué pasa si el contenido de nuestro fichero no puede entrar en el tamaño de bloque? Aquí viene la gracia de FAT, la File-Allocation-Table. La FAT es una tabla compuesta por entradas que indican el siguiente bloque del archivo. La tabla está indexada por bloque y además de indicar cuál es el siguiente bloque del archivo también indica si el archivo acaba ahí o si ese bloque está libre y puede usarse para un archivo nuevo.

En la foto el archivo /home/user/hola.txt tiene una longitud menor al tamaño de bloque. Así que miramos en la FAT la entrada 150 y efectivamente comprobamos que no hay bloque siguiente ya que es un End-of-File.

Pongamos un ejemplo con un archivo largo. Cada celda de la tabla correspondiente al índice del bloque actual indica el siguiente bloque a leer. Estos bloques pueden estar en cualquier parte. Si en un disco duro muchos ficheros tienen los bloques muy dispersos, se dice que necesita ser desfragmentado.

Conclusiones

FAT es muy rápido si la tabla FAT consigue ser cargada en memoria, ya que obtener los datos necesarios de ella será muy rápido. Desgraciadamente, dependiendo del tamaño del disco duro y del tamaño de los bloques, esta tabla puede ocupar mucho y ser impráctico.

El mecanismo FAT como vemos es simple pero efectivo. Dispone de una fragmentación interna limitada y carece de fragmentación externa. Un inconveniente es que FAT cuando busca un archivo necesita empezar siempre desde el directorio raíz. Esto implica accesos cada vez más lentos a medida que vayamos haciendo carpetas, aunque con sistemas de caché se puede reducir.

El esquema FAT tampoco impone restricciones propiamente dichas al número archivos de un sistema ni a su tamaño. Sin embargo en la práctica suelen existir límites.

Edito: He explicado mejor que significa la fragmentación y sus tipos

La entrada ¿Cómo funcionan los sistemas de archivos basados en FAT? se publicó primero en Adrianistán.

» Leer más, comentarios, etc...

Picando Código

Lanzamiento ATuNombre.uy 2.0 👩👧👱👵

junio 11, 2018 12:00

DATA presenta una versión renovada de ATuNombre: la aplicación que señala el problema de la desigualdad de género en el nomenclator urbano:

ATuNombre.uy

Hoy les escribimos para contarles del re-lanzamiento, en una versión completamente renovada, de ATuNombre.uy. Un proyecto que toma los nombres de las calles de Montevideo, para hablar sobre la desigualdad de género.

El objetivo de este mapa es dar visibilidad a los aportes que hacen las mujeres a nuestra sociedad, promover su visibilidad y reconocimiento público a través de los nombres de calles de Montevideo. Queremos ver en qué medida estos aportes son valorados, reconocidos y qué hacían esas mujeres para que “pasen a la historia” otorgándoles el nombre de una calle, pasaje o avenida.
¿Cómo lo hicimos?

Este proyecto nace de una propuesta de DATA Uruguay, que invitó a la comunidad a desarrollar esta idea a través de nuestros encuentros regulares llamados Café de DATA (reuniones para trabajar en Datos Abiertos y herramientas sociales).

La primera versión de este sitio se comenzó a trabajar junto a un equipo voluntario (¡gracias!) el 21 de febrero de 2015 (Día Mundial de los Datos Abiertos) y en 2017, empezamos a trabajar nuevamente en renovar la propuesta a lo largo de varios encuentros y con trabajo remoto. Conocé al equipo que hizo esto posible y tené en cuenta que este proyecto va a seguir desarrollándose y mejorando.

» Leer más, comentarios, etc...

Poesía Binaria

Programación de tareas en segundo plano para nuestras aplicaciones web (Introducción. Parte 1 de 3)

junio 11, 2018 08:32

Tareas programadas en segundo plano

Las aplicaciones web se van complicando cada vez más. Es un hecho y es algo bueno. Los ordenadores tienen más potencia, y los creadores cada vez más opciones e imaginación para destacar sobre la competencia. Una práctica interesante es la posibilidad de realizar tareas en segundo plano o en un momento concreto del tiempo sin que exista una mediación por parte del usuario.

Tenemos varias posibilidades y en estos posts vamos a centrarnos en cada una de ellas. Por otro lado, aunque este post es más general e independiente del lenguaje, quiero centrarme en PHP. Por un lado, es un lenguaje con el que he trabajado mucho, por otro, aunque es posible que diferentes partes de un proyecto estén realizadas en diferentes lenguajes, y PHP no sea un lenguaje pensado a priori para esto, en ocasiones, es más fácil y barato para una empresa pequeña recolocar a alguien en otro módulo de la apliación si todo está desarrollado en el mismo lenguaje. Como quiero hacer este post más o menos completo y relatar mi experiencia en cada uno de los puntos, se me ha alargado mucho, por eso he decidido dividirlo en tres partes, incluso puede que algún ejemplo dé para una cuarta.

¿Qué podemos hacer con estas tareas?

Bueno, ¿para qué vale todo esto? Es cierto que una web que tenga sus usuarios, envíe correos y sirva cierta información al usuario no tiene por qué ejecutar nada en segundo plano. Todo se servirá y se realizará al ritmo que los usuarios lo pidan. Eso está bien, en principio. Pero claro, imaginad que la web empieza a ganar visitantes. Lo primero que puede darnos problema es el envío de correo.

Puede que si se registran 10 usuarios por hora no pase nada pero, ¿10 usuarios por segundo? A los que debemos enviar confirmaciones de registro (no son muchos), pero luego enviaremos alertas para ciertos eventos, recordatorios de contraseña, avisos de que están intentando entrar con esa cuenta de correo… en definitiva, nuestra aplicación deberá establecer varias conexiones por segundo con el sistema de correo, sistema que muchas veces es externo y seguro que se cabrea (y nos expulsa por establecer tantas conexiones). Seguro que estaría bien coger cada 10, 15 o 20 segundos, y ejecutar una tarea que envíe todos los mensajes que tenga pendiente, lo que se llama un spool de correo, incluso enviar todos los correos pendientes aprovechando una misma conexión al servidor de correo, de manera mucho más eficiente y ordenada.

Supongamos que queremos tener un control de la llegada de dichos mensajes, o al menos comprobar que el e-mail sigue siendo válido para determinar si nuestros usuarios son legítimos o tenemos algún problema con sus datos. En este caso, cuando el servidor de correo de destino está recibiendo el mail normalmente contestará. Puede que el correo se entregue bien, o puede que estemos en una lista negra, el servidor de destino puede aplicar listas grises, nuestro correo puede ser tachado como Spam, o incluso que el servidor de destino no funcione ahora mismo. En caso de fallo, nuestro servidor de correo nos enviará un mensaje con el motivo por el que no se ha podido entregar el correo. Dichos mensaje podemos leerlos periódicamente y asignar un código de estado a los mensajes que hemos enviado a modo de diagnóstico, o para hacer un reintento pasados unos minutos, desactivar usuarios, etc.

Una web actual, normalmente también descarga contenidos de otros lados, y los procesa. Puede que descarguemos tweets, una captura de una web, extraigamos una imagen o una descripción de un post, la cotización del euro o de alguna criptomoneda. Esto son tareas que no dependen de nosotros. Así que no podemos controlar que el tiempo que tardamos en ellas sea fijo, o que nos pueda echar por tierra una página. También tenemos que pensar en que si cada vez que un visitante requiere alguno de estos datos desde nuestro sitio tenemos que acceder a otro lado para sacarlo, tal vez el otro lado se harte de nosotros, o que no responda en algún momento debido a una caída. Lo que podemos hacer es acceder periódicamente a esos sitios sin que exista mediación por parte de nuestros usuarios y guardar esos contenidos en una memoria o base de datos, de modo que nosotros podamos acudir a esos datos de forma muy rápida. Nuestro dato tal vez no sea el más actualizado, en el caso de las cotizaciones, tal vez tengamos un cierto desfase (aunque podemos aplicar otras técnicas), pero si se trata de tweets, posts y demás, esos contenidos no van a variar con el tiempo.

Otro uso muy útil es la importación, exportación y cálculo de datos. En sistemas de estadística, puede que nuestra aplicación deba importar gran cantidad de datos procedente de diversas fuentes (webs externas, ficheros de usuario, APIs, etc). Esta tarea puede ser muy rápida o llegar a ser muy lenta en su ejecución. ¿Por qué no hacerla en segundo plano y avisar al usuario cuando terminemos? Por un lado, evitamos que el usuario esté eternamente esperando una respuesta (y que se estrese poniéndose a pedir los mismos datos una y otra vez), por otro, si la tarea hace un uso muy intensivo de CPU, podemos limitarlo (la tarea puede realizarse de forma más lenta, pero al menos no nos tira el servicio). Tanto si estamos introduciendo datos, generando un fichero de reporte, realizando cálculos, que también puede ser convirtiendo un vídeo u obteniendo una miniatura de un vídeo, son tareas que pueden estresar al usuario y no deberían influir en la carga de nuestras páginas.

También puede que nos interese eliminar sesiones de usuarios inactivos, invalidar o generar cachés de contenidos complicados. El objetivo es que el usuario final de nuestra página tenga que esperar lo menos posible para la realización de las tareas antes de ver un resultado en la página web. Por supuesto, luego podemos consultar por Ajax si una tarea ha finalizado para avisar al usuario o, como es el caso del envío de correos, es algo que al usuario no le importa, pero a nosotros sí.

Escalabilidad

Algo que tenemos que tener en cuenta cuando estamos inmersos en un proyecto es su escalabilidad. En un primer momento, y si no nos queda otra, cuando empezamos a tener visitas a nuestros servicios podremos optar por una escalabilidad vertical. Es decir, utilizar máquinas más potentes para servir nuestros contenidos. Aunque pronto empiezan a surgir más problemas si hacemos de esta práctica algo común. Los costes suelen dispararse, es más barato comprarse 4 ordenadores pequeños que uno cuya potencia englobe a los cuatro. Vale, no siempre es así, pero si nos ponemos a mirar presupuestos de ordenadores llega un momento en el que sí se cumple. Por otro lado, si tenemos nuestro servicio alojado únicamente en una máquina, sólo hace falta que se rompa una máquina para echar todo al traste. Parece una frase tonta pero es así, mientras que si tenemos nuestro servicio replicado en cuatro máquinas, si se rompe una nos fastidia, pero no nos pillamos los dedos. Bueno, tras todo esto tendríamos que pensar qué parte debemos escalar y cómo, porque puede que escalar toda una aplicación mastodóntica no sea la solución, debemos analizar los cuellos de botella y optar por una solución inteligente.

El caso es que muchas veces este tipo de tareas pueden pasarse a otra máquina. Es decir, nuestra máquina principal (o máquinas), como punto de entrada servirán webs y punto. Pero para las tareas programadas, que puede que algunas requieran mucha CPU vamos a utilizar otra máquina, que incluso será privada y funcionará de forma independiente, incluso si vemos que las tareas son muy exigentes podemos dividirlas en varias máquinas a nuestro gusto. De esta manera podremos hacer crecer nuestra aplicación.

Informes de errores

Aunque muchos programadores dejan esto para el final, en mi opinión es lo primero que deberíamos implementar. Cada vez que ejecutamos una de estas tareas deberíamos escribir en algún lado (base de datos, fichero log, servicio de logs externo, etc) que estamos empezando a realizar la tarea. Deberíamos escribir los errores que nos encontramos y los pasos que damos a la hora de ejecutar tareas, así como su finalización. Con esto tendremos un seguimiento que nos puede ayudar a corregir errores o saber si alguna tarea se ha quedado bloqueada. Más tarde podremos desactivar mensajes para que solo muestre inicio de tarea, fin de tarea y errores, así sabemos que nuestro programa hace su trabajo.

Tareas tras la carga de la web

Una técnica que se ha utilizado durante mucho tiempo es que tras la carga completa de la web se ejecute cierto código de mantenimiento. Es cierto que durante mucho tiempo, una vez se ha cargado la web, se comprueba si se deben realizar tareas y se realizan en caso afirmativo. Muchos sitios lo hacen cuando carga una web, otros sitios hacen una petición Ajax para ejecutar tareas… el problema es que en cualquier caso, estamos haciendo que el usuario intervenga en el disparo de dichas tareas. Eso puede causar que la página cargue más lenta, que haya demasiadas peticiones innecesarias al sistema de tareas, que si la tarea tarda mucho en ejecutarse se cancele debido a los timeouts de PHP o del servidor web, o que, si ningún usuario entra a la web en un tiempo, no se lance ninguna tarea. En cualquier caso, yo soy de la idea de que “son cosas que al usuario no le interesan“, incluso en algunos sistemas, podemos ver tras la petición (sobre todo las Ajax), si se ha ejecutado la tarea y cómo ha ido dicha ejecución, cosa que interesa mucho menos al usuario.

El origen de todo esto es porque muchos servidores (sobre todo los compartidos), históricamente no nos dejaban hacer llamadas a otras aplicaciones de gestión de tareas o incluso la creación de tareas programadas (cron jobs). Actualmente, hasta los hospedajes compartidos nos dejan hacer cosas así, de todas formas, yo te recomiendo montarte por lo menos un VPN para tener más libertar con estas cosas. Aún así, las necesidades de ejecución de tareas en nuestras aplicaciones están creciendo.

Cron jobs

Reloj
Son las tareas programadas de toda la vida, de esta forma le podemos decir al sistema ejecuta este programa cada x días, h horas y m minutos. Normalmente un servidor ejecuta muchas tareas en segundo plano como pueden ser mantenimiento de logs, sincronización del reloj, comprobación de actualizaciones, envío de informes de salud, generación de estadísticas y algunas más. Así que, ¿por qué no introducir nuestras propias tareas?

Por un lado podríamos utilizar un script para cada tarea, por ejemplo invalidación de cachés o limpieza de base de datos, consulta de webs externas y envío de e-mails incluso introducir diferente periodicidad para cada una de ellas. Pero en muchas ocasiones podemos incluso centralizarlo todo en una única llamada. Si hablamos de PHP, podremos ejecutar un único archivo PHP o Python, o Java y que éste se encargue de revisar las tareas pendientes y ejecutar las que crea pertinentes. Habrá ejemplos en PHP en el siguiente post. Incluso si lo hacemos de forma centralizada, podremos introducir tareas que solo deban lanzarse una vez en el juego. Por ejemplo cuando exista una tarea que no tiene por qué ser periódica, como puede ser la obtención de los últimos posts de un blog que acaban de introducir en nuestra web (no es periódico, se realiza cuando un usuario da de alta su blog). Luego este cron job lo podemos ejecutar cada minuto o cada dos minutos, como lo creamos necesario. Basta con ejecutar:

crontab -e

Y luego añadir
1
2
# m h  dom mon dow   command
*/2 * * * * php /ruta/del/archivo/archivo.php

Con esto ejectaremos la tarea cada dos minutos. Seguro que buscando por la red encontramos mucha información sobre los cron jobs.

En nuestro script podremos hacer una consulta a base de datos para determinar las tareas que hay pendientes (periódicas o no) y ejecutar la que toque, incluso cada tarea podrá realizar llamadas a otros procesos, y todo sin que el usuario tenga que intervenir y sin costarle tiempo a él.

Lo malo es que los cron jobs de Unix no pueden alcanzar una resolución de segundos por lo que si queremos lanzar una tarea cada segundo, cada dos, o cada quince segundos tenemos que andar ejecutando tareas con sleep delante, aunque podríamos tener problemas si una tarea tarda más de lo normal en ejecutarse.

Programador de tareas en ejecución

Otra opción es crear nosotros un programador de tareas en el lenguaje en el que estemos trabajando. Por un lado, si utilizamos una base de datos para el control de nuestras tareas no tendremos que iniciar conexiones cada vez que ejecutamos la tarea, lo que gasta tiempo y recursos, sino que podremos tener una conexión abierta todo el tiempo. Y, por otro lado, podemos mirar si tenemos una tarea pendiente cada menos tiempo, por ejemplo cada dos segundos. En este caso, si, en el caso de una tarea de envío de correo electrónico, comprobamos si tenemos que enviar e-mails cada minuto, en el peor de los casos, el mensaje tardará un minuto (más o menos en salir). Mientras que si comprobamos cada dos segundos, al usuario prácticamente le da tiempo a cambiar de ventana, acceder a su correo y comprobar si tiene algo nuevo cuando ya ha recibido el mensaje. Parece que no, pero da una mejor sensación a los usuarios.

Por otro lado, si utilizamos un programador de tareas de este estilo podremos tener varias instancias del programador corriendo en una misma máquina. En caso de que no sean tareas muy intensas computacionalmente, como son enviar e-mails, descargar información de webs externas, etc podríamos tener varias tareas en ejecución a la vez. Eso sí, deberíamos controlar muy bien que no se lance la misma tarea por dos programadores a la vez, lo que puede ser un problema. Así como determinar qué tareas pueden ejecutarse en varios sitios a la vez y cuáles no.

Como el artículo me ha quedado muy largo, he decidido dividirlo en tres. Así que para el próximo incluiré ejemplos de código fuente. La segunda parte estará disponible el día 18 de Julio.

Foto principal: unsplash-logoEstée Janssens

Foto (reloj): unsplash-logoÁlvaro Bernal

The post Programación de tareas en segundo plano para nuestras aplicaciones web (Introducción. Parte 1 de 3) appeared first on Poesía Binaria.

» Leer más, comentarios, etc...

Variable not found

Enlaces interesantes 325

junio 11, 2018 08:29

Enlaces interesantesAhí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)

.NET / .NET Core

ASP.NET / ASP.NET Core

Azure / Cloud

Data

HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

Otros

Publicado en Variable not found.

» Leer más, comentarios, etc...

Koalite

Microsoft, GitHub, unicornios y apuestas

junio 11, 2018 05:06

OJO: Éste es un post de tertuliano de medio pelo de esos que salen en televisión hablando de cosas sin tener ni idea. No le hagas mucho caso.

La noticia de la semana pasada en el mundo tecnológico fue la compra de GitHub por parte de Microsoft. A estas alturas ya no es noticia, pero éste tampoco ha sido nunca un blog de actualidad, así que no tenía ninguna prisa por escribir sobre el tema.

Las reacciones a la noticia han sido variadas y, en muchas ocasiones, claramente sesgadas en función del amor/odio de quien las emitiera hacia Microsoft. O Micro$oft, que todavía hay quien lo ve así.

Las reticencias de siempre

Entre los que se llevan las manos a la cabeza por la adquisición, encontramos los que creen que Microsoft se cargará GitHub y lo llenará de características enterprise de dudosa utilidad para integrarlo con sus otros productos. Supongo que es un temor razonable viendo la evolución que ha tenido Skype. Por lo que se ha comunicado hasta ahora, no parece que eso vaya a ocurrir y GitHub seguirá operando de forma independiente, aunque sólo el tiempo dirá como acaba la cosa.

Otros hablan de la inseguridad que genera almacenar repositorios privados a los que puede tener acceso Microsoft y robarte el código fuente. Esto no hay por donde cogerlo, la verdad. Para empezar, el valor de la mayoría del código fuente es mucho menor del que pensamos, y en los casos en que realmente es tan importante, seguramente esté alojado on-premise y no en un servicio en la nube.

Además, si Microsoft estuvieran tan interesado en alguna tecnología, probablemente le resultaría más rentable comprar la empresa entera que andar robando su código fuente. Y por último, no olvidemos que uno de los principales competidores de GitHub está alojado en Azure, por lo que si Microsoft fuese ese ente maligno robacódigo podría también obtenerlo por ahí.

Suspicacias aparte, podemos también encontrar argumentos a favor de la compra. Puestos a confiar tus repositorios a alguien, parece mejor Microsoft, que tradicionalmente ha mantenido sus productos durante mucho tiempo, que una startup de incierto futuro (y no lo digo sólo por GitHub, sino por algunas alternativas que se han planteado los que huyen ahora de allí).

El modelo actual

Con todo lo exitoso que nos pudiera parecer GitHub a nosotros como desarrolladores, desde el punto de vista de empresa su única posibilidad de supervivencia era ser comprada.

Y puestos a ser comprada, Microsoft no parece la peor opción (imagínate que hubiera sido Oracle).

Lo malo es que se han pagado por él 7.500 millones de dólares y ¿cuántas empresas pueden pagar eso? Sólo las más grandes, lo que implica que cada vez se concentran más las herramientas de desarrollo en unas pocas grandes empresas. No tengo nada en contra del capitalismo y el libre mercado, pero como usuario no me gusta que todo quede en unas pocas manos porque acaba limitando mi libertad de elección.

Uno de los motivos que intuyo influyen en todo esto es la cantidad de capital riesgo que hay en circulación. Algunos hablan de burbuja (yo no sé tanto del tema como para afirmarlo), pero lo que está claro es que hay mucho dinero en circulación para financiar startups. Muchas de estas startups tienen un modelo de negocio más que cuestionable (si es que lo tienen), y su único objetivo es “ponerse guapas” y esperar a ser compradas por una empresa grande.

Como desarrollador, esto tiene varias implicaciones directas. Por una parte, al haber más dinero en circulación es más fácil conseguir buenos sueldos, y eso obviamente se agradece, pero son sueldos que no están mantenidos por el modelo productivo de la empresa, sino por financiación externa. Si te cortan el grifo, se acabó. A esto se le une que el objetivo de la empresa es ser apetecible para que te compren, por lo que es fácil ver empresas que queman rápido la inversión externa recibida para que las curvas de crecimiento de los KPIs de turno queden bien en un Powerpoint, pero el crecimiento no es todo lo sólido, ni técnica ni económicamente, que debería.

Se podría pensar que la existencia de estas empresas que dilapidan financiación sin conseguir triunfar (o sea, ser compradas) es algo terrible para los inversores, pero lo cierto es que es algo que se da por descontado. Este tipo de inversión funciona al modo de apuestas: meto dinero en 100 startups asumiendo que voy a perder lo invertido en muchas, recuperar lo de algunas, ganar con las menos y, si tengo suerte, dar el pelotazo con algún unicornio. Mientras se mantenga el ratio de éxito los inversores estarán contentos. Otra cosa son los compradores de estos unicornios.

Eso deja en una posición complicada a las empresas que quieren crecer sin financiación externa, de una forma orgánica y sostenible, manteniéndose con sus propios beneficios. Cuando las empresas que hay a tu alrededor reciben rondas de financiación que se gastan en dos años en aras de conseguir ese crecimiento explosivo que las haga apetecibles a sus compradores, captar y retener talento no es sencillo y seguir el ritmo del mercado a nivel de sueldos se hace francamente complicado.

Por mi trabajo en el Mundo Real™ tengo contacto con bastantes startups que desarrollan productos para nuestro sector y nos plantean llegar a alianzas o integraciones con nuestos productos. Son empresas financiadas por capital riesgo, que consiguen montar equipos de desarrollo mucho más grandes que el nuestro en poco tiempo y que (parecen) avanzar muy rápido, pero en la mayoría de los casos su plan de negocio no soporta el primer contacto con el mundo real.

En definitiva (y como ya decía al principio, este no es un tema que domine) me cuesta pensar que una situación así sea sostenible. Inflar los precios de la tecnología a base de financiación externa sin un modelo de negocio real (más allá de la propia inversión y compraventa de empresas) me parece una situación arriesgada para todo el sector y, como dice David Heinemeier Hansson el mundo necesita más empresas modestas de crecimiento lineal.

Posts relacionados:

  1. Open Source en un mundo Microsoft

» Leer más, comentarios, etc...

Blog Bitix

Soporte para lanzar eventos desde JavaScript con Ajax en un componente de Apache Tapestry

junio 09, 2018 06:00

Apache Tapestry
Java

Para desarrollar aplicaciones web con el lenguaje de programación Java hay gran cantidad de frameworks que proponen un marco de trabajo para proporcionar la mayor parte de la funcionalidad de infraestructura necesaria para una aplicación. La mayoría basados en acciones y unos pocos basados en componentes. Una aplicación web no solo consta de la parte del servidor y desde hace tiempo la parte de cliente ha cobrado gran importancia.

Algunos frameworks proporcionan cierto soporte para JavaScript y recursos CSS en otros es muy escaso o inexistente. En el caso del framework Apache Tapestry en la categoría de los basados en componentes proporciona un gran soporte no solo en la parte del servidor sino también para la parte cliente.

Una de estas funcionalidades que proporciona Tapestry es poder lanzar eventos desde el cliente mediante una petición Ajax para que sean procesados en el servidor y obtener la respuesta que se devuelva desde el servidor normalmente en formato Json. Hay que definir un manejador de evento en el servidor siguiendo la convención on[Event] y en caso de querer lanzar un evento desde el cliente anotándolo con @PublishEvent.

El uso del componente en una plantilla de una página.

En el código JavaScript asociado a una página o componente hay que hacer uso del módulo que ofrece el soporte para Ajax y los eventos desde el cliente, con RequireJS se obtiene una referencia a él. Solo es necesario indicar como parámetro el nombre del evento a lanzar, los parámetros si los hubiese y los manejadores de respuesta, tanto en el caso de ser correcta que recibirá los datos devueltos en el servidor como incorrecta. En el archivo ajax.coffee están documentados todos los parámetros que posee la función ajax del módulo t5/core/ajax.

En el primer elemento del HTML se añade un atributo data-componenent-events que contiene la URL necesaria para cada evento que haya sido declarado como lanzable. A partir del elemento indicado en la opción element se busca la URL en el atributo data-componenent-events siguiendo un orden empezando por el propio elemento, en los previos al mismo nivel jerárquicamente empezando por el más cercano desde abajo hacia arriba, en los padres y finalmente en el elemento body.

Petición Ajax y atributo con la URL del evento

Esta funcionalidad se incorporó en Apache Tapestry 5.2 donde hasta entonces era necesario construir la URL del evento en el servidor con ComponentResources.createEventLink() y enviarlo al componente haciendo uso de JavaScriptSupport como se muestra en el componente Ajax que no hace uso de esta funcionalidad de eventos.

Con este soporte es algo más fácil enviar eventos y realizar peticiones Ajax desde el cliente para obtener datos.

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew run.

Portada libro: PlugIn Tapestry

Libro PlugIn Tapestry

Si te interesa Apache Tapestry descarga gratis el libro de más de 300 páginas que he escrito sobre este framework en el formato que prefieras, PlugIn Tapestry: Desarrollo de aplicaciones y páginas web con Apache Tapestry, y el código de ejemplo asociado. En el libro comento detalladamente muchos aspectos que son necesarios en una aplicación web como persistencia, pruebas unitarias y de integración, inicio rápido, seguridad, formularios, internacionalización (i18n) y localización (l10n), AJAX, ... y como abordarlos usando Apache Tapestry.


» Leer más, comentarios, etc...

Adrianistán

Bindings entre Rust y C/C++ con bindgen

junio 07, 2018 10:30

Rust es un lenguaje con muchas posibilidades pero existe mucho código ya escrito en librerías de C y C++. Código que ha llevado mucho tiempo, que ha sido probado en miles de escenarios, software maduro y que no compensa reescribir. Afortunadamente podemos reutilizar ese código en nuestras aplicaciones Rust a través de bindings. Los bindings no son más que trozos de código que sirven de pegamento entre lenguajes. Esto es algo que se ha podido hacer desde siempre pero dependiendo de la librería podía llegar a ser muy tedioso. Afortunadamente tenemos bindgen, un programa que permite generar estos bindings de forma automática analizando el código de la librería de C o C++.

En este post veremos como usar SQLite desde Rust usando bindgen.

Instalando bindgen

En primer lugar necesitamos tener instalado Clang 3.9 o superior. En Ubuntu o Debian necesitamos estos paquetes:

sudo apt install llvm-3.9-dev libclang-3.9-dev clang-3.9

Para el resto de plataformas puedes descargar el binario desde la página de descargas de LLVM.

Bindgen permite dos modos de uso: línea de comandos o desde el código Rust. El más habitual es desde código Rust pero antes veremos el modo en línea de comandos.

Modo línea de comandos

Para bindings sencillos podemos usar el modo línea de comandos. Instalamos binden con Cargo:

cargo install bindgen

Su uso es muy sencillo:

bindgen /usr/include/sqlite3.h -o sqlite.rs

Simplemente indicamos el fichero de cabecera que queremos traducir y su correspondiente fichero de salida en Rust. Este fichero será el pegamento. Vamos a crear un programa que use este pegamento:

mod sqlite;

use sqlite::{sqlite3_open, sqlite3_exec, sqlite3_close, SQLITE_OK};
use std::ffi::CString;
use std::ptr::{null_mut,null};

fn main(){
    let mut db = null_mut();
    let database_name = CString::new("test.db").unwrap().into_raw();
    let sql = CString::new("
    CREATE TABLE contacts (name TEXT, tel TEXT);
    INSERT INTO contacts VALUES ('Adrian','555-555-555');").unwrap().into_raw(); 
    let mut error_msg = null_mut();
    unsafe{
        sqlite3_open(database_name,&mut db);
        let rc = sqlite3_exec(db,sql,None,null_mut(),&mut error_msg);
        if rc != SQLITE_OK as i32 {
            let error = CString::from_raw(error_msg);
            println!("ERROR: {}",error.into_string().unwrap());
        }
        sqlite3_close(db);
    }
}

Como se puede apreciar, las llamadas al módulo de pegamento de hacen desde un bloque unsafe ya que se van a usar punteros al estilo C, de forma insegura. Hace tiempo escribí sobre ello así que voy a saltarme esa parte.

Compilamos enlazando de forma manual libsqlite3 de la siguiente forma:

rustc main.rs -lsqlite3

Si todo va bien, compilará aunque con numerosos warnings. En principio no son importantes.

Ahora si ejecutamos el programa resultante debería crear una base de datos nueva con una tabla contacts y los datos insertados.

¡Hemos conseguido llamar a una librería de C desde Rust y no hemos escrito ningún binding!

Build.rs

El sistema anterior funciona, pero no es lo más práctico, además no usa Cargo que es el sistema estándar de construcción de programas y crates un Rust. Lo habitual es dejar este proceso automatizado en el fichero build.rs que se ejecuta con Cargo.

Lo primero es añadir la siguiente línea al fichero Cargo.toml:

[build-requires]
bindgen = "0.26.3"

El siguiente paso consiste en crear un archivo cabecera de C que a su vez haga referencia a todos los archivos de cabecera que necesitamos. En el caso de SQLite es bastante simple.

#include <sqlite3.h>

Y lo llamamos wrapper.h

Ahora viene lo interesante. Dentro de build.rs creamos un programa que gracias a la API de bindgen haga lo mismo que la línea de comandos.

extern crate bindgen;

use std::env;
use std::path::PathBuf;

fn main() {
    // indicamos al linker que necesitamos sqlite3
    println!("cargo:rustc-link-lib=sqlite3");


    let bindings = bindgen::Builder::default()
        .header("wrapper.h")
        .generate()
        .expect("Unable to generate bindings");

    // escribir los bindings en $OUT_DIR/bindings.rs
    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("Couldn't write bindings!");
}

El archivo build.rs debe estar en la misma carpeta que Cargo.toml para que funcione.

Finalmente para hacer accesible nuestros bindings creamos un módulo llamado sqlite.rs con el siguiente contenido.

#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

Lo único que hace es desactivar unos warnings molestos e incluir el texto de bindings.rs al completo.

Una vez hecho esto podemos usar desde el programa principal la librería de la misma forma que hemos visto antes.

Ahora podríamos usar estos bindings directamente en nuestro programa o rustizarlos (darles una capa segura alrededor e idiomática) y subirlo a Crates.io.

El código del post está en GitHub

La entrada Bindings entre Rust y C/C++ con bindgen se publicó primero en Adrianistán.

» Leer más, comentarios, etc...

Variable not found

Novedades de ASP.NET Core 2.1

junio 07, 2018 02:07

ASP.NET CoreComo seguro sabréis, tras varias previews y release candidates, hace pocos días se lanzaron las actualizaciones de varios productos que ya llevábamos tiempo esperando:
Centrándonos en ASP.NET Core, la versión 2.1 incluye numerosas mejoras sobre su predecesora. Muchos componentes internos han sido reescritos y se han añadido características que, sin duda, continúan haciendo de ASP.NET Core un framework cada vez más completo y eficiente.

Pero eso sí, no rompe nada y todo lo que sabemos de ASP.NET Core 2.0 sigue siendo válido (de ahí que sea simplemente una revisión 2.x: no hay breaking changes).

En este post vamos a ver por encima las novedades que creo más destacadas de esta entrega.

Más rendimiento

Este ha sido uno de los grandes focos en ASP.NET 2.1, y vamos a notarlo en muchos aspectos.

Primero, la compilación de proyectos será bastante más rápida, algo que podrá apreciarse especialmente en proyectos grandes.

Pero más importante son las mejoras en tiempo de ejecución, muchas de ellas apoyadas en las novedades a este respecto introducidas en .NET Core 2.1. De hecho, ASP.NET Core está apareciendo ya en las posiciones más altas de los rankings de rendimiento.

En versiones anteriores de ASP.NET Core, libuv, una biblioteca de comunicaciones asíncronas, era el transporte utilizado por defecto por Kestrel para comunicarse con el exterior. En ASP.NET Core 2.1, este componente ha sido reemplazado por una implementación de sockets .NET; al evitar dependencias nativas, hace que el despliegue en nuevas plataformas sólo dependa de la disponibilidad de .NET Core.

Además, el equipo de ASP.NET Core ha puesto toda la carne en el asador con esta nueva implementación de sockets. Aprovechando nuevas capacidades del framework como Span<T> y afinando al máximo han conseguido un componente que es mucho más rápido que el que disfrutábamos hasta la versión 2.0.
Lamentablemente, a última hora se ha caído de esta revisión el esperado nuevo modo de funcionamiento in-process del módulo ASP.NET Core para IIS, que prometía un aumento del rendimiento de entre 4x y 6x eliminado la necesidad de conexiones en localhost entre IIS y Kestrel.
En definitiva, como podéis comprobar, mejorar el rendimiento ha sido un objetivo importante para esta revisión. Por haceros una idea, según benchmarks realizados por TechEmpower hace bien poco, ASP.NET Core es capaz de resolver más de siete millones de peticiones por segundo respondiendo texto plano:

Ranking de becnhmarks de Techempower

Hey, pero no penséis que migrando vuestra aplicación a ASP.NET Core 2.1 seréis capaces de responder peticiones a ese ritmo, ni mucho menos. Estos benchmarks se realizan en entornos muy controlados y afinando al máximo el código para conseguir este rendimiento. Por ejemplo, en este caso, la aplicación testada no usa MVC, ni siquiera routing o middlewares, lo cual hace que estas pruebas concretas sólo muestren el tope máximo al que podría llegarse en un escenario ideal (e irreal), y su posición respecto al resto de competidores en escenarios similares.
Podéis ver el código fuente de la aplicación que consigue este rendimiento en el repositorio de TechEmpower en GitHub.

Mejoras en MVC/Web APIs

El nuevo atributo [ApiController] permite identificar los controladores cuyas acciones son Web APIs para aplicarles de forma automática convenciones bastante razonables, como asumir que los objetos complejos serán bindeados desde el cuerpo de la petición, obligar a que se use rutado por atributos, o retornar un HTTP 400 cuando no se cumplan las reglas de validación establecidas.

También en esta revisión volvemos a encontrarnos con un viejo conocido: ActionResult… o más exactamente en este caso, ActionResult<T>. Una acción que retorne este tipo indica que ésta retornará o bien un IActionResult o bien un objeto de tipo T, lo cual facilita bastante su autodocumentación, sobre todo vistas a la alineación con OpenAPI, y hace su implementación más fácil de escribir y leer:
// Antes (<=2.0)
[Produces(typeof(Invoice))]
public IActionResult Get(int id)
{
var invoice = _invoiceServices.GetInvoice(id);
if (invoice == null)
return NotFound();

return Ok(invoice);
}

// Ahora (>=2.1)
public ActionResult<Invoice> Get(int id)
{
var invoice = _invoiceServices.GetInvoice(id);
if (invoice == null)
return NotFound();

return invoice;
}
Además de esto, se ha añadido soporte para el estándar Problem Details, que especifica una forma estructurada de retornar detalles de error en APIs HTTP. Para ello, se han introducido clases como ValidationProblemDetails y el método ValidationProblem() para retornar un error 400 con esta estructura de datos:
if (!ModelState.IsValid)
{
return ValidationProblem(ModelState);
}

Mejoras en Razor

Las vistas o páginas Razor pueden ser ahora compiladas y distribuidas en bibliotecas, lo que permitirá construir interfaces de usuario reutilizables. Cuando una aplicación MVC o Web Pages incorpora estas bibliotecas, las vistas que incluya serán detectadas automáticamente y utilizadas de forma normal, como si se encontraran en la carpeta /Views o /Pages. Sin embargo, pueden ser sobrescritas por las aplicaciones simplemente creando vistas con el mismo nombre en las ubicaciones habituales.

Esto, por ejemplo, se pone de manifiesto en la nueva forma de introducir en las aplicaciones creadas usando las plantillas existentes el interfaz de usuario relativo a la gestión de identidad (autenticación, perfil, etc.) usando Identity Framework. Todo el interfaz se distribuye en forma de biblioteca de clases Razor y puede ser incluido en nuestros proyectos usando los scaffolders disponibles a nivel de línea de comando o desde Visual Studio.

Por otra parte, las vistas parciales pueden incluirse ahora desde otra vista utilizando el tag helper <Partial>, que elimina problemas y dudas relativas al uso de helpers como Html.Partial(), Html.RenderPartial() y sus correspondientes versiones asíncronas.

De hecho, los problemas causados por el uso de las versiones síncronas son tan graves que, a partir de esta versión, las llamadas a Html.Partial() serán marcadas como warning, sugiriéndose que sean sustituidas por <partial> o await Html.PartialAsync():

Warning al usar Partial()

También Razor Pages ha sufrido algunas pequeñas mejoras, como el soporte para áreas, el uso de /Pages/Shared como fallback para la búsqueda de páginas, o la introducción de IPageFilter e IAsyncPageFilter para la implementación de funcionalidades transversales, tipo filtro de acción, en este tipo de componentes.

El nuevo paquete Microsoft.AspNetCore.App

Hasta ahora, el metapaquete referenciado habitualmente en los proyectos era Microsoft.AspNetCore.All, pero ahora se promoverá el uso de Microsoft.AspNetCore.App en los nuevos proyectos.

En este nuevo metapaquete han eliminado dependencias hacia proyectos de terceros como los conectores con Sqlite, Redis o algunos servicios de Azure. Por tanto, a partir de la versión 2.1, si queremos usar estos componentes tendremos que referenciar sus paquetes de forma directa.

Por cierto, el metapaquete Microsoft.AspNetCore.All seguirá existiendo durante todas las versiones 2.x, por lo que no es necesario migrarlo de momento. Pero en cualquier caso, si hubiera que hacerlo, tampoco sería demasiado complicado; podéis leer más en este issue de GitHub.

Testing end to end

Las versiones anteriores de ASP.NET Core disponían de una infraestructura muy básica para preparar tests de extremo a extremo, aunque era bastante complicado echarlo a andar con MVC.

ASP.NET Core 2.1 incluye el nuevo paquete Microsoft.AspNetCore.Mvc.Testing que añade infraestructura para la creación de pruebas end to end en memoria. Recordad que este tipo de pruebas, a diferencia de las unitarias, permiten verificar el funcionamiento del stack completo, pues consiste básicamente en simular peticiones y analizar las respuestas.
public class TestingMvcFunctionalTests : IClassFixture<WebApplicationTestFixture<Startup>>
{
public HttpClient Client { get; }

public TestingMvcFunctionalTests(WebApplicationTestFixture<Startup> fixture)
{
Client = fixture.Client;
}


[Fact]
public async Task GetHomePage()
{
// Arrange & Act
var response = await Client.GetAsync("/");

// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
}
Fijaos que no estamos hablando de hacer peticiones, sino de simularlas en memoria, por lo que todo será bastante más rápido que si tuviéramos que lanzar un servidor y realizar peticiones reales a través de red (aunque sea el loopback).

Seguridad y cumplimiento de GDPR

La seguridad es un área en continuo movimiento y, además, la llegada de GDPR ha sido uno de los grandes acontecimientos de este año. Obviamente la nueva versión de un framework moderno como ASP.NET Core no podía ignorar este hecho.
  • Los proyectos ASP.NET Core 2.1 utilizarán por defecto HTTPS (se lanzará en localhost:5001), y tendremos disponible un middleware específico para forzar la redirección HTTP->HTTPS:
    app.UseHttpsRedirection();
     
  • Asimismo, mediante otro middleware podremos utilizar HSTS (HTTP Strict Transport Security) para informar a los navegadores de nuestro uso de protocolos seguros.
     
  • Las plantillas de proyectos web incluyen un banner de aceptación de cookies que impedirá la emisión de galletitas no esenciales si no es aceptado.
     
  • La plantilla de proyecto que utiliza Identity Framework incluye de serie opciones en el perfil para descargar los datos personales y para eliminar el usuario.

¡SignalR Core!

Pues sí, amigos, por fin tenemos disponible la versión "core" del framework de servicios en tiempo real lista para utilizar en producción.

Aunque siguen existiendo hubs y otros conceptos que nos resultarán familiares, se trata de una reescritura desde cero, que no mantiene compatibilidad ningún tipo con SignalR 2.x para .NET framework. Aparte de aprovechar las ventajas de ASP.NET Core, En esta versión se han eliminado las dependencias con jQuery, se usan protocolos más eficientes, un modelo de escalado simplificado y muchas novedades más.

Podéis leer más sobre SignalR Core en la documentación oficial.

Mejoras en CLI

Aunque estrictamente hablando se trata de una novedad de .NET Core, vamos a comentar aquí también las mejoras en el interfaz de línea de comandos (CLI) porque seguro que afectará a la forma en que desarrollamos aplicaciones web.

En primer lugar, ahora vienen integrados de serie comandos que antes eran externos y debían ser instalados como paquetes NuGet en el proyecto:
  • dotnet watch, para habilitar la compilación automática ante cambios de código.
  • dotnet ef para realizar operaciones relacionadas con Entity Framework.
  • dotnet user-secrets para gestionar user secrets.
  • dotnet dev-certs para manejar certificados en la máquina local.
  • dotnet sql-cache para configurar servidores de caché distribuida.
Por otra parte, ¿habéis mirando alguna vez con envidia esos comandos basados en NodeJS que se instalan a través de npm? ¡Pues demos la bienvenida a las .NET Core Global tools!

Gracias a esta nueva característica podremos crear comandos con .NET Core y distribuirlos a través de NuGet mediante el comando dotnet tool install -g. Tras ello, lo tendremos disponibles como órdenes de línea de comandos, por ejemplo:
C:\>dotnet tool install -g dotnetsay
Puede invocar la herramienta con el comando siguiente: dotnetsay
La herramienta "dotnetsay" (versión '2.1.3') se instaló correctamente.

C:\>dotnetsay

Welcome to using a .NET Core global tool!
[...]

C:\>_
Sin duda, esto nos llevará a un interesante ecosistema de herramientas para ASP.NET Core basadas en el CLI.

Otras mejoras

Aunque no todas ellas están relacionadas directamente con ASP.NET Core, creo que es interesante al menos citar las siguientes novedades:
  • HttpClientFactory es un nuevo mecanismo para facilitar la creación y uso correcto de objetos HttpClient. A diferencia de las versiones anteriores, hace uso del inyector de dependencias para registrar y obtener clientes HTTP, implementa un nuevo concepto llamado outgoing middleware y gestiona el ciclo de vida de forma automática.
     
  • Mejoras en la forma de trabajar con objetos Option, como la posibilidad de configurarlos a través de servicios registrados en el sistema, o de implementar clases que configuren varios objetos de este tipo al mismo tiempo.
     
  • El UI de Identity Framework se puede incluir en los proyectos como biblioteca Razor UI, y modificar únicamente lo que necesitemos. Relacionado con esto, también se ha incluido un nuevo scaffolder en Visual Studio que permite añadir desde el IDE dicha biblioteca y los componentes a sobrescribir.
     
  • Se ha introducido una fórmula para especificar el nivel de compatibilidad de nuestro código con versiones de ASP.NET Core MVC. Mediante el método SetCompatibilityVersion() podemos indicar expresamente si queremos que el framework actúe conforme a una versión específica para proteger nuestra aplicación de posibles cambios futuros:
    public void ConfigureServices(IServiceCollection services)
    {
    services.AddMvc()
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    }
    Por defecto se asume que el nivel de compatibilidad de las aplicaciones que no lo indiquen es CompatibilityVersion.Version_2_0.
     
  • Se han actualizado las plantillas de proyecto web SPA para Angular, React y React con Redux.

Y esto es todo, o casi…

… o al menos lo que más me ha llamado la atención hasta el momento. Espero no haberme dejado muchas novedades interesantes por detrás, pero, si detectáis alguna que os parezca interesante comentar, no dudéis en decirlo ;)

Ah, por último, en la documentación oficial tenéis información sobre el proceso de migración desde ASP.NET Core 2.0, aunque que es bastante sencillito.

Publicado en Variable not found.

» Leer más, comentarios, etc...

Poesía Binaria

Ejecutando tareas desde consola simultáneamente con GNU Parallel

junio 05, 2018 07:56


Los ordenadores cada día son más potentes. Y una de las formas de poder ejecutar tareas de forma más rápida, es simultanearlas. Por un lado, es lógico que si una tarea está esperando un dato (ese dato puede venir del disco, del usuario por medio del teclado o el ratón, o descargándoselo de Internet), es normal que el ordenador decida ejecutar otras cosas para no perder el tiempo. Y de hecho se ha aprovechado esto para tener varios programas en ejecución, constantemente dando y quitando el acceso al procesador. Aunque, con la evolución en el mundo del hardware, ahora es común tener ordenadores que comprenden varios núcleos de procesador y son capaces de disponer de varios hilos de ejecución simultáneos. Lo que quiere decir, que pueden hacer realmente varias cosas a la vez, por lo que tiene más sentido incluso ejecutar tareas en paralelo.

Ejecución concurrente en la shell

Normalmente, si queremos ejecutar varias tareas al mismo tiempo, o concurrentes desde una terminal Unix. Basta con ejecutar un programa de la siguiente manera:

programa &
19238
… ejecuto más cosas …

Con un ampersand (&) al final. De esta forma, el terminal nos devolverá el control a nosotros como usuarios, permitiéndonos ejecutar otras cosas mientras la tarea que le hemos pedido que ejecute lo hace de fondo (en background). Es conveniente que la tarea no escriba muchas cosas en pantalla, porque si no va a ser un jaleo.
También podemos ejecutarla de forma normal, y en mitad de la ejecución pulsar Control+Z (Representado por ^Z). Control+Z enviará una señal SIGSTOP al programa en cuestión, por lo que la ejecución se detendrá, permitiéndonos ahora ejecutar otras cosas, y luego continuar la tarea anterior tanto en el frente (foreground), escribiendo fg o, de fondo (como hacíamos antes, en background), escribiendo bg.
programa
^Z
[1]+ Stopped   programa
… ejecuto más cosas …
fg
programa
… programa sigue ejecutándose …
^Z
[1]+ Stopped    programa
bg
[1]+ programa &
… puedo ejecutar lo que quiera …

Generalmente, si tenemos un ordenador con varios núcleos y capaz de ejecutar varias tareas al mismo tiempo. El núcleo del sistema operativo se encargará de repartir estas tareas entre los núcleos disponibles para que la ejecución global sea más rápida. Si tenemos 2 tareas que se ejecutan en un solo hilo y nuestro ordenador puede ejecutar dos hilos de forma simultánea, ¡vamos a ejecutar los dos al mismo tiempo y terminarán antes!
Concretamente, si tenemos dos programas:

  • pi : Calcula el número PI con 4000 decimales, y tarda 10 segundos.
  • color : Corrige el color de una imagen jpg enorme, y tarda 14 segundos.

Si ejecutamos los dos programas de forma secuencial (primero uno y luego otro), tardaremos 24 segundos en realizar las dos tareas. Pero si las ejecutamos a la vez, como nuestro procesador tiene dos núcleos y no está haciendo nada más, tardaremos en completar las dos tareas 14 segundos (lo que tarda la más lenta de las dos).

Cuando se nos queda corto…

Cuando hablamos de paralelizar tareas, podemos hacerlo en casi cualquier lenguaje o entorno. Es más, justo con la información de antes podemos aprovechar para lanzar varias tareas. Incluso con algo de programación, establecer algún que otro bloqueo para guardar el orden de las ejecuciones. O incluso para poder ejecutar las tareas de dos en dos, o de tres en tres… aunque tratándose de Bash puede ser un poco duro programar ciertas cosas, sobre todo cuando tenemos prisa.

GNU Parallel nos permite ejecutar tareas de forma simultánea, agrupar tareas, ordenar la salida, e incluso nos evita tener que pelearnos con aquellos conceptos de concurrencia que a más de uno nos han traído de cabeza. Como extras, nos permite jugar con los argumentos de entrada de los programas de forma muy divertida a la par que útil.

Instalación

GNU Parallel suele venir en los repositorios de las distribuciones de GNU/Linux más populares, así que podemos hacer:

sudo apt install parallel

En caso de Debian/Ubuntu y derivados

dnf install parallel

Si estamos en Fedora.

pacman -S parallel

El programa no tiene muchas dependencias y es totalmente multiplataforma, ya que está escrito en Perl. Podremos ejecutarlo en nuestro ordenador, un servidor, un móvil, una Raspberry PI o cualquier cacharro que corra GNU/Linux.

Es importante saber la versión que tenemos instalada. Podemos averiguarlo con:

parallel --version
GNU parallel 20161222

Aunque casi todos los ejemplos del post corren con versiones anteriores, para algunas cosas necesitaremos como mínimo la versión 20161222. Si tu distribución no la tiene por defecto, siempre podemos descargarlo de la página oficial.

Calcular el número PI

Un programa que suelo utilizar para este tipo de ejemplos es el cálculo del número Pi. Sobre todo porque es una tarea que tarda un rato y utiliza intensamente el procesador. Sólo necesitamos tener bc instalado. El script es muy sencillo y lo he llamado pi:

1
2
#!/bin/bash
echo "scale=4000; a(1)*4" | bc -l

Ahora debemos dar permiso de ejecución a dicho archivo:

chmod +x pi

Y podemos hacer una prueba sin concurrencia ni nada para ver lo que tarda en ejecutarse en nuestro ordenador:
time ./pi
3.141592653589793238462643383279502884197169399375105820974944592307\
….
66983895228684783123552658213144957685726243344189303968642624341077\
3226978028073189154411010446823252716201052652272111660396
real 0m10.800s
user 0m10.792s
sys 0m0.008s

Si quieres seguir esta guía y ves que la ejecución de pi tarda mucho, puedes bajar el número de dígitos (donde pone scale=4000, puedes bajar el número). El objetivo es no eternizarnos, pero darnos cuenta de cómo se está ejecutando todo.

Varias ejecuciones de un programa

GNU Parallel está diseñado para pasar argumentos de forma variable a cada una de las instancias que ejecutemos del programa. Siempre tendremos que pasar argumentos al programa a ejecutar aunque, en este caso, solo queremos ejecutar varias veces pi, sin argumentos. Una forma fácil es generar una secuencia de números del 1 al total de veces que queremos ejecutar el programa, lo podemos hacer con seq (por ejemplo con 10):

seq 10
1
2
3
4
5
6
7
8
9
10

Una vez que tenemos esta entrada, se la podemos pasar a GNU Parallel. En este caso, le diremos que a pi no le pase argumentos (-n0):
seq 10 | parallel -n0 ./pi

Esta ejecución tardará un tiempo. Eso sí, mientras se ejecuta, estaría bien que investigáramos un poco qué está haciendo el programa. En mi ordenador, puedo ejecutar hasta 8 hilos de forma simultánea. Eso quiere decir que, como pi es una tarea que tira mucho de CPU, ejecutar las 10 instancias del programa al mismo tiempo no haría que la ejecución global fuera más rápido que una ejecución secuencial puesto que dos procesos se pelearían por utilizar la CPU y el sistema operativo tendría que dar y quitar el paso a CPU de los procesos. La situación se agravaría si en lugar de 10 ejecuciones hiciéramos 100 o más, incluso podríamos tener otros problemas si intentamos ejecutar una tarea muy complicada demasiadas veces al mismo tiempo.

Podemos ver qué está haciendo la CPU en cada momento ejecutando top, un gestor de tareas o un monitor de CPU.
Uso de CPU
Lo que quiero que observemos es el número de procesos que estamos utilizando en cada momento. En este caso, la ejecución se hará de 8 en 8 porque GNU Parallel ha detectado que mi sistema puede correr 8 hilos simultáneamente. Podemos comprobarlo si mientras ejecutamos el comando anterior, en otro terminal ejecutamos:

pidof bc | wc -w
8

Veremos ese 8 todo el tiempo. Ya que parallel controla ese número de ejecuciones.

Alternativamente, podemos especificar nosotros cuántas ejecuciones simultáneas tendrá el programa con el argumento -j. De la siguiente manera:

seq 10 | parallel -j2 -n0 ./pi

En este caso, ejecutaremos las tareas de 2 en 2. Podemos especificar el número de tareas simultáneas que necesitemos. Incluso puede exceder el número de núcleos o hilos disponibles en nuestro sistema. El objetivo es que da igual lo que dure la ejecución de pi, mantendremos el número de ejecuciones que le digamos a parallel hasta que ya no haya más instancias por lanzar.

Cambiando los argumentos de entrada

La gracia de ejecutar tareas en paralelo es que podamos variar los argumentos de entrada de dicho programa. Es más, un programa podrá tener argumentos fijos y argumentos variables. Esto lo podemos poner en práctica ejecutando echo de forma concurrente con diferentes parámetros. Vale, ejecutar echo no es ninguna tarea intensiva, pero nos vale para ver cómo se están produciendo las ejecuciones y algunas cosas curiosas más.

Argumentos desde teclado (entrada estándar)

Una forma muy sencilla de pasar argumentos de entrada a parallel es de forma interactiva, escribiéndolos por teclado y pulsando Control+D al finalizar:

parallel echo
parallel: Warning: Input is read from the terminal. Only experts do this on purpose. Press CTRL-D to exit.
Jaén
Córdoba
Sevilla
Huelva
Cádiz
Málaga
Granada
Almería
Jaén
Córdoba
Sevilla
Huelva
Cádiz
Málaga
Granada
Almería

Argumentos desde pipe

Estos argumentos podemos pasarlos desde una pipe o desde un archivo, pero tiene que haber uno por línea. Podemos introducir las provincias en un archivo o hacer lo siguiente (con tr estamos convirtiendo los espacios en retornos de carro), además, estamos introduciendo un argumento fijo “Saludos desde”:

echo “Jaén Córdoba Sevilla Huelva Cádiz Málaga Granada Almería” | tr ‘ ‘ ‘\n’ | parallel echo “Saludos desde”
Saludos desde Jaén
Saludos desde Córdoba
Saludos desde Sevilla
Saludos desde Huelva
Saludos desde Cádiz
Saludos desde Málaga
Saludos desde Granada
Saludos desde Almería

Argumentos desde pipe en orden

Pero claro, no todos los programas aceptan los argumentos en el mismo lugar, imaginemos que queremos decir algo más tras el nombre de la provincia andaluza, para ello tendremos que introducir alguna cosa más:

echo “Jaén Córdoba Sevilla Huelva Cádiz Málaga Granada Almería” | tr ‘ ‘ ‘\n’ | parallel echo “Saludos desde” {}”, una bonita provincia andaluza.”
Saludos desde Jaén, una bonita provincia andaluza.
Saludos desde Córdoba, una bonita provincia andaluza.
Saludos desde Sevilla, una bonita provincia andaluza.
Saludos desde Huelva, una bonita provincia andaluza.
Saludos desde Cádiz, una bonita provincia andaluza.
Saludos desde Málaga, una bonita provincia andaluza.
Saludos desde Granada, una bonita provincia andaluza.
Saludos desde Almería, una bonita provincia andaluza.

Varios argumentos variables por ejecución

Ahora, vamos a crear un archivo, en el que introduciremos un número (el número de letras que tiene la provincia) y la provincia, de forma alternativa en cada línea. Lo llamaremos andalucia.txt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
4
Jaén
7
Córdoba
7
Sevilla
6
Huelva
5
Cádiz
6
Málaga
7
Granada
7
Almería

Con este archivo podríamos hacer lo siguiente:

cat andalucia.txt | parallel -n 2 echo “Dame {1} letras para {2}”
Dame 4 letras para Jaén
Dame 7 letras para Córdoba
Dame 7 letras para Sevilla
Dame 6 letras para Huelva
Dame 5 letras para Cádiz
Dame 6 letras para Málaga
Dame 7 letras para Granada
Dame 7 letras para Almería

Con -n2 estamos indicando que echo llevará dos argumentos, por lo que cada ejecución de echo tomará dos líneas del fichero como argumentos. Con {1} y {2} indicamos el número de parámetro que estamos colocando. También podríamos hacer lo siguiente:
cat andalucia.txt | parallel -n 2 echo “{2} tiene {1} letras.”
Jaén tiene 4 letras.
Córdoba tiene 7 letras.
Sevilla tiene 7 letras.
Huelva tiene 6 letras.
Cádiz tiene 5 letras.
Málaga tiene 6 letras.
Granada tiene 7 letras.
Almería tiene 7 letras.

Argumentos de parallel como argumentos del programa

Otra forma de incluir argumentos variables es la siguiente:

parallel echo ::: Ávila Burgos León Palencia Salamanca Segovia Soria Valladolid Zamora
Ávila
Burgos
León
Palencia
Salamanca
Segovia
Soria
Valladolid
Zamora

Argumentos y combinaciones

Cuando tenemos que lanzar muchas tareas, a veces es útil que los argumentos hagan combinaciones de parámetros. De esta manera no tendremos que generar un fichero que reúna las combinaciones y luego pasárselo a Parallel. Por ejemplo, si queremos hacer una operación lógica AND, podemos hacer lo siguiente:

parallel ‘echo -n {1} \&\& {2} = ; (({1}&&{2})) && echo 1 || echo 0’ ::: 0 1 ::: 0 1
0 && 0 =0
0 && 1 =0
1 && 0 =0
1 && 1 =1

O, mucho más sencillo, si queremos todas las combinaciones que podemos hacer con 3 bits, podemos hacer lo siguiente:

parallel echo ::: 0 1 ::: 0 1 ::: 0 1
0 0 0
0 0 1
0 1 0
0 1 1
1 0 0
1 0 1
1 1 0
1 1 1

Un paso más, ahora queremos averiguar con bc su valor en decimal:

parallel echo -n {1}{2}{3} = \;’echo “ibase=2;{1}{2}{3}” | bc’ ::: 0 1 ::: 0 1 ::: 0 1
000 =0
001 =1
010 =2
011 =3
100 =4
101 =5
110 =6
111 =7

Datos desde un fichero separado por columnas

Podemos utilizar un archivo cuyo contenido pueda ser determinado por columnas, como un CSV (Comma Separated Values) o TSV (Tab Separated Values). Podríamos coger el archivo de provincias y hacer lo siguiente (andalucia2.csv):

1
2
3
4
5
6
7
8
4,Jaén,jiennense
7,Córdoba,cordobés
7,Sevilla,sevillano
6,Huelva,onubense
5,Cádiz,gaditano
6,Málaga,malagueño
7,Granada,granadino
7,Almería,almeriense

En este caso descartaremos el primer valor. No tenemos por qué utilizarlo si no queremos.

parallel --colsep ‘,’ echo “Un habitante de {2} se llama {3}” :::: andalucia2.csv
Un habitante de Jaén se llama jiennense
Un habitante de Córdoba se llama cordobés
Un habitante de Sevilla se llama sevillano
Un habitante de Huelva se llama onubense
Un habitante de Cádiz se llama gaditano
Un habitante de Málaga se llama malagueño
Un habitante de Granada se llama granadino
Un habitante de Almería se llama almeriense

El orden de ejecución

Como se lanzan varios procesos a la vez y no todos tienen por qué tardar lo mismo. El orden en el que vemos los resultados puede variar. Lo podemos comprobar de la siguiente forma:

parallel sleep {}\; echo {} ::: 4 8 2 6
2
4
6
8

De esta forma hemos ejecutado 4 procesos que generarán esperas de 4, 8, 2 y 6 segundos, eso sí, la salida se irá generando por el orden en el que los procesos lleguen al echo. Aunque el orden de salida no es el orden de los argumentos que le hemos pasado. Es más, si queremos, podemos hacerlo más enrevesado:
parallel -j2 sleep {}\; echo {} ::: 4 8 2 6
4
2
8
6

En este caso ejecutamos dos hilos de forma simultánea, por lo que se lanzan los procesos que tardan 4 y 8 segundos a la vez. El de 4 segundos termina, y se lanza el de 2 (ahora están trabajando el proceso que tarda 8 (que va por su cuarto segundo) y el que tarda 2. Lógicamente acaba primero el que tarda 2 segundos y se lanza el de 6, pero al de 8 le quedan 2 segundos, por lo que acaba antes que el de 6.
El caso es que puedo forzar a parallel para que muestre la salida en el mismo orden en el que le paso los argumentos de entrada con el argumento -k:
parallel -k sleep {}\; echo {} ::: 4 8 2 6
4
8
2
6

Da igual qué hilos esté lanzando y cómo termine, la salida generada me la mostrará parallel en el orden que yo le he dicho, lo cual es útil para nuestros scripts si necesitamos controlar la salida generada.

Ejecutando funciones de Bash

Como hemos visto parallel nos permite ejecutar echo, palabra reservada de Bash, pero también es posible crear nuestras propias funciones y ejecutarlas. De esta forma, no solo podemos ejecutar un programa, podremos crear funciones y ejecutarlas también. Solo tenemos que exportarlas:

1
2
3
4
5
6
7
8
function distancia() {
  local ORIGEN="$(echo -n $1 | jq -s -R -r @uri)";
  local DESTINO="$(echo -n $2 | jq -s -R -r @uri)";
  curl -s  "http://maps.googleapis.com/maps/api/directions/json?origin=${ORIGEN}&destination=${DESTINO}&sensor=false" 2>/dev/null | tee "${ORIGEN}_${DESTINO}.txt" | jq '.routes[0].legs[0].distance.text';
}

export -f distancia
parallel echo -n "De {1} a {2} =\> " \; distancia {1} {2} ::: "La Coruna" "Lugo" "Orense" "Pontevedra" ::: "Alicante" "Castellon" "Valencia"

Con este ejemplo sacaremos muchas distancias entre ciudades con la ayuda de la API de mapas de Google. Es un ejemplo donde podemos ver que cada petición tarda un tiempo diferente, los resultados salen en orden de llegada.

Reemplazos de argumentos

Parallel permite no solo pasar los argumentos a los programas que ejecuta tal cual, sino que podemos hacer alguna operación de reemplazo por si no queremos el parámetro tal cual nos lo pasan. Algunos parámetros comunes pueden ser nombres de archivo (a los que podremos quitarle rutas o extensiones), cadenas de caracteres (que podremos recortar), o incluso extraer información de las tareas. Incluso podremos cambiar las cadenas si nos resulta incómodo utilizar estos caracteres en nuestros scripts. Esto podría ser muy extenso, así que voy a extraer algunos ejemplos rápidos:

Convertir imágenes de JPG a PNG

Con esta orden podremos buscar todas las imágenes jpg que hay en el directorio actual y convertirlas a png. Eso sí, utilizando toda la potencia de nuestro ordenador, iniciando tantos procesos convert simultáneos como núcleos tenga nuestra CPU. Podemos ver que {} muestra el argumento tal cual, pero {/.} extrae la ruta y la extensión. Si queremos investigar, {/} solo extrae la ruta y {.} solo extrae la extensión. En este caso guardamos las imágenes convertidas en el directorio convertidas.

find -maxdepth 1 -name ‘*.jpg’ | parallel convert -verbose {} convertidas/{/.}.png

Slots y tareas

Podemos saber qué número de tarea estamos ejecutando en cada momento. Es muy útil si por ejemplo cada tarea tiene que repartirse un tamaño muy grande y estas deben saber qué trozo de tarea les corresponde. Pensad en una imagen, divididla en cuadros numerados secuencialmente y cada tarea que lancemos debe atacar a uno de los cuadros. En este caso cada una de las tareas debe saber a qué cuadro tienen que ir. Por otro lado, los slots son grupos, como si fueran “cajas” en las que se ejecutan las tareas. Es decir, si vamos a ejecutar 3 tareas simultáneamente, tendremos 3 cajas y una tarea en ejecución ocupará una caja. De tal forma que nunca tendremos dos trabajos corriendo al mismo tiempo en el mismo slot. Podemos ver cómo funciona esto de los slots así:

parallel -j2 sleep {} \; echo {%} {#} --- {} ::: 5 2 1 3 4
2 2 --- 2
2 3 --- 1
1 1 --- 5
2 4 --- 3
1 5 --- 4

Reemplazo con funciones

Parallel también soporta funciones (casi todas las funciones están disponibles a partir de la versión 20161222. Por ejemplo podemos decirle a una tarea el número total de tareas que vamos a ejecutar. Aunque {= ‘$_=total_jobs($_)’ =} podemos sustituirlo por {##}:

seq 100 | parallel echo Job {#} of {= ‘$_=total_jobs($_)’ =}

Aunque algo muy útil es la implementación de condiciones dentro de la propia ejecución, de la siguiente manera:

seq 50 | parallel echo Hola Mundo {%} ‘> {= $_= $job->seq() % 2 == 0 ? “/dev/tty” : “/dev/null” =}’

Con esto le decimos que la salida se produzca por /dev/tty si el número de la tarea (job->seq()) es par (si su división por dos da resto cero), y si no, mandamos la salida a /dev/null.

Hay muchos más reemplazos

Progreso

Como siempre, el usuario se desespera, y muchas veces necesita que le demos algo de información para que no se desespere. Una buena idea es mostrar un progreso para que sepa por dónde va la ejecución. Como parallel está pensado para ejecutar muchas tareas, puede medir el progreso mirando todas aquellas tareas que han finalizado y midiendo tiempos. Podemos utilizar el mismo ejemplo de antes para convertir fotos de la siguiente manera:

find -maxdepth 1 -name ‘*.jpg’ | parallel --progress convert {} convertidas/{/.}.png

O también podemos utilizar –bar. O incluso mejor, pasarle la salida del progreso a un programa como zenity, para ver la salida de forma bonita:
find -maxdepth 1 -name ‘*.jpg’ | parallel --bar convert {} convertidas/{/.}.png 2> >(zenity --progress --auto-kill)

También podemos utilizar –eta para que nos indique el tiempo estimado de finalización de la tarea.

Muchas más opciones

Las utilidades de este programa no han terminado. El propio parallel nos permite ejecutar tareas simultáneas en servidores desde nuestra máquina cliente. Además, junto con GNU Parallel encontramos utilidades para acceso a base de datos y para la realización de operaciones. Es un programa muy flexible que nos permite realizar multitud de operaciones muy útiles y muy interesantes.

Sobre GNU Parallel

El autor nos pide que le citemmos si utilizamos este programa para una publicación. Sólo hay que mencionar al autor, además, en el enlace encontramos un manual muy completo y actualizado:

Encontramos otras formas de mención muy útiles si estamos escribiendo en LaTeX:

parallel --bibtex

Foto principal: unsplash-logoJonathan Pendleton

The post Ejecutando tareas desde consola simultáneamente con GNU Parallel appeared first on Poesía Binaria.

» Leer más, comentarios, etc...

Blog Bitix

Generador de páginas web estáticas y bitácoras Hugo

junio 05, 2018 04:30

Hugo es un de las mejores herramientas en la categoría de generadores de páginas web estáticas, también puede utilizarse para generar bitácoras. Su fácil instalación, rapidez, personalización o su gran sistema de plantillas y taxonomía son varias de sus características destacadas. Con Hugo conseguí resolver varias de las necesidades que tenía para Blog Bitix, la gestión de enlaces internos y externos, personalización de diseño y procesado de imágenes.

Hugo

En diciembre del 2013 pase de usar Blogger a usar Ocotpress junto con GitHub Pages para el hospedaje. En mayo del 2015 modifiqué mi bitácora para usar en vez de Octopress la herramienta equivalente Hugo, ambas sirven para generar sitios web estáticos formados únicamente por HTML, CSS, imágenes, documentos y JavaScript. Tanto Octopress, Jekyll como Hugo y otros como Pelican son similares, a partir de archivos normalmente en formato markdown genera el HTML de la página web o bitácora junto con los recursos estáticos que necesite. Cada una de estas opciones son adecuadas para páginas de presencia en internet y bitácoras.

Varían en el lenguaje de programación que emplean, Octopress usa Ruby y Hugo usa Go, y el formato de las plantillas que utilizan para personalizar la generación del contenido. La migración desde Blogger a Octopress que hice en 2013 me supuso una gran mejora al editar los artículos. La edición de los artículos se puede hacer con cualquier editor de textos en la computadora local en vez de usar una editor en un navegador mucho más limitado y lento, además de editar el texto la gestión de las imágenes es mucho más simple al tratarse como simples archivos que basta colocar entre el código fuente de la bitácora que usará el generador para producir la página web o bitácora y no menos importante aprovechar la corrección ortográfica de las palabras que en mi caso hago con vim, no me imagino la gran cantidad de errores que tendré en los artículos de Blogger y no me habré dado cuenta a pesar de siempre leer un par de veces cada artículo antes de pubicarlo. El cambio de Octopress a Hugo más tarde me permitió otros aspectos importantes a la hora de editar los artículos.

La web está formada por un conjunto de páginas enlazadas, la gestión de los enlaces es un aspecto muy importante en una página web y en una bitácora es igual. Si una página a la que hacíamos referencia cambia de dirección todos los enlaces que tuviésemos quedarán rotos. Hay varias posibilidades para buscar enlaces rotos, encontrados y sustituirlos usando Blogger es complicado ya que hay que usar el lento y poco cómodo editor web, usando Octopress era más sencillo haciendo una búsqueda y reemplazo en los archivos de texto en formato markdown con un buen editor de textos pero con Hugo me resulta mucho más sencillo por la forma en que he organizado el código fuente. Los enlaces comunes que utilizo en múltiples artículos los incluyo en un shortcode y otro para los propios artículos del blog y luego en la página o artículo hago referencia a ellos usando un identificador de forma que si un día cambia una página de dirección o una URL de algún artículo solo he de hacer la sustitución en un único lugar en esos shortcodes. Con Disqus externalizo los comentarios.

La gestión de las imágenes es otro aspecto que si no se organiza se acaba con un directorio sin ninguna organización donde es complicado saber que imágenes utiliza cada artículo. Los principales directorios que utilizo son dos uno para imágenes de logotipos y otro para las imágenes de cada artículo con una subcarpeta por año y otra con el identificador del artículo. Hasta hace poco usaba ImageMagick para producir las previsulizaciones de las imágenes y una reducción de las imágenes originales. Recientemente en Hugo se ha incorporado la funcionalidad de aplicar procesado a las imágenes con lo que es posible generar estas previsualizaciones y reducciones sin necesidad de perder las imágenes originales, esto tiene la ventaja de que si un tiempo después hay que hacer algún cambio a un imagen se puede utilizar la original. También podido simplificar la forma de organizar las imágenes, ahora al lado del artículo markdown en que se usan.

Tanto la gestión de enlaces como de los recursos estáticos es algo que no podía hacer con Blogger y fue uno de los principales motivos para migrar a un generador estático de páginas web. Como todo son archivos estáticos lo único que hace falta es un servidor web, hay varias opciones de hospedaje bastante baratas al no necesitar un lenguaje de programación en el servidor como PHP ni una base de datos relacional. Yo utilizo GitHub Pages que es incluso sin coste. Usando algunas de las varias formas para monetizar un blog escribiendo contenido interesante y de forma regular los ingresos por este medio cubren el coste del hospedaje y del dominio.

Con Hugo personalicé completamente el diseño y estilos de de la bitácora pero Hugo dispone de múltiples temas. En la página de documentación de Hugo está el formato a usar en las plantillas, shortcodes y partials así como las variables disponibles y funciones disponibles en las plantillas. Con la guía de inicio rápido es posible tener una página web en unos pocos minutos y la estructura básica de directorios.

Una de las características destacadas de Hugo es que es muy rápido, en unos pocos segundos es capaz de generar la página web completa a partir del código fuente de los archivos. Ya he escrito más de 300 artículos y tarda menos de 3 segundos en generar este blog. Otra que destaco es que solo se necesita un único binario con lo que se evita el infierno de dependencias que en alguna ocasión con Octopress me causó problemas. Usando el servidor web incorporado que posee es posible previsualizar en local el contenido de la página antes de publicarla.

» Leer más, comentarios, etc...

Variable not found

Enlaces interesantes 324

junio 04, 2018 07:02

Enlaces interesantesAhí van los enlaces recopilados durante la semana pasada. Espero que os resulten interesantes. :-)

.NET / .NET Core

ASP.NET / ASP.NET Core

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

Data

HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

Xamarin

Otros

Publicado en Variable not found.

» Leer más, comentarios, etc...

Blog Bitix

Actualizar las versiones de las librerías JavaScript built-in de Apache Tapestry

junio 03, 2018 06:30

Apache Tapestry
Java

El framework Apache Tapestry para el desarrollo de aplicaciones web Java basado en componentes aparte de ser un framework para el desarrollo de la capa de presentación del lado del servidor y lógica de negocio también ofrece soporte para el desarrollo de funcionalidad del lado del cliente. Incorpocopora de serie las librerías RequireJS para la gestión de módulos y dependencias de JavaScript, la popular jQuery para la manipulación de elementos del HTML y Underscore que añade algunas utilidades que no tiene el lenguaje JavaScript y Bootstrap para los estilos además de alguna otra librería JavaScript de menor relevancia que estas.

Sin embargo, las versiones de las librerías de lado del cliente que incorpora de serie son antiguas. En la versión 5.4.3 de RequireJS se incorpora la versión 2.1.17, de jQuery la versión 1.12.1 y de Underscore la versión 1.8.3 cuando en el momento de publicar este artículo sus versiones más nuevas son 2.3.5, 3.3.1 y 1.9.1 respectivamente. Dado que de Apache Tapestry no se publican versiones frecuentemente el framework no sigue el ritmo de actualizaciones más rápido de las librerías JavaScript. Pero pueden ser actualizadas sin mucho esfuerzo.

Apache Tapestry es un framework extremadamente personalizable, adaptable y extensible, prácticamente cualquier cosa interna de su funcionamiento puede ser modificada gracias a su propio gestor de dependencias o inversión de control. Las versiones de las librerías anteriores se definen en el archivo JavaScriptModule.java del código fuente de Tapestry y haciendo una contribución en el contenedor de dependencias a la configuración del servicio JavaScriptStack se pueden modificar.

Estas pocas líneas de código bastan para redefinir las versiones de las librerías.

Antes y después de actualizar las librerías JavaScript

Además, en este caso las nuevas versiones las he proporcionado gestionando las dependencias de lado del cliente con webjars que se incluyen como cualquier otra dependencia Java del proyecto. Esto permite saber qué dependencias de lado de cliente tiene el proyecto, obtener las dependencias de forma automática y actualizarlas de forma sencilla con la herramienta de construcción del proyecto como Gradle.

Al usar una versión más reciente de las librerías es importante asegurarse y revisar que todas las funcionalidades necesarias son compatibles hacia atrás. Al hacer en el caso de jQuery una actualización a una versión mayor hay que probar y leer las notas de publicación de las versiones por si hubiera un problema de compatibilidad en las funcionalidades que requiere de ella el framework Apache Tapestry.

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew run.

Portada libro: PlugIn Tapestry

Libro PlugIn Tapestry

Si te interesa Apache Tapestry descarga gratis el libro de más de 300 páginas que he escrito sobre este framework en el formato que prefieras, PlugIn Tapestry: Desarrollo de aplicaciones y páginas web con Apache Tapestry, y el código de ejemplo asociado. En el libro comento detalladamente muchos aspectos que son necesarios en una aplicación web como persistencia, pruebas unitarias y de integración, inicio rápido, seguridad, formularios, internacionalización (i18n) y localización (l10n), AJAX, ... y como abordarlos usando Apache Tapestry.


Referencia:

» Leer más, comentarios, etc...

Jesús Perales

No me funciona el depurador de Firefox

junio 02, 2018 07:48

No me funciona el depurador de Firefox

Al grano:

Se escribe en la barra navegación about:config , aceptamos el riesgo y después buscamos devtools.debugger.new-debugger-frontend , damos click derecho y cambiamos de true a false, ya podrias depurar javascript de nuevo.

Esto parece suceder debido a que se esta cambiando el depurador por uno nuevo y en Firefox edición para desarrolladores esta activado por defecto, por lo cual debemos desactivarlo y tendremos el viejo y confiable depurador, solo hasta que se encuentre mas estable, en teoría traerá muchas mejoras.

Fuentes

» Leer más, comentarios, etc...

Blog Bitix

Dependencias sobre librerías de lado de cliente con Webjars en una aplicación web Java

junio 01, 2018 07:00

Java
Webjars

Una aplicación web se compone de código de lado de servidor, en el caso de utilizar el lenguaje de programación Java de código Java normalmente utilizando algún de los muchos framework web, por otra parte se compone de código de lado de cliente con una gran variedad de librerías de JavaScript como jQuery, React, Underscore o Bootstrap para los estilos. En las aplicaciones Java las librerías de lado de servidor se gestionan como dependencias del proyecto y con herramientas como Gradle se puede automatizar el descargar la librería de repositorios como Maven Central y la versión que se necesite así como hacer sencillo actualizar a una nueva. En el caso de las librerías de lado del cliente con Webjars se consiguen los mismos beneficios.

Los webjars son librerías de extensión jar con los recursos de lado del cliente empaquetados en ellos que en el momento de ser requeridos pueden ser devueltos como un recurso estático por la aplicación, incluyen los archivos JavaScript sin minimizar y minimizados, los archivos map para depuración si minimizados están ofuscados, recursos de estilos CSS o imágenes. Se gestionan como cualquier otra dependencia del proyecto Java lo que proporciona las mismas ventajas de obtener las dependencias de forma automática y hace fácil actualizar a una nueva versión. Por si fuera poco es muy sencillo utilizar webjars, para los frameworks más populares se ofrece una pequeña guía de uso en la documentación.

Las librerías más populares de JavaScript o CSS están empaquetadas como webjars en las diferentes versiones y han sido publicadas de forma que es posible añadir la dependencia en la versión concreta que necesite la aplicación. Dado que los webjars se gestionan como una dependencia Java si estos a su vez tiene alguna dependencia sobre otra librería está se incluyen en el proyecto de forma transitiva. El contenido del webjar para jQuery es el siguiente.

En el caso del framework web Apache Tapestry basado en componentes para el desarrollo de aplicaciones web Java tan solo hay que incluir la dependencia en el proyecto y un poco de configuración en el módulo de la aplicación para el contenedor de dependencias como se indica en la guía de uso con el objetivo que los recursos de los webjars sean servidos.

Inspeccionando el código fuente de la página devuelta se observa que en el caso de Tapestry la URL generada al solicitar en un webjar es del estilo https://localhost:8443/assets/webjars/z941c28a3/requirejs/2.3.5/require.js.

Apache Tapestry con Webjars

Los webjars muy útiles para gestionar las librerías de lado cliente que hacen innecesario descargar manualmente las dependencias, automatizan la descarga, hacen muy sencillo actualizar a nuevas versiones y es muy fácil de usar al no requerir mucha confiración ni ser invasiva. Además, al estar como una dependencia en el archivo de construcción del proyecto queda indicado de forma explícita que el proyecto utiliza y necesita esa librería. Para mi son una herramienta imprescindible.

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando el comando ./gradlew run.

Portada libro: PlugIn Tapestry

Libro PlugIn Tapestry

Si te interesa Apache Tapestry descarga gratis el libro de más de 300 páginas que he escrito sobre este framework en el formato que prefieras, PlugIn Tapestry: Desarrollo de aplicaciones y páginas web con Apache Tapestry, y el código de ejemplo asociado. En el libro comento detalladamente muchos aspectos que son necesarios en una aplicación web como persistencia, pruebas unitarias y de integración, inicio rápido, seguridad, formularios, internacionalización (i18n) y localización (l10n), AJAX, ... y como abordarlos usando Apache Tapestry.


» Leer más, comentarios, etc...

Picando Código

NotPinkCon – conferencia de seguridad informática en Buenos Aires, Argentina

mayo 29, 2018 06:16

NotPinkCon es un evento de conferencias técnicas en seguridad informática, impartidas por mujeres y dirigidas a estudiantes, profesionales y entusiastas del tema (hombres y mujeres). Las charlas serán de nivel técnico variado, útiles tanto para quienes recién comienzan o aún no lo han hecho pero tienen interés en aprender, como para quienes tienen más experiencia.

Está abierto registro y call for papers en este enlace. El llamado a charlas cierra el 30 de junio, las charlas se anuncian el 10 de julio y el evento es el 24 de agosto.

Pueden seguir la conferencia en Twitter para mantenerse al tanto de las novedades: @NotPinkCon.

Fecha y hora
Viernes 24 de agosto de 2018
9:00 – 17:00 hora estándar de Argentina (Buenos Aires)

Ubicación
Mario Bravo 1050
1050 Mario Bravo
Palermo, CABA C1175
Argentina

La entrada es gratuita, y ya está abierto el registro.

NotPinkCon 2018

» Leer más, comentarios, etc...

Poesía Binaria

Ejemplo para analizar y procesar expresiones matemáticas (y más) en PHP (Parsear en PHP)

mayo 28, 2018 07:40

Parsear expresiones en PHP

Una de las mayores armas de doble filo en cuanto a los lenguajes de programación interpretados es la función o expresión eval. Esta orden nos suele permitir escribir una cadena de caracteres con un texto en el mismo lenguaje que estamos escribiendo y ejecutarla. Es decir, si nos encontramos en Python y escribimos:

1
eval("100+23")

Devolverá 123. O si estamos en PHP y hacemos:

1
2
<?php
eval("echo 100+23;");

En la cadena de caracteres que introducimos podemos utilizar variables, bucles, condiciones… vamos, todo lo que nos permite el lenguaje de programación. Lo que ponemos en la cadena se ejecutará de forma nativa en el lenguaje. Esto puede ser muy útil a veces. Pero tendremos que pensar un poco antes de utilizarlo.

Peligros de utilizar eval

Quiero hacer una introducción más o menos rápida y sin centrarme en un lenguaje de programación. En principio, hemos visto que eval() admite una cadena de caracteres como entrada. Si pensamos un poco, que esa cadena de caracteres sea fija, es decir, que en nuestro programa tuviera algo como los ejemplos de arriba no tiene mucho sentido, porque podemos prescindir de eval y todo se ejecutaría igual. Bueno, es cierto que en algunos casos concretos nos puede interesar la pequeña pérdida de tiempo que introduce eval(), aunque hay formas más elegantes de hacerlo. O incluso podemos hacer que el lenguaje que estemos utilizando no utilice el caché de la expresión que estamos escribiendo. Pero son, de hecho casos muy raros y excepcionales.

Otro de los usos es construir una cadena de caracteres nosotros mismos, en tiempo de ejecución y pasárselo a eval(). En este caso, podemos sacar de una base de datos un fragmento de código para ejecutar o incluso el usuario puede introducir una expresión en un formulario y nosotros ejecutarlo. Imaginad que le pedimos al usuario una fórmula para calcular el precio de venta de un producto y claro, al usuario no vamos a pedirle programación. Eso sí, podemos tener un grave agujero de seguridad en nuestra aplicación. Sencillamente, porque eval() va a ejecutar todo lo que nosotros le pasemos. Lo mismo hace operaciones matemáticas, que llamada a cualquier función del lenguaje y ahí está el problema, al usuario no le podemos dar tanto control. Un gran poder conlleva una gran responsabilidad y, otra cosa no, pero el usuario, de responsable tiene poco y, sobre todo si estamos hablando de una plataforma web en la que cualquiera podría explotar una vulnerabilidad, mucho más.

Incluso si se nos ocurre filtrar lo que le pasamos a eval(), acotando el número de expresiones que le pasamos y cómo se lo pasamos, no me fiaría yo mucho de que algún usuario malintencionado fuera capaz de, incluso pasando los filtros, ejecutar código malicioso. Así que, mi consejo, es que si alguna vez ejecutamos eval() sea solo para hacer pruebas, a la hora de hacer tests de nuestro programa y algún caso contado más, pero no hacerlo con código en producción.

Expresiones del usuario

Así que, ¿qué hacemos si aún así queremos que el usuario ejecute sus propias expresiones? No nos queda otra que analizar nosotros la expresión, y evaluarla. Como si estuviéramos programando un intérprete de un lenguaje de programación nosotros mismos. Así que, manos a la obra, vamos a construir un parser que analice una expresión y luego la procesaremos. Este programa tendrá algunas expresiones internas que evaluará directamente, aunque luego tendremos la opción de añadir funciones personalizadas.

Atención: El código que voy a mostrar tiene ya un tiempo, aunque para escribir el post me he asegurado de que funciona con PHP7. Está basado en un código de hack.code.it de hace mucho tiempo, retocado y con algunas mejoras por mi parte.

No pretendo crear un lenguaje de programación, solo un sistema en el que los usuarios puedan pasar de forma segura expresiones matemáticas, pueda analizarlas, evaluarlas y dar un resultado. Aunque todo se puede complicar, podemos utilizar funciones como senos, cosenos, raíces, etc, incluso funciones creadas por nosotros. Y debemos tener una manera de decirle las funciones que admitimos.

Programando…

Vale, voy a poner un montón de código por aquí, para tener funcionando esto. El código puede tener bugs, y, por supuesto podéis enviármelos para que poco a poco vayamos mejorando el programa. Yo lo he utilizado para unos pocos casos, pero realmente son muy pocos. El script soporta funciones, variables y operaciones como suma, resta, multiplicación, división y potencia.

Primero, aunque no utilizaremos ningún paquete, vamos a configurar composer para generar el autoload de los ficheros:
composer.json:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
    "name": "gasparfm/simplePHPexpr",
    "keywords": ["math", "expressions", "functions"],
    "homepage": "https://github.com/gasparfm/simplePHPexpr",
    "description": "An expression parser in PHP",
    "license": "MIT",
    "authors": [
        {
            "name": "smassey",
            "homepage": "http://codehackit.blogspot.fr/"
        },{
            "name": "Gaspar Fernández",
            "homepage": "https://gaspar.totaki.com/"
        }
    ],
    "require": {
        "php": ">=5.6"
    },
    "autoload":     {
        "psr-0": {
            "spex": "src/"
        }
    }
}

Crearemos un fichero principal (main.php) en el mismo directorio que composer.json. El esquema de archivos y directorios será el siguiente

|- composer.json
|- main.php
|- src/
| |-spex/
| | |-exceptions/
| | | |- DivisionByZeroException.php
| | | |- MaxDepthException.php
| | | |- OutOfScopeException.php
| | | |- ParseTreeNotFoundException.php
| | | |- ParsingException.php
| | | |- UnknownFunctionException.php
| | | |- UnknownTokenException.php
| | |-scopes/
| | | |- Scope.php
| | | |- FunScope.php
| | |- Parser.php
| | |- Util.php

spex/Parser.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
<?php
namespace spex;

/**
 * this model handles the tokenizing, the context stack functions, and
 * the parsing (token list to tree trans).
 * as well as an evaluate method which delegates to the global scopes evaluate.
 */


class Parser {
    protected $_content = null;
    protected $_context_stack = array();
    protected $_tree = null;
    protected $_tokens = array();
    protected $_options;

    public function __construct($options = array(), $content = null) {
        $this->_options = $options;
        if ( $content ) {
            $this->set_content( $content );
        }
    }

    /**
     * this function does some simple syntax cleaning:
     * - removes all spaces
     * - replaces '**' by '^'
     * then it runs a regex to split the contents into tokens. the set
     * of possible tokens in this case is predefined to numbers (ints of floats)
     * math operators (*, -, +, /, **, ^) and parentheses.
     */

    public function tokenize() {
        $this->_content = str_replace(array("\n","\r","\t"), '', $this->_content);
        $this->_content = preg_replace('~"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"(*SKIP)(*F)|\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'(*SKIP)(*F)|\s+~', '', $this->_content);
        $this->_content = str_replace('**', '^', $this->_content);
        $this->_content = str_replace('PI', (string)PI(), $this->_content);
        $this->_tokens = preg_split(
            '@([\d\.]+)|([a-zA-Z_]+\(|,|=|\+|\-|\*|/|\^|\(|\))@',
            $this->_content,
            null,
            PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY
        );
        return $this;
    }

    /**
     * this is the the loop that transforms the tokens array into
     * a tree structure.
     */

    public function parse() {
        # this is the global scope which will contain the entire tree
       $this->pushContext( new \spex\scopes\Scope($this->_options) );
        foreach ( $this->_tokens as $token ) {
            # get the last context model from the context stack,
            # and have it handle the next token
            $this->getContext()->handleToken( $token );
        }
        $this->_tree = $this->popContext();

        return $this;
    }

    public function evaluate() {
        if ( ! $this->_tree ) {
            throw new \spex\exceptions\ParseTreeNotFoundException();
        }
        return $this->_tree->evaluate();
    }

    /*** accessors and mutators ***/

    public function getTree() {
        return $this->_tree;
    }

    public function setContent($content = null) {
        $this->_content = $content;
        return $this;
    }

    public function getTokens() {
        return $this->_tokens;
    }


    /*******************************************************
     * the context stack functions. for the stack im using
     * an array with the functions array_push, array_pop,
     * and end to push, pop, and get the current element
     * from the stack.
     *******************************************************/


    public function pushContext(  $context ) {
        array_push( $this->_context_stack, $context );
        $this->getContext()->setBuilder( $this );
    }

    public function popContext() {
        return array_pop( $this->_context_stack );
    }

    public function getContext() {
        return end( $this->_context_stack );
    }
}

spex/Util.php
Este archivo proporciona compatibilidad con PHP5. Podemos adaptar el código para PHP7 y eliminar esta dependencia.

1
2
3
4
5
6
7
8
9
<?php
namespace spex;

class Util {
    public static function av($arr, $key, $default=null) {
        return (isset($arr[$key]))?$arr[$key]:$default;
    }

};

spex/scopes/Scope.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
<?php
namespace spex\scopes;

class Scope {
    protected $_builder = null;
    protected $_children_contexts = array();
    protected $_raw_content = array();
    protected $_operations = array();
    protected $options = array();
    protected $depth = 0;

    const T_NUMBER = 1;
    const T_OPERATOR = 2;
    const T_SCOPE_OPEN = 3;
    const T_SCOPE_CLOSE = 4;
    const T_FUNC_SCOPE_OPEN = 5;
    const T_SEPARATOR = 6;
    const T_VARIABLE = 7;
    const T_STR = 8;

    public function __construct(&$options, $depth=0) {
        $this->options = &$options;
        if (!isset($this->options['variables']))
            $this->options['variables'] = array();

        $this->depth = $depth;
        $maxdepth = \spex\Util::av($options, 'maxdepth',0);
        if ( ($maxdepth) && ($this->depth > $maxdepth) )
            throw new \spex\exceptions\MaxDepthException($maxdepth);
    }

    public function setBuilder( \spex\Parser $builder ) {
        $this->_builder = $builder;
    }

    public function __toString() {
        return implode('', $this->_raw_content);
    }

    protected function addOperation( $operation ) {
        $this->_operations[] = $operation;
    }

    protected function searchFunction ($functionName) {
        $functions = \spex\Util::av($this->options, 'functions', array());
        $func = \spex\Util::av($functions, $functionName);
        if (!$func)
            throw new \spex\exceptions\UnknownFunctionException($functionName);

        return $func;
    }

    /**
     * handle the next token from the tokenized list. example actions
     * on a token would be to add it to the current context expression list,
     * to push a new context on the the context stack, or pop a context off the
     * stack.
     */

    public function handleToken( $token ) {
        $type = null;
        $data = array();

        if ( in_array( $token, array('*','/','+','-','^','=') ) )
            $type = self::T_OPERATOR;
        if ( $token == ',' )
            $type = self::T_SEPARATOR;
        if ( $token === ')' )
            $type = self::T_SCOPE_CLOSE;
        if ( $token === '(' )
            $type = self::T_SCOPE_OPEN;
        if ( preg_match('/^([a-zA-Z_]+)\($/', $token, $matches) ) {
            $data['function'] = $matches[1];
            $type = self::T_FUNC_SCOPE_OPEN;
        }

        if ( is_null( $type ) ) {
            if ( is_numeric( $token ) ) {
                $type = self::T_NUMBER;
                $token = (float)$token;
            } elseif (preg_match('/^".*"$|^\'.*\'$/', $token)) {
                $type = self::T_STR;
            } elseif (preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $token)) {
                $type = self::T_VARIABLE;
            } else
                  echo "**".$token."**";
        }

        switch ( $type ) {
            case self::T_NUMBER:
            case self::T_OPERATOR:
                $this->_operations[] = $token;
                break;
            case self::T_STR:
                $delim = $token[0];
                $this->_operations[] = str_replace('\'.$delim, $delim, substr($token, 1, -1)) ;
                break;
            case self::T_VARIABLE:
                $this->_operations[] = array('
v', $token);
                break;
            case self::T_SEPARATOR:
                break;
            case self::T_SCOPE_OPEN:
                $this->_builder->pushContext( new namespace\Scope($this->options, $this->depth+1) );
            break;
            case self::T_FUNC_SCOPE_OPEN:
                $this->_builder->pushContext( new namespace\FunScope($this->options, $this->depth+1, $this->searchFunction($data['
function'])) );
            break;
            case self::T_SCOPE_CLOSE:
                $scope_operation = $this->_builder->popContext();
                $new_context = $this->_builder->getContext();
                if ( is_null( $scope_operation ) || ( ! $new_context ) ) {
                    # this means there are more closing parentheses than openning
                    throw new \spex\exceptions\OutOfScopeException();
                }
                $new_context->addOperation( $scope_operation );
            break;
            default:
                throw new \spex\exceptions\UnknownTokenException($token);
            break;
        }
    }

    private function isOperation($operation) {
        return ( in_array( $operation, array('
^','*','/','+','-','='), true ) );
    }

    protected function setVar($var, $value) {
        $this->options['
variables'][$var] = $value;
    }

    protected function getVar($var) {
        return \spex\Util::av($this->options['
variables'], $var, 0);
    }

    protected function getValue($val) {
        if (is_array($val)) {
            switch (\spex\Util::av($val, 0)) {
                case '
v': return $this->getVar(\spex\Util::av($val, 1));
                default:
                    throw new \spex\exceptions\UnknownValueException();
            }
        }
        return $val;
    }
    /**
     * order of operations:
     * - parentheses, these should all ready be executed before this method is called
     * - exponents, first order
     * - mult/divi, second order
     * - addi/subt, third order
     */
    protected function expressionLoop( & $operation_list ) {
        while ( list( $i, $operation ) = each ( $operation_list ) ) {
            if ( ! $this->isOperation($operation) )
                continue;
            $left =  isset( $operation_list[ $i - 1 ] ) ? $operation_list[ $i - 1 ] : null;
            $right = isset( $operation_list[ $i + 1 ] ) ? $operation_list[ $i + 1 ] : null;

            if ( (is_array($right)) && ($right[0]=='
v') )
                $right = $this->getVar($right[1]);
            if ( ($operation!='
=') && ( (is_array($left)) && ($left[0]=='v') ) )
                $left = $this->getVar($left[1]);

            if ( is_null( $right ) ) throw new \Exception('
syntax error');

            $first_order = ( in_array('
^', $operation_list, true) );
            $second_order = ( in_array('
*', $operation_list, true ) || in_array('/', $operation_list, true ) );
            $third_order = ( in_array('
-', $operation_list, true ) || in_array('+', $operation_list, true )|| in_array('=', $operation_list, true ) );
            $remove_sides = true;
            if ( $first_order ) {
                switch( $operation ) {
                    case '
^': $operation_list[ $i ] = pow( (float)$left, (float)$right ); break;
                    default: $remove_sides = false; break;
                }
            } elseif ( $second_order ) {
                switch ( $operation ) {
                    case '
*': $operation_list[ $i ] = (float)($left * $right); break;
                    case '
/':
                        if ($right==0)
                            throw new \spex\exceptions\DivisionByZeroException();
                        $operation_list[ $i ] = (float)($left / $right); break;
                    default: $remove_sides = false; break;
                }
            } elseif ( $third_order ) {
                switch ( $operation ) {
                    case '
+': $operation_list[ $i ] = (float)($left + $right);  break;
                    case '
-': $operation_list[ $i ] = (float)($left - $right);  break;
                    case '
=': $this->setVar($left[1], $right); $operation_list[$i]=$right; break;
                    default: $remove_sides = false; break;
                }
            }

            if ( $remove_sides ) {
                if (!$this->isOperation($operation_list[ $i + 1 ]))
                    unset($operation_list[ $i + 1 ]);
                unset ($operation_list[ $i - 1 ] );
                $operation_list = array_values( $operation_list );
                reset( $operation_list );
            }
        }
        if ( count( $operation_list ) === 1 ) {
            $val = end($operation_list );
            return $this->getValue($val);
        }
        return $operation_list;
    }

    # order of operations:
    # - sub scopes first
    # - multiplication, division
    # - addition, subtraction
    # evaluating all the sub scopes (recursivly):
    public function evaluate() {
        foreach ( $this->_operations as $i => $operation ) {
            if ( is_object( $operation ) ) {
                $this->_operations[ $i ] = $operation->evaluate();
            }
        }

        $operation_list = $this->_operations;

        while ( true ) {
            $operation_check = $operation_list;
            $result = $this->expressionLoop( $operation_list );

            if ( $result !== false ) return $result;
            if ( $operation_check === $operation_list ) {
                break;
            } else {
                $operation_list = array_values( $operation_list );
                reset( $operation_list );
            }
        }
        throw new \Exception('
failed... here');
    }
}

spex/scopes/FunScope.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
namespace spex\scopes;

class FunScope extends namespace\Scope {
    private $fun = null;

    public function __construct(&$options, $depth, $callable) {
        parent::__construct($options, $depth);
        $this->fun = $callable;
    }

    public function evaluate() {
        $arguments = parent::evaluate();
        return call_user_func_array($this->fun, (is_array($arguments))?$arguments:array( $arguments ) );
    }
}

spex/exceptions/UnknownFunctionException.php

1
2
3
4
5
6
7
8
9
10
<?php

namespace spex\exceptions;

class UnknownFunctionException extends \Exception
{
    function __construct($functionName) {
        parent::__construct('Unkown function '. $functionName);
    }
}

spex/exceptions/DivisionByZeroException.php
Todos los archivos de excecpción serán iguales, cambiando el nombre, el objetivo es diferenciar las excepciones para poder capturarlas.

1
2
3
4
5
6
7
<?php

namespace spex\exceptions;

class DivisionByZeroException extends \Exception
{
}

main.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?php
include('vendor/autoload.php');

$config = array(
    'maxdepth' => 2,
    'functions' => array(
        'sin' => function ($rads) { return sin(deg2rad($rads)); },
        'upper' => function ($str) { return strtoupper($str); },
        'fact' => function ($n) { $r=1; for ($i=2; $i<=$n; ++$i) $r*=$i; return $r; },
        'word' => function ($text, $nword) { $words=explode(' ', $text); return (isset($words[$nword]))?$words[$nword]:''; },
    )
);

$builder = new \spex\Parser($config);

while ( (fputs(STDOUT,'math > ')) && $e = fgets(STDIN) ) {
    if ( ! ($e = trim($e)) ) continue;
    if ( in_array( $e, array('quit','exit',':q') ) ) break;

    try {
        $result = $builder->setContent($e)->tokenize()->parse()->evaluate();
    } catch ( \spex\exceptions\UnknownTokenException $exception ) {
        echo 'unknown token exception thrown in expression: ', $e, PHP_EOL;
        echo 'token: "',$exception->getMessage(),'"',PHP_EOL;
        continue;
    } catch ( \spex\exceptions\ParseTreeNotFoundException $exception ) {
        echo 'parse tree not found (missing content): ', $e, PHP_EOL;
        continue;
    } catch ( \spex\exceptions\OutOfScopeException $exception ) {
        echo 'out of scope exception thrown in: ', $e, PHP_EOL;
        echo 'you should probably count your parentheses', PHP_EOL;
        continue;
    } catch ( \spex\exceptions\DivisionByZeroException $exception ) {
        echo 'division by zero exception thrown in: ', $e, PHP_EOL;
        continue;
    } catch ( \Exception $exception ) {
      echo 'exception thrown in ', $e, PHP_EOL;
      echo $exception->getMessage(), PHP_EOL;
      continue;
    }

    echo $result, PHP_EOL;
}

Probando el programa

Tras todo esto, podemos hacer una ejecución como esta:

php main.php
1+1
2
upper(“hola mundo”)
HOLA MUNDO
fact(10)
3628800
word(“Hola Mundo Mundial”, 1)
Mundo
sin(fact(3))
0.10452846326765

Publicación del código

Quiero publicar en GitHub el código tal y como ha quedado con algunos ejemplos prácticos más en las próximas semanas.

Foto principal: unsplash-logoChris Liverani

The post Ejemplo para analizar y procesar expresiones matemáticas (y más) en PHP (Parsear en PHP) appeared first on Poesía Binaria.

» Leer más, comentarios, etc...

Koalite

Auditoría: Quién ha hecho qué

mayo 28, 2018 05:06

Hace poco me preguntaba Alfonso Vargas en twitter sobre patrones para guardar un registro de los cambios realizados en entidades. No conozco ninguna receta mágica para hacerlo, pero sí he usado varias técnicas que a lo mejor pueden resultar útiles a alguien.

Por cierto, esto es algo que os animo a hacer: preguntar. La mayoría estamos dispuestos a ayudar en la medida de nuestras posibilidades.

El concepto de auditoría

Como hice cuando conté que toda aplicación debería tener un log, vamos a empezar por ver a qué nos referimos (o, al menos, a qué me voy a referir yo en este post) con auditoría.

Una aplicación puede guardar mucha información sobre lo que está pasando, pero no toda la información forma parte de la auditoría. La auditoría está formada por acciones de negocio. Son acciones realizadas por usuarios de la aplicación de las cuales queremos guardar una trazabilidad. Por ejemplo, conocer qué usuario activó una promoción. O cuándo se creó un nuevo producto. O quién canceló un pedido.

Aquí el concepto de negocio puede ser un tanto difuso, y dependiendo del tipo de aplicación y del tipo de usuarios que tenga, podemos auditar también aspectos como cambios en la configuración, cambios de versión, etc.

Esto dejaría fuera de la auditoría información como estadísticas de uso, errores, controles de rendimiento o, en general, cosas que interesan más a quién desarrolla la aplicación que a quien la usa.

Partiendo de esta visión de la auditoría como un registro de acciones de negocio, vamos a ver varias formas de enfocar el problema de cómo gestionarla.

No hacer nada

Suena mal, pero muchas veces nos empeñamos en hacer cosas que luego no valen de mucho.

Generar un buen registro de auditoría, que sea fácil de mantener, se consulte con comodidad y ofrezca información valiosa para el negocio no es trivial, por lo que antes de hacerlo porque sí, planteáte si te lo puedes evitar.

Generarla a mano

Sin duda es la opción más tediosa, porque implica que debemos escribir código específico para registrar cada evento de auditoría.

Depende de cómo esté estructurada la aplicación es posible que acabemos teniendo que intercalar código de auditoría con el código puro “de negocio” y se haga todo un poco más difícil de leer, mantener y testear.

Por otra parte, si estás generando la auditoría es porque se supone que es importante para el negocio, por lo que tratarla como parte integral del código de negocio tampoco parece tan mala idea.

La principal ventaja de esta técnica es que, aunque tediosa, es muy simple de implementar y ofrece el máximo control sobre qué auditamos y cómo lo hacemos. Por ejemplo, si en una operación de negocio queremos generar distintos eventos de auditoría en función del resultado o de los parámetros, podemos hacerlo fácilmente. O si necesitamos que una operación de negocio genere más de un evento de auditoría. O que lo haga sólo en determinados casos.

Además del trabajo que conlleva esta técnica, corremos el riesgo de que se nos olvide auditar determinadas acciones. A fin de cuentas, es algo manual y es fácil que cuando implementas una nueva operación se te olvide que hay que auditarla, o que al cambiar los datos generados por una acción se te olvida modificar los datos auditados.

Utilizar Event Sourcing

Si estás usando un diseño basado en Event Sourcing tienes auditoría casi gratis. O al menos es fácil tenerla si tus eventos están bien diseñados, porque deberían representar las acciones de negocio importantes para tu aplicación. Siendo realistas, más que las acciones de negocio son sus consecuencias, por lo que puede que pierdas parte del contexto.

De todas formas, tal vez eso sea demasiado optimista y haya unos cuantos eventos que no son interesantes para la auditoría, pero es relativamente fácil poder marcar de alguna forma aquellos eventos que sí lo son y hacer que pasen a formar parte de la auditoría.

Esta opción suena muy bien, pero su pega principal es que utilizar Event Sourcing introduce una complejidad muy grande en el sistema y no creo que compense introducirlo sólo para facilitar la parte de auditoría.

Utilizar triggers en base de datos

Es una técnica que tradicionalmente se ha usado mucho. Si estás usando una base de datos y la base de datos que estás utilizando permite crear triggers, puedes utilizarlos para detectar los cambios que se van produciendo en las tablas y actuar en consecuencia.

Es fácil construir unos preciosos triggers genéricos que registren todos los cambios a nivel de base de datos, marcando qué usuario modifica cada vez qué tabla e incluso registrando los cambios concretos en cada tabla.

El problema de esta aproximación es que si no haces triggers muy específicos (y te acercas entonces más al caso de la auditoría manual), la información que se va a guardar será demasiado genérica y difícilmente se pueda considerar operaciones de negocio porque tendrán poco valor semántico.

Apoyarse en eventos del ORM

Si subes un poco el nivel de abstracción, en lugar de utilizar triggers en base de datos puedes usar su equivalente en capa de aplicación, que serían los eventos disparados por todos los ORMs (o por la capa de acceso a datos que estés usando).

Esta forma de auditar tiene una ventaja importante sobre hacerlo a nivel de base de datos, y es que en lugar de trabajar con tablas podemos trabajar con entidades. Eso nos aisla (parcialmente) de la estructura de datos y ayuda (probablemente) a que los eventos de auditoría que registramos tengan mayor valor de negocio.

Aun así, sigue sin ser una auditoría tan semántica como en el caso de hacerla a mano y dependemos de que la capa de acceso a datos dispare los eventos necesarios para engancharnos y poder auditar.

Aprovechar los puntos de entrada de un API

Subiendo todavía más el nivel de abstracción, podemos pasar de basarnos en las entidades a basarnos en los puntos de entrada que tengamos en el API de la aplicación. No hace falta que estemos hablando de un API web, tal vez tengas una capa de servicios de aplicación, o de casos de uso, o un mediador gestionando peticiones y respuestas.

Lo bueno de aprovechar este punto para auditar es que se supone que las acciones del API representan operaciones completas contra su tu modelo, por lo que debería corresponderse bastante bien con las acciones de negocio que quieres auditar. Puede que no todas, pero si la mayoría. Mientras que en el caso de Event Sourcing estábamos enganchándonos al resultado de las operaciones, aquí nos podemos enganchar al comando que desencadena la operación.

Si sólo te limitas a auditar en los puntos de entrada puedes perder granularidad, por ejemplo si de una operación necesitas auditar varias cosas, pero podrías mezclar esta aproximación con la de auditoría manual que vimos al principio.

En el caso de que tu API esté tremendamente bien diseñada, podrías incluso utilizar algún sistema declarativo para definir lo que hay que auditar en cada punto de entrada, y usar alguna técnica (decoradores, AOP o similar) para realizar la auditaría. Desgraciadamente, muchas veces no es viable automatizar tanto y la tarea es más manual de lo que nos gustaría.

Dónde almacenar la auditoría

Algunas de las estrategias que he comentado te llevan a almacenar la auditoría junto a los datos de la aplicación, como el caso de los triggers en base de datos, pero en muchas ocasiones en posible utilizar otros tipos de almacenamiento.

Para elegir uno y otro entran en juego varios aspectos.

Si la información que vas a auditar tiene una estructura poco homogénea, utilizar un sistema que permita almacenar documentos (JSON, XML, o lo que sea) puede suponer ventajas frente a una base de datos relacional.

Sin embargo, antes de decidir usar otro sistema de persistencia para la auditoría debes tener en cuenta el nivel de transaccionalidad que necesitas. Si para ti es crítico garantizar que siempre que se produce algo en tu aplicación queda auditado, utilizar dos sistemas de persistencia diferentes te lleva al maravilloso mundo de las transacciones distribuidas, que es un mundo en el que es preferible no estar.

Implicaciones legales

Un último aspecto que me gustaría señalar en esto de la auditoría son las implicaciones legales que puede tener. No soy abogado y no tengo mucha idea sobre el tema, pero hay dos factores a considerar.

En primer lugar, puede haber escenarios en los que la auditoría sea crítica desde un punto de vista normativo/legal. Si desarrollas un software para empresas de control de plagas (por poner un ejemplo), legalmente necesitas tener un control de quién ha usado qué venenos, cuándo y dónde. En estos casos necesitas tratar la auditoría como una parte fundamental del proceso de negocio y asegurarte de que se genera y almacena correctamente (cuidado entonces si tenías pensado tratarla como un subproducto almacenado en mongodb).

Por otra parte, con las normativas de protección de datos cada vez más complejas que existen, tu sistema de auditoría debería poder permitirte eliminar toda la información relativa a un usuario. Esto, dependiendo del tipo de almacenamiento que hayas elegido, puede ser muy sencillo (sobreescribir los datos de usuario de una tabla User) o terriblemente complejo (reescribir eventos en un stream con millones de operaciones).

Posts relacionados:

  1. ¿De quién son las herramientas de desarrollo?
  2. Cuánto daño han hecho… Los generadores de aplicaciones
  3. Cuánto daño han hecho… los arquitectos de software

» Leer más, comentarios, etc...

Adrianistán

Ayuda a la ciencia desde tu casa con BOINC

mayo 26, 2018 05:17

El invento de los ordenadores se lo debemos a los científicos, que buscando maneras de acelerar sus cálculos decidieron crear máquinas que los automatizaban. A partir de esos desarrollos la informática se ha diversificado en muchas más ramas, sin embargo la necesidad de procesar grandes cantidades de datos sigue siendo de vital importancia para la investigación en física, química, medicina y demás ramas de la ciencia.

El ENIAC, uno de los primeros ordenadores construidos

Este tipo de cálculos se realizan normalmente en supercomputadoras en clúster que trabajan en paralelo. Sin embargo en muchas ocasiones, nada impide que cada nodo esté separado kilómetros de distancia. Ahí nace el proyecto BOINC (siglas de Berkeley Open Infrastructure for Network Computing). BOINC es un sistema de computación distribuida en Grid. Esto quiere decir que el cómputo necesario para un proyecto se divide en paquetes. Cada paquete se manda a un ordenador que forma parte de la red. Cada ordenador se encarga de procesarlo. Cuando los cálculos han finalizado, los envía al servidor central que le entrega a su vez al ordenador un paquete con cómputos nuevos por realizar.

De todos los sistemas de computación distribuida en Grid, BOINC es sin duda el más popular. La potencia de cálculo de BOINC es de media 23 petaflops, algo lejos del mayor supercomputador (Sunway TaihuLight, de China, con 93 petaflops) pero superior a muchos supercomptadores como MareNostrum, el mayor supercomputador de España y que tiene 11 petaflops.

Supercomputador Mare Nostrum 4

Cualquier ordenador del mundo puede colaborar con la red BOINC aportando su potencia a la red y calculando datos muy importantes para las investigaciones científicas.

¿Cómo puedo usar BOINC y ayudar?

Es muy sencillo. BOINC dispone de versiones para Windows, Mac, Linux y Android, así como versiones optimizadas con CUDA (Nvidia), OpenCL y existen versiones de terceros para Solaris, HP-UX, FreeBSD y otros sistemas.

El primer paso es descargar BOINC desde https://boinc.berkeley.edu/ . Si estás en Android puedes ir directamente a Google Play y descargar BOINC desde allí.

El cliente BOINC se divide en dos partes: una que se dedica a calcular y otra que es una interfaz de usuario que nos permite controlar la parte que calcula (BOINC Manager). La ventaja de esto es que son programas independientes y no hace falta tener la interfaz de usuario abierta para seguir calculando.

Proyectos

Lo primero que tenemos que hacer nada más abrir el BOINC Manager es registrarnos en un proyecto para que nos empiece a mandar tareas.

Algunos de los más populares son:

  • SETI@Home. Es el proyecto que busca vida extreterrestre por el universo analizando las señales de radiotelescopios como el de Arecibo. Fue uno de los primeros proyectos de BOINC y uno de los más populares.
  • Rosetta@Home: Este proyecto busca nuevos tipos de proteínas así como su aplicación en enfermedades como la malaria y el alzheimer,.
  • Einstein@Home: Este proyecto busca detectar ondas gravitacionales desde los datos recogidos de observatorios como LIGO.
  • PrimeGrid: Busca nuevos números primos, esenciales en campos como la de la criptografía.
  • ClimatePrediction: Busca modelos climáticos mejorados que permitan mejorar nuestra capacidad de predicción del clima
  • MilkyWay@Home: Investiga modelos tridimensionales sobre nuestra galaxia, la vía láctea
  • LHC@Home: Tareas relacionadas con la investigación en física de partículas con datos generados por el LHC.
  • Asteroids@Home: Amplía nuestro conocimiento sobre los billones de asteroides que hay en el universo, analizando los que podemos ver

Para registrarnos indicamos nuestro correo y nuestra contraseña y automáticamente todo empezará a funcionar. ¡No hay que hacer nada más! BOINC se encargará de todo automáticamente.

Desde la interfaz podemos parar los cálculos en cualquier momento pulsando Suspend o Pausar.

BOINC Credits

En recompensa a nuestro trabajo se nos dan BOINC Credits. La cantidad de BOINC Credits es proporcional a los gigaflops que hayamos aportado. Estos créditos no sirven realmente para nada. Únicamente para fardar ante tus amigos. Y es que hay gente realmente picada y existen equipos de gente que se unen para superar a otros equipos. Los BOINC Credits también sirven para realizar benchmarks de los ordenadores. Puedes consultar tus BOINC Credits desde BOINC Manager o desde webs externas como BOINC Stats.

Mis estadísticas en BOINC

La entrada Ayuda a la ciencia desde tu casa con BOINC se publicó primero en Adrianistán.

» Leer más, comentarios, etc...

Picando Código

Humble Book Bundle: libros digitales sobre Desarrollo y Diseño Web por O’Reilly

mayo 21, 2018 08:30

Nuevo paquete de libros digitales sobre Diseño y Desarrollo Web por O’Reilly. Paga el precio que elijas por hasta 15 libros sobre JavaScript, CSS, HTTP/2, SVG y más.

Humble Book Bundle: Web Design & Development

Pagando el monto mínimo de USD 1, obtenemos: Programming Voice Interfaces, Mobile App Development with Ionic, JSON at Work, Refactoring JavaScript y CSS Refactoring.

Por USD 8 o más, obtenemos también: Interactive Data Visualization for the Web, Learning React Native, Learning HTTP/2, SVG Animations y Working with Static Sites.

Por último, pagando USD 15 o más, el paquete incluye React Native Cookbook, CSS: The Definitive Guide, Using SVG with CSS3 and HTML5, Learning React y High Performance Images.

Los ebooks están disponibles en PDF, ePUB y MOBI, por lo que pueden ser leídos en casi cualquier dispositivo. Como se acostumbra en los paquetes Humble Bundle, además de elegir el precio, podemos elegir dónde destinamos el dinero que pagamos, incluyendo una organización de beneficencia. En este caso se trata de Code for America, una fundación sin fines de lucro que trabaja con sociedad civil y el gobierno para mejorar las comunidades.

Visita Humble Book Bundle para comprar el paquete de libros

Humble Book Bundle: Web Design &amp; Development

» Leer más, comentarios, etc...

Meta-Info

¿Que es?

Planeta Código es un agregador de weblogs sobre programación y desarrollo en castellano. Si eres lector te permite seguirlos de modo cómodo en esta misma página o mediante el fichero de subscripción.

rss subscripción

Sponsors

Puedes utilizar las siguientes imagenes para enlazar PlanetaCodigo:
planetacodigo

planetacodigo

Si tienes un weblog de programación y quieres ser añadido aquí, envíame un email solicitándolo.

Idea: Juanjo Navarro

Diseño: Albin