Weblogs Código

Picando Código

PyCon Latam 2019 – La conferencia de Python en América Latina

junio 18, 2019 06:40

PyCon Latam es una versión de la conferencia PyCon que tiene como objetivo reunir a los desarrolladores de Python de todos los países latinoamericanos, y servir como una plataforma para que interactúen con la comunidad de Python en general. Esta conferencia de 3 días comienza el 29 de Agosto y termina el 31 en Puerto Vallarta, México.

PyCon Latam 2019

Agenda

Está enfocada en que los conferencistas principales representen las diferentes áreas de la industria, así como diferentes partes de la sociedad. Desde nacionalidad y género hasta experiencia en la industria, esperan que al compartir su experiencia partícular concienticen, generen empatía y provean una base que resulte familiar al contexto.

Por ahora hay 3 oradores anunciados:

Lorena Mesa – Politóloga convertida en programadora, Lorena Mesa es ingeniera de datos en el equipo de sistemas de inteligencia de software de GitHub, Directora en la Python Software Foundation, y co-organizadora de PyLadies Chicago.

Manuel Kaufmann – En estos años, Manuel contribuyó a la traducción de la Guía Oficial del Libro de Django y los tutoriales de Django Girls, al proyecto de One Laptop Per Child, entre otros. Actualmente, Manuel trabaja en Read the Docs como desarrollador de software.

Tania Allard – developer advocate en Microsoft con un enfoque en la ciencia de datos y todo lo relacionado con la informática científica de código abierto. También es miembro de la RSE del Reino Unido y de la asociación Python del Reino Unido y fundadora y organizadora de PyLadies NorthWest UK.

Becas

Como parte de su compromiso con la comunidad de Python, la organización ofrece becas especiales para las personas que necesitan ayuda financiera para asistir a PyCon Latam. Pueden ver más información en este enlace, y empresas que quieran patrocinar las becas también contactarse para apoyar la iniciativa.

Lugar y entradas

La conferencia se va a realizar en el centro de convenciones del Hotel Friendly Vallarta. Están a la venta las entradas con un precio promedio todo incluido de menos de $300 USD por persona para toda la estadía: Sala, boleto de conferencia, comida, café, bebidas y botín de PyLatam, y se pueden elegir habitaciones singles, dobles, triples y cuádruples.

Con un enfoque todo incluido, no tendrá que preocuparse por pagar más por la comida o las bebidas. Sin embargo, se suspenderá el consumo de alcohol durante la conferencia (por obvias razones), pero la fiesta se reanuda al final de cada día! 🍹

Se ubica tan solo a:

  • 10 minutos del Aeropuerto Internacional.
  • 15 minutos del Centro Internacional de Convenciones.
  • 5 minutos del Centro de Puerto Vallarta y el malecón.

Por más información pueden visitar el sitio web de PyCon Latam o seguirlos en Twitter: @PyLatam.

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

Variable not found

Etiquetado de consultas en Entity Framework Core

junio 18, 2019 06:10

Entity Framework CoreComo sabemos, las consultas que definimos mediante LINQ contra conjuntos de datos de Entity Framework son traducidas automáticamente a queries SQL, que es lo que finalmente ejecuta el servidor de base de datos.

Muchas veces estas sentencias SQL generadas de forma automática y ejecutadas al servidor son fáciles de leer y entender, pero hay veces que EF traduce el LINQ a consultas SQL enormes, complejas, con escasa legibilidad y difícilmente reconocibles.

Seguro que alguna vez habéis tenido por delante una de estas complejas sentencias SQL generada por Entity Framework y os hubiera gustado saber en qué punto del código fue lanzada. Esto es muy frecuente, por ejemplo, cuando estamos monitorizando las consultas en ejecución con SQL Profiler, o al examinar las queries que consumen mayor número de recursos desde los paneles de Azure SQL.

En versiones "clásicas" de Entity Framework había que ingeniárselas para conseguirlo, pero, como podréis comprobar a continuación, en EF Core la cosa se ha simplificado bastante :)

Etiquetando consultas

Cuando creamos consultas usando SQL a pelo, es bastante sencillo incluirles una marca que, a posteriori, nos ayude a identificar su origen o finalidad. Basta con insertar un comentario como en el siguiente ejemplo:
var sql = @"
-- Get top 10 older friends with country

SELECT TOP 10 [friend].[Name] AS [FriendName], [friend].[Age],
[friend.Country].[Name] AS [CountryName]
FROM [Friends] AS [friend]
LEFT JOIN [Countries] AS [friend.Country] ON [friend].[CountryId] = [friend.Country].[Id]
ORDER BY [friend].[Age] DESC";

context.Database.ExecuteSqlCommand(sql);
De esta forma, cuando veamos aparecer dicha consulta en los logs del servidor, podremos identificar rápidamente de qué se trata.

Pues bien, la solución planteada por Entity Framework Core 2.2 es igualmente sencilla, o incluso más ;) Basta con introducir una llamada a TagWith() en la especificación de la consulta LINQ, suministrándole la etiqueta que deseamos añadir a la misma, como se muestra a continuación:
var query = context.Friends
.OrderByDescending(friend => friend.Age)
.Take(10)
.Select(friend =>
new { FriendName = friend.Name, friend.Age, CountryName = friend.Country.Name })
.TagWith("Get top 10 older friends with country");
Y la consulta que enviaremos a la base de datos será la siguiente:
-- Get top 10 older friends with country

SELECT TOP(@__p_0) [friend].[Name] AS [FriendName], [friend].[Age],
[friend.Country].[Name] AS [CountryName]
FROM [Friends] AS [friend]
LEFT JOIN [Countries] AS [friend.Country] ON [friend].[CountryId] = [friend.Country].[Id]
ORDER BY [friend].[Age] DESC
Mola, ¿eh?

Publicado en Variable not found.

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

Picando Código

[Libro] THEM: Adventures with extremists por Ron Jonson

junio 17, 2019 03:30

Libro: Jon Ronson, Them

Jon Ronsom: Them – Adventures with extremists

Me encontré con este título de casualidad hace por lo menos un año. Estaba mirando libros en una tienda, en esos momentos en los que no buscamos nada en particular, pero paseamos por las estanterías levantando y ojeando libros al azar.

Me llamó la atención la tapa (que en ese momento era la de esta edición), y después de leer la descripción, me quedó guardado en la memoria:

THEM comenzó como un libro sobre distintos tipos de extremistas, pero cuando Jon pudo conocer a varios de ellos -Fundamentalistas islámicos, neo-Nazis, miembros del Ku Klux Klan- encontró que todos tenían una creencia similar: que una pequeña, misteriosa elite gobierna el mundo desde un cuarto secreto.

En THEM, Jon se pone en camino, con la ayuda de los extremistas, a ubicar ese cuarto. El viaje es tan horripilante como cómico, y por el camino Jon es perseguido por hombres con lentes oscuros, desenmascarado como judío en medio de un campo de entrenamiento Yihad, y presencia a CEOs internacionales y políticos participando en un ritual pagano estrafalario en los bosques del norte de California.

THEM es una exploración fascinante y entretenida del extremismo, en el cual Jon aprende algunas cosas alarmantes sobre el mundo al otro lado del espejo de «ellos» (them) y «nosotros». ¿Están en algo los extremistas? ¿O se ha convertido Jon en uno de ellos?

La semana pasada lo adquirí y empecé a leerlo (a pesar de tener ya una pila de libros sin leer o medio empezados). Me resultó tremendamente entretenido, al punto que lo leí en 2 días. Hacía años que no leía un libro en tan poco tiempo. Creo que ayudó haber empezado el viernes de tarde. Para el sábado de noche ya lo había terminado. Me atrapaba la intriga de un capítulo a otro, «a ver en qué se mete ahora», y me llamaba a volver al libro cada vez que tenía tiempo ocioso.

THEM no analiza las razones que llevan a la gente a ser extremistas o el por qué del odio hacia otros. Pero con esa idea en común del grupo que domina el mundo desde un cuarto secreto, relata las aventuras del autor pasando tiempo con distintos extremistas. Es bastante divertido, a la vez que enseña de personajes y eventos relacionados a grupos extremistas en la historia. También baja un poco a tierra la imagen de estos hombres en el sentido de que más allá de todo, siguen siendo seres humanos de carne y hueso (teóricamente).

Cuando algún hecho despertaba mi curiosidad y me sentía en la necesidad de aprender un poco más, siempre usé Tor para buscar más información. No quiero que el gobierno que espía mi tráfico en internet me agregue a alguna lista rara por estar buscando información sobre extremistas pensando que estoy en el equipo de alguno de ellos. Hasta ahí llega mi paranoia, que es un tema común en el libro, relatando teorías conspiratorias de un Nuevo Orden Mundial involucrando tanto a multimillonarios y políticos como a razas extraterrestres reptilianas. ¿Demasiado alocado o una conspiración de los miembros del grupo mismo para ridiculizar toda la teoría? Son el tipo de cosas se plantean en THEM.

El libro está disponible en Amazon.es y Amazon.com. Fue el primer libro que leí del autor, ni siquiera vi la película basada en su obra The Men Who Stare at Goats. Después de esto, la agrego a mi lista de películas para ver y también agrego para leer a futuro The Psychopath Test: A Journey Through the Madness Industry, lo que podría alimentar mi teoría de que la mayoría de los CEOs, políticos y empresarios son psicópatas.

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

Variable not found

Enlaces interesantes 366

junio 17, 2019 09:21

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

Por si te lo perdiste...

.NET / .NET Core

ASP.NET / ASP.NET Core

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

Data

Machine learning / IA / Bots

Web / HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

Xamarin

Otros

Publicado en Variable not found.

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

Koalite

Sobre checked exceptions y manejo de excepciones

junio 17, 2019 05:06

Hace unos días me «retaba» Iván en Twitter a escribir un post sobre las checked exceptions de Java, y lo cierto es que me parece un tema entretenido para escribir algo más que un par de tweets, así que aquí va mi opinión.

Checked exceptions en Java

Desde la perspectiva de un sistema de tipos estático esto de las checked exceptions parece una buena idea: hace más expresiva la declaración del método y permite hacer más validaciones en tiempo de compilación.

Todo acorde con lo que uno esperaría de un sistema de tipos así. Si quieres flexibilidad vete a JavaScript y disfruta de su anarquía libertad, ¿no?

Sin embargo, para mi, que no soy ningún experto en Java, resultan incómodas. Quizá sea la sintaxis de Java, quizá sea el abuso de excepciones para situaciones que no son nada excepcionales, como que falle una conversión de string a int.

Si a eso le unes que existe un tipo mágico de excepciones que no se chequean (las que derivan de RuntimeException), pues te queda una mezcla un poco rara: por una parte me obligas a declarar y capturar excepciones con una sintaxis poco amigable, y por otra dejas una la puerta abierta a lanzar excepciones no chequeadas que hacen que nunca pueda estar seguro de si un método va a lanzar o no una excepción, por lo que esa presunta seguridad de tipos de queda en eso: presunta.

Excepciones como gestión de errores

En mi opinión, si una función puede no completar la operación que se supone que debe hacer es mejor codificarlo en el propio tipo devuelto por la función. Siguiendo con el ejemplo de convertir un string en un int, dado que es algo que puede fallar, me parece más razonable tener una función cuyo resultado indique si la conversión se pudo realizar o no y, en caso de que se realizase, el entero obtenido, en lugar de lanzar una excepción si el string no tiene un formato válido.

Si asumimos que la función puede fallar, podemos codificar el fallo en el tipo de retorno y convertir lo que sería una función parcial en una función total. En muchos lenguajes (como muchos funcionales) esto se representaría con un tipo Either o Maybe (si te da igual el motivo del fallo) y sería lo más normal del mundo.

Cuando unes ese diseño con un lenguaje que permite utilizar pattern matching para el análisis de resultados, o incluso algo como el do notation de Haskell, que suena muy exótico en pero en C# puedes simular con el select/from y seguro que en Java tienes alguna alternativa para hacer algo similar, la cosa se vuelve bastante cómoda y gestionar los errores de forma normal en lugar de como algo excepcional se convierte en rutinario.

Subiendo el nivel semántico de la excepción

Volviendo al hilo de Iván, él plantea:

Los argumentos que he escuchado en su contra son principalmente dos, el primero es que exponen detalles de implementación en la interfaz (la firma del método).

El segundo motivo en su contra es que si se modifica ese método y cambia su implementación de forma que pueda lanzar nuevas excepciones, es necesario actualizar todos sus usos añadiendo los correspondientes try.

Ciertamente son argumentos habituales en contra de las checked exceptions, y la respuesta de Iván a esos argumentos es más que razonable:

Tras darle vueltas y pensar en ello, para mi ninguno de los dos argumentos son válidos. Imagina una interfaz llamada Mailer que pueda tener diferentes implementaciones según como se quiera enviar un mensaje. Típico servicio registrado en un contenedor.

El método sería Future SendEmail(EmailAddress address, EmailContent content) throws EmailException

Bajo mi punto de vista, la interfaz debe de exponer esta excepción, y si ocurre algún tipo de excepción en alguna de sus implementaciones, relanzarla como EmailException.

De esta forma, me aseguro que la excepción siempre será EmailException y no otra. Imagina que estoy escribiendo una implementación que hace una llamada HTTP. Si HttpClient tiene HttpBadCodeResponseException como checked, me obligo a capturarla y relanzarla como EmailException de forma que no se me olvide hacer ningún catch y SendEmail lance un IOException o HttpBadResponseException, que SÍ que expondrían detalles de implementación.

A la hora de utilizar el servicio, sólo tendría que capturar EmailException en vez de Exception por si en alguna implementación se me ha olvidado capturar la excepción que produce y me encuentro con un FileNotFoundException o algo similar.

Básicamente la idea es aumentar el nivel semántico del error. Si tienes un método/función que envía un email, el resultado puede ser que lo ha enviado con éxito o que ha fallado, y si es así, el motivo real (fallo en la respuesta HTTP, fallo en el formato de la dirección del destinatario, etc.) puede considerarse algo secundario que quedará encapsulado en un EmailException.

Visto así tiene mucho sentido y es mucho más informativo encontrarte con un EmailException que con un HttpBadResponseException, pero lo cierto es que a efectos prácticos soluciona el problema a medias.

Es cierto que evita el carácter «vírico» de las checked excepctions, y si una nueva implementación del servicio puede encontrarse con otro tipo de problema los consumidores del servicio Mailer no tienen que preocuparse por ella puesto que quedará encapsulada en una EmailException. Esto funciona muy bien si desde el principio ya hemos decicido que el método SendEmail puede lanzar una excepción.

Pero, ¿qué ocurre cuando al diseñar nuestro interfaz decidimos no incluir ninguna excepción porque en las implementaciones iniciales no vemos ningún caso problemático? Básicamente, hemos vuelto al punto de partida. Cuando encontramos la primera implementación que puede lanzar excepciones, debemos encapsularlas en una excepción (con mayor valor semático, eso sí), y eso nos obliga a cambiar todas las implementaciones el interfaces Mailer y todos los consumidores del interfaces.

Una opción sería hacer que, especulativamente, todos y cada uno de los métodos de nuestros interfaces lanzasen una excepción con valor semántico asociado al interfaz; pero, sinceramente, empezar a añadir posible excepciones por si acaso en el futuro alguna implementación del interfaz la lanza no me parece muy práctico.

Y, ojo, esto no es algo que se solucione con la alternativa más «funcional» que comentaba antes de utilizar un tipo Either o similar para representar el resultado de la operación. Estás en las mismas, o bien introduces desde el principio el tipo Either como valor de retorno de todas tus funciones «por si acaso» en un futuro alguna puede fallar, o en el momento en que lo introduzcas te va a tocar modificar el código que ya existe.

Conclusión

Admito que aquí hay la cosa va por barrios.

En cierto modo, me recuerda a la típica discusión de lenguajes dinámicos y estáticos. Yo me siento más cómodo con lenguajes estáticos y nunca me ha supueto un freno tener que lidiar con tipos, más bien al contrario, pero es cierto que la excepciones chequeadas, que podrían verse como un paso más hacia la comprobación estática de errores, me han supuesto fricción adicional a la hora de desarrollar.

En realidad, ni siquiera estoy convencido de que las excepciones sean una buena idea como mecanismo de gestión de errores frente al uso de valores de retorno al estilo go, pero sin entrar a debatir eso, sí tengo claro que deberían representar cosas excepcionales y que, en general, se tienden a usar en situaciones que tienen poco de excepcional.

Habitualmente intento no gestionar excepciones en el lugar en que se producen a menos que esté tratando con una librería que las lance por casi cualquier motivo y pueda hacer algo para recuperarme del error o al menos convertirlas a algo con mayor valor semántico (cosa que, sinceramente, ocurre menos veces de las que me gustaría).

Desde mi punto de vista, una excepción debería representar algo excepcional y, por tanto, algo que de lo que la aplicación no tiene muchas formas de recuperarse. Por ello, tiendo a gestionar las excepciones en la «frontera» de las aplicaciones (controladores de un API Web, manejadores globales de excepciones), y muchas veces me limito a dejar que muera el proceso (ya sea la petición concreta a un API o incluso la aplicación entera) y logear la información para evitar en problemas futuros.

No hay posts relacionados.

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

Israel Perales

Nexus Repository

junio 16, 2019 05:54

Nexus Repository

Don't repeat yourself

Un principio del desarrollo del software muy simple, "no repitas código".

Gracias a este principio creo que muchos desarrolladores dieron forma a las librerias, prefiriendo encapsular un comportamiento deseado en un solo lugar.

Esto hace necesario administrar las librerias de una forma eficiente y fácil de utilizar en el equipo de trabajo.

Aunque podemos expresarlo en una frase tiene una complejidad muy grande al llevarlo a la practica.

Debemos ser capaces de soportar los diferentes formatos de los componentes con los que trabajamos, en mi caso utilizo Maven, NPM/Yarn, Bower y Docker.

Al buscar una solución para estos componentes llegué a la questión de ¿que será mas facil de manejar?, ¿una plataforma por cada formato o uno que englobe a todos?.

Fue así que llegue a Nexus Repository OSS.

The free artifact repository with universal support for popular formats.

Todo en uno, gratis y con soporte para mis componentes de trabajo y algunos extras , aquí una lista de los formatos soportados:

  • Bower
  • Docker
  • Git LFS
  • Maven
  • npm
  • NuGet
  • PyPI
  • Ruby Gems
  • Yum
  • Apt
  • Conan
  • R
  • CPAN
  • Raw (Universal)
  • P2
  • Helm
  • ELPA

Instalación

Como ya es costumbre usaremos Docker para su instalación, podemos encontrar la imagen oficial de Nexus en Docker Hub.

Vamos a correr los siguientes comandos:

mkdir nexus && cd nexus

mkdir nexus-data

sudo chown -R 200 nexus-data

docker run -d -p 8081:8081 --name nexus -v nexus-data:/nexus-data sonatype/nexus3

Y ahora solo debemos entrar a la liga http://localhost:8081

Nexus Repository

Para iniciar sesión usaremos las credenciales por defecto que son admin y admin123.

Para utilizar Nexus cada formato requiere su propia configuración de los repositorios privados, en este caso, configuraremos Maven como ejemplo.

Configuración Maven

Crearemos un archivo llamado settings.xml en el directorio .m2 que se encuentra en nuestro home como se muestra en el siguiente ejemplo.

<settings>
  <mirrors>
    <mirror>
      <id>nexus</id>
      <mirrorOf>*</mirrorOf>
      <url>http://localhost:8081/repository/maven-public/</url>
    </mirror>
  </mirrors>
  <profiles>
    <profile>
      <id>nexus</id>
      <repositories>
        <repository>
          <id>central</id>
          <url>http://central</url>
          <releases><enabled>true</enabled></releases>
          <snapshots><enabled>true</enabled></snapshots>
        </repository>
      </repositories>
     <pluginRepositories>
        <pluginRepository>
          <id>central</id>
          <url>http://central</url>
          <releases><enabled>true</enabled></releases>
          <snapshots><enabled>true</enabled></snapshots>
        </pluginRepository>
      </pluginRepositories>
    </profile>
  </profiles>
  <activeProfiles>
    <activeProfile>nexus</activeProfile>
  </activeProfiles>
  <servers>
    <server>
      <id>nexus</id>
      <username>admin</username>
      <password>admin123</password>
    </server>
  </servers>
</settings>

Probaremos que funciona con un proyecto real:


git clone https://github.com/ripper2hl/dockerejemplos.git

cd dockerejemplos/java

mvn clean install

Si todo esta bien deberíamos algo así en la terminal:

[INFO] Scanning for projects...
[INFO] 
[INFO] -----------------------< javadocker:javadocker >------------------------
[INFO] Building javadocker 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
Downloading from nexus: http://localhost:8081/repository/maven-public/org/apache/maven/plugins/maven-compiler-plugin/3.1/maven-compiler-plugin-3.1.pom
Downloaded from nexus: http://localhost:8081/repository/maven-public/org/apache/maven/plugins/maven-compiler-plugin/3.1/maven-compiler-plugin-3.1.pom (0 B at 0 B/s)
Downloading from nexus: http://localhost:8081/repository/maven-public/org/apache/maven/plugins/maven-compiler-plugin/3.1/maven-compiler-plugin-3.1.jar
Downloaded from nexus: http://localhost:8081/repository/maven-public/org/apache/maven/plugins/maven-compiler-plugin/3.1/maven-compiler-plugin-3.1.jar (0 B at 0 B/s)
Downloading from nexus: http://localhost:8081/repository/maven-public/org/apache/maven/plugins/maven-assembly-plugin/2.2-beta-5/maven-assembly-plugin-2.2-beta-5.pom
Downloaded from nexus: http://localhost:8081/repository/maven-public/org/apache/maven/plugins/maven-assembly-plugin/2.2-beta-5/maven-assembly-plugin-2.2-beta-5.pom (0 B at 0 B/s)
Downloading from nexus: http://localhost:8081/repository/maven-public/org/apache/maven/plugins/maven-plugins/16/maven-plugins-16.pom
Downloaded from nexus: http://localhost:8081/repository/maven-public/org/apache/maven/plugins/maven-plugins/16/maven-plugins-16.pom (0 B at 0 B/s)
Downloading from nexus: http://localhost:8081/repository/maven-public/org/apache/maven/plugins/maven-assembly-plugin/2.2-beta-5/maven-assembly-plugin-2.2-beta-5.jar
.
.
.

En Nexus vemos que se crea un cache de los paquetes que descargamos y así también ayudamos a ahorrar ancho de banda.

Nexus Repository

Este ejemplo es especificamente del formato de maven, para configurar otros formatos, podemos seguir la guiá del blog oficial.

https://blog.sonatype.com/using-nexus-3-as-your-repository-part-1-maven-artifacts

Mantener el orden y tener nuestros componentes a la mano, nos dará el impulso de llevar el flujo de trabajo a otro nivel.

Tal vez existan mejores alternativas a Nexus pero me ha funcionado y aun no busco un reemplazo a esta herramienta.

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

Blog Bitix

Metadatos e introspección en GraphQL

junio 15, 2019 10:00

Una API REST no ofrece introspección y por tanto hay que recurrir a un sistema de documentación que puede estar desactualizado y hay que mantener para conocer como usar la API y cuales son sus tipos y parámetros. Por el contrario GraphQL incorpora un sistema de introspección que permite conocer sus tipos y campos, a través del editor GrapiQL o si fuese necesario de forma automtizada con código.

GraphQL

Una de las cosas que me gustan de GraphQL sobre REST es que la API de un servicio se define en un esquema. Tanto las operaciones de consulta, de modificación con sus nombres de parámetros, tipos y si son requeridos o no. Esta información es básica para hacer un buen uso de esa API y conocer cual es su contrato. Además con la herramienta GraphiQL se pueden crear y realizar consultas con un pequeño IDE con asistencia de código. GraphQL genera los metadatos e ofrece la instrospección a partir únicamente de la definición del esquema del servicio sin ningún esfuerzo adicional por parte del creador del servicio.

En el ejemplo de esta serie de artículos sobre GraphQL he usado el siguiente esquema que utiliza como modelo de datos el de una librería.

 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
scalar LocalDate
type Book {
id: Long
title: String
author: Author
isbn: String
date: LocalDate
comments(after: String, limit: Long): CommentsConnection
batchedIsbn: String
batchedComments(after: String, limit: Long): CommentsConnection
}
type Magazine {
id: Long
name: String
pages: Long
}
type Comment {
id: Long
text: String
}
type Author {
id: Long
name: String
}
input BookFilter {
title: String
}
type CommentsConnection {
edges: [CommentEdge]
pageInfo: PageInfo
}
type CommentEdge {
node: Comment
cursor: String
}
type PageInfo {
startCursor: String
endCursor: String
hasNextPage: Boolean
}
union Publication = Book | Magazine
type Query {
books(filter: BookFilter): [Book]!
publications: [Publication]!
book(id: Long): Book!
authors: [Author]!
author(id: Long): Author!
}
type Mutation {
addBook(title: String, author: Long): Book
}
schema {
query: Query
mutation: Mutation
}

Si no se conoce el esquema qué operaciones, tipos y nombres ofrece la API GraphQL permite introspección y con únicamente el endpoint se puede averiguar esta información.

Por ejemplo, con la siguiente consulta se puede conocer qué tipos contiene el esquema de una API. Los que comienzan con dos barras bajas o __ son tipos parte del sistema de introspección. Entre los que están el que representa un libro y autor pero también está Query que es un punto de acceso a la API.

1
2
3
4
5
6
7
{
__schema {
types {
name
}
}
}
 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
{
"data": {
"__schema": {
"types": [
{
"name": "Author"
},
{
"name": "Book"
},
{
"name": "BookFilter"
},
{
"name": "Boolean"
},
{
"name": "Comment"
},
{
"name": "CommentEdge"
},
{
"name": "CommentsConnection"
},
{
"name": "LocalDate"
},
{
"name": "Long"
},
{
"name": "Magazine"
},
{
"name": "Mutation"
},
{
"name": "PageInfo"
},
{
"name": "Publication"
},
{
"name": "Query"
},
{
"name": "String"
},
{
"name": "__Directive"
},
{
"name": "__DirectiveLocation"
},
{
"name": "__EnumValue"
},
{
"name": "__Field"
},
{
"name": "__InputValue"
},
{
"name": "__Schema"
},
{
"name": "__Type"
},
{
"name": "__TypeKind"
}
]
}
}
}

Conocer el tipo de las consultas de lectura y que consultas se pueden realizar inspeccionando el tipo Query.

1
2
3
4
5
6
7
{
__schema {
queryType {
name
}
}
}
1
2
3
4
5
6
7
8
{
__type(name: "Query") {
name
fields {
name
}
}
}
1
2
3
4
5
6
7
8
9
{
"data": {
"__schema": {
"queryType": {
"name": "Query"
}
}
}
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"data": {
"__type": {
"name": "Query",
"fields": [
{
"name": "author"
},
{
"name": "authors"
},
{
"name": "book"
},
{
"name": "books"
},
{
"name": "publications"
}
]
}
}
}

Por la propiedad de GraphQL de que se pueden realizar varias consultas en una única petición se pueden obtener ambos resultados a la vez.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{
__schema {
queryType {
name
}
}
__type(name: "Query") {
name
fields {
name
}
}
}
 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
{
"data": {
"__schema": {
"queryType": {
"name": "Query"
}
},
"__type": {
"name": "Query",
"fields": [
{
"name": "author"
},
{
"name": "authors"
},
{
"name": "book"
},
{
"name": "books"
},
{
"name": "publications"
}
]
}
}
}

Se puede obtener más en detalle los campos que contiene un tipo.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
book: __type(name: "Book") {
name
fields {
name
type {
name
kind
}
}
}
author: __type(name: "Author") {
name
fields {
name
type {
name
kind
}
}
}
}
 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
{
"data": {
"book": {
"name": "Book",
"fields": [
{
"name": "author",
"type": {
"name": "Author",
"kind": "OBJECT"
}
},
{
"name": "batchedComments",
"type": {
"name": "CommentsConnection",
"kind": "OBJECT"
}
},
{
"name": "batchedIsbn",
"type": {
"name": "String",
"kind": "SCALAR"
}
},
{
"name": "comments",
"type": {
"name": "CommentsConnection",
"kind": "OBJECT"
}
},
{
"name": "date",
"type": {
"name": "LocalDate",
"kind": "SCALAR"
}
},
{
"name": "id",
"type": {
"name": "Long",
"kind": "SCALAR"
}
},
{
"name": "isbn",
"type": {
"name": "String",
"kind": "SCALAR"
}
},
{
"name": "title",
"type": {
"name": "String",
"kind": "SCALAR"
}
}
]
},
"author": {
"name": "Author",
"fields": [
{
"name": "id",
"type": {
"name": "Long",
"kind": "SCALAR"
}
},
{
"name": "name",
"type": {
"name": "String",
"kind": "SCALAR"
}
}
]
}
}
}

Incluso se puede inspeccionar los tipos del sistema de instrospección. Con las descripciones de los campos o parámetros de entrada si los tuviese.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
__type(name: "__Type") {
name
fields {
name
type {
name
kind
}
}
}
}
 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
{
"data": {
"__type": {
"name": "__Type",
"fields": [
{
"name": "description",
"type": {
"name": "String",
"kind": "SCALAR"
}
},
{
"name": "enumValues",
"type": {
"name": null,
"kind": "LIST"
}
},
{
"name": "fields",
"type": {
"name": null,
"kind": "LIST"
}
},
{
"name": "inputFields",
"type": {
"name": null,
"kind": "LIST"
}
},
{
"name": "interfaces",
"type": {
"name": null,
"kind": "LIST"
}
},
{
"name": "kind",
"type": {
"name": null,
"kind": "NON_NULL"
}
},
{
"name": "name",
"type": {
"name": "String",
"kind": "SCALAR"
}
},
{
"name": "ofType",
"type": {
"name": "__Type",
"kind": "OBJECT"
}
},
{
"name": "possibleTypes",
"type": {
"name": null,
"kind": "LIST"
}
}
]
}
}
}

Conocer cuales son los campos de un tipo puede utilizarse para validar una API, comprobando que no se han eliminado campos necesarios. Es útil en el caso de querer automatizar esta validación de una API de GraphQL que se consuma ayudando a detectar de forma temprana problemas de compatibilidad al publicarse una nueva versión que no está bajo propiedad del que la usa.

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.

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

Blog Bitix

Pruebas de carga y rendimiento de un servicio web con Apache Bench

junio 14, 2019 03:00

En algunos que un servicio devuelva los datos esperados no es suficiente, otros requisitos no funcionales o de términos de servicio son que sus tiempos de respuesta sean menores al especificado en sus requisitos, que sea capaz de soportar cierto número de peticiones concurrentes o de atender un número de peticiones por minuto. Para asegurar que el servicio es capaz de cumplir estos requisitos funcionales hay que utilizar herramientas que permitan evaluar su desempeño, una de ellas muy fácil de utilizar y que proporciona valiosa información es Apache Bench.

Apache

Para hacer pruebas de carga o medir el rendimiento de cualquier servicio que funcione mediante el protocolo HTTP hay multitud de herramientas. Una de las más sencillas de utilizar y con un informe con información interesante es Apache Bench o simplemente ab. Este comando se puede utilizar con simplemente tres parámetros el endpoint a probar, el número de peticiones en total a realizar (-n) y cuantas peticiones concurrentes al mismo tiempo (-c). Otos parámetros son los datos POST a enviar, cabeceras (-H) y cookies (-C) de las peticiones, tiempos de timeout (-s) o cerficado de cliente (-E) entre algunos otros. En vez limitar las pruebas a un número de peticiones las pruebas se pueden limitar a un tiempo determinado por ejemplo 60 segundos (-t).

Es una herramienta que se utiliza para medir el rendimiento de el servidor Apache pero utilizable para cualquier otro servicio por ejemplo una web o una API REST o GraphQL. Está disponible por supuesto para GNU/Linux pero también para macOS y para Windows.

Si quisiese medir el rendimiento en mi blog alojado en GitHub Pages podría hacerlo lanzando 1000 peticiones para que sea una muestra suficientemente amplia con 20 usuarios de forma concurrente que son los que en los momentos de más tráfico tiene mi blog. Mi conexión de internet es un ADSL que no llega a 1 MB/s de subida por lo que la conexión en cierta medida limite el test.

1
$ ab -n 1000 -c 20 https://picodotdev.github.io/blog-bitix/

El informe de resultado que ofrece ab al finalizar la prueba incluye el tiempo dedicado en la conexión, en el procesado, esperando y en total con los valores para cada uno de ellos con mínimo y máximo, de media y la mediana. El tiempo total empleado por la prueba, el protocolo SSL/TLS usado, los bytes devueltos en la petición, el número de peticiones servidas por segundo, el tiempo de media empleado de media por cada petición y de media teniendo en cuenta la concurrencia, la tasa de transferencia en la respuesta y finalmente el tiempo de respuesta según percentil que van que desde el 50 al 100, es decir, que el 50% de las peticiones se han respondido en el tiempo en milisegundos indicado. Si las hubiera también muestra las peticiones fallidas y las que han devuelto un código de respuesta distinto de 200.

 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
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking picodotdev.github.io (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests
Server Software: GitHub.com
Server Hostname: picodotdev.github.io
Server Port: 443
SSL/TLS Protocol: TLSv1.2,ECDHE-RSA-AES128-GCM-SHA256,2048,128
Server Temp Key: X25519 253 bits
TLS Server Name: picodotdev.github.io
Document Path: /blog-bitix/
Document Length: 28389 bytes
Concurrency Level: 20
Time taken for tests: 29.220 seconds
Complete requests: 1000
Failed requests: 0
Total transferred: 29007394 bytes
HTML transferred: 28389000 bytes
Requests per second: 34.22 [#/sec] (mean)
Time per request: 584.404 [ms] (mean)
Time per request: 29.220 [ms] (mean, across all concurrent requests)
Transfer rate: 969.45 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 149 349 149.7 303 1361
Processing: 122 225 75.5 207 586
Waiting: 53 122 68.8 108 481
Total: 329 574 174.0 512 1612
Percentage of the requests served within a certain time (ms)
50% 512
66% 534
75% 555
80% 744
90% 832
95% 915
98% 1104
99% 1171
100% 1612 (longest request)

Esta herramienta puede ser utilizada para par medir el rendimiento de cualquier servicio web. Un blog de Wordpress, una página de una organización, un endpoint de un servicio REST o GraphQL, etc… Es muy sencilla de utilizar y genera un informe corto pero con interesante información sobre el rendimiento. Si se hacen cambios se puede medir el antes y el después y comparar los resultados para observar de que modo han afectado al redimiento si de forma positiva o negativa y en que grado.

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

Fixed Buffer

La potencia de la Reflexión (Parte 1)

junio 11, 2019 08:00

¡Ya estamos de vuelta por estos lares! Hoy vengo a contaros un problema que he tenido en el trabajo y como lo he solucionado con una herramienta no muy visible pero muy potente que nos ofrece .Net, la reflexión. Antes de nada, ¿qué es eso de la reflexión y como puedo usarla en C#?

Si nos fijamos en la definición:

La reflexión proporciona objetos (de tipo Type) que describen los ensamblados, módulos y tipos. Puede usar la reflexión para crear dinámicamente una instancia de un tipo, enlazar el tipo a un objeto existente u obtener el tipo desde un objeto existente e invocar sus métodos, o acceder a sus campos y propiedades. Fuente: Reflexión (C#)

Dicho con otras palabras más sencillas, la reflexión en C# es la capacidad que tenemos para desde el código, conseguir información sobre tipos (clases, librerías, objetos, etc…) y usar esa información para crear objetos dinámicamente y/o acceder a sus miembros sin crear una instancia tipada.

Vale, ¿con lo que he dicho no he conseguido aclararlo mucho más verdad? La verdad es que personalmente creo que es un concepto avanzado del lenguaje y que eso es lo que echa para atrás a muchos programadores. De hecho, si vemos su página de MSDN, vamos que tiene varias opciones:

Seguramente después de esto te sigas preguntando en que me ha podido valer la reflexión de c#. Vale, en mi caso, yo tenia una serie de clases que heredaban todas de una clase base, pero cada una tenia sus particularidades, algo como por ejemplo esto:

public class BaseClass
{
    public string ModeloMotor { get; set; }
}

public class Coche : BaseClass
{
    public bool Descapotable { get; set; }
}

public class Moto : BaseClass
{
    public bool Motor2Tiempos { get; set; }
}

public class Camion : BaseClass
{
    public bool EsVehiculoLargo{ get; set; }
}

Todo ello son posibles clases que recibimos de un método o una api, la cual nos devuelve un «BaseClass», si nos fijamos, todas están relacionadas entre ellas, pero cada una de las clases tiene sus particularidades. Aquí, tendríamos una opción clara, aprovecharnos del poliformismo y tener una clase base que lo contenga todo:

public class BaseClass
{
    public string ModeloMotor { get; set; }
    public bool Descapotable { get; set; }
    public bool Motor2Tiempos { get; set; }
    public bool EsVehiculoLargo{ get; set; }
}

public class Coche : BaseClass
{
    //...
}

public class Moto : BaseClass
{
    //...
}

public class Camion : BaseClass
{
    //...
}

De este modo, desde cada una de las clases hijas tendremos acceso a todas las propiedades. Pero claro, tampoco tiene mucho sentido hablar de un camión con un motor de dos tiempos o una moto que sea un vehículo largo.

Otra opción posible, es hacer casting a absolutamente todas las posibles clases hijas para ver si alguna coincide:

var moto = claseBase as Moto;
if (!(moto is null))
    Console.WriteLine(moto.Motor2Tiempos);
var coche = claseBase as Coche;
if (!(coche is null))
    Console.WriteLine(coche.Descapotable);
var camion = claseBase as Camion;
if (!(camion is null))
    Console.WriteLine(camion.Tara);
//.....

¿Os imagináis lo larga que se puede hacer la lista si tengo 20 clases diferente que heredan de clase base?

Aquí es donde entra en juego la reflexión, desde un objeto de tipo «BaseClass», mediante la reflexión de C#, podemos iterar las propiedades del objeto encapsulado y obtener su valor, por ejemplo, vamos a crear un método de extensión que nos permita obtener desde la clase base si es un vehículo largo:

static class Extensiones
{
    public static bool? EsVehiculoLargo(this BaseClass clase)
    {
        //Obtenemos todas las propiedades de la clase que nos pasan
        var properties = clase.GetType().GetProperties();
        //Iteramos las propiedades
        foreach (var propertyInfo in properties)
        {
            //Si alguna se llama 
            if (propertyInfo.Name == "EsVehiculoLargo")
            {
                //Retornamos el valor
                return Convert.ToBoolean(propertyInfo.GetValue(clase));
            }
        }
        //Si ninguna coincide, retornamos null
        return null;
    }
}

Si nos fijamos en el código, estamos creando un método extensor para «BaseClass». En él, lo que vamos a hacer es obtener todas las propiedades, pero no de «BaseClass», sino de la clase hija encapsulada dentro. Dentro de las propiedades, vamos a buscar la que tenga el nombre que nos interesa, y si hay alguna, vamos a obtener el valor pasándole al objeto PropertyInfo la instancia de donde tiene que obtener el valor.

Con esto, no solo podemos leer la propiedad, también podríamos escribirla llamando al método «SetValue»:

propertyInfo.SetValue(clase, true);
propertyInfo.SetValue(clase, false);

Pero eso no es todo lo que podemos conseguir, también podemos obtener información sobre su tipo, obtener el método get o set, los atributos que tiene, si es de lectura y/o escritura …

De este modo tan elegante, podemos acceder a la información de la clase hija sin tener que conocer el tipo exacto de la clase hija. En mi caso, pude conseguir consumir la api sin tener que hacer casting individuales (en mi caso yo tenía más de 60 clases hijas).

Pero no se queda ahí, ¡la reflexión da para mucho más! Si con este ejemplo te ha picado el gusanillo sobre lo que puede ofrecer la reflexión, en las próximas entradas vamos a profundizar en diferentes casos, como crear un objeto de una clase de manera dinámica, buscar entre los atributos de un objeto, o llamar a métodos de objetos dinámicamente. De momento, dejo un enlace al código para poder probarlo y lo iremos ampliando.

**La entrada La potencia de la Reflexión (Parte 1) se publicó primero en Fixed Buffer.**

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

Variable not found

Índices y rangos en C# 8 (2 de 2)

junio 11, 2019 06:14

.NET CoreEn el post anterior vimos que la estructura Index, junto con alguna cortesía del compilador, permitía la especificación de índices en arrays de forma muy sencilla. Veíamos cómo podíamos acceder a elementos concretos utilizando su posición en la colección, tanto contando desde el principio como desde el final:
var primes = new[] { 2, 3, 5, 7, 11, 13, 17, 19 };
Index fromStart = 2; // = Index.FromStart(2) - conversión implícita
Index fromEnd = ^2; // = Index.FromEnd(2)

Console.WriteLine(primes[fromStart]); // 5
Console.WriteLine(primes[fromEnd]); // 17
Sin embargo, puede que a Index por sí mismo tampoco le veáis demasiada utilidad... y así es. De hecho, su finalidad es más bien el dar soporte a rangos, una nueva característica de C#8 que nos permitirá referirnos a "porciones" de arrays o colecciones similares usando una sintaxis compacta e integrada en el lenguaje.

La estructura Range

La estructura Range ofrece una vía para la especificación de rangos en colecciones indexadas mediante el almacenamiento de sus índices iniciales y finales. O en otras palabras, Range permite definir un rango del interior de un array o similar mediante la indicación de un Index inicial y un Index final.

Veamos un ejemplo de uso:
var primes = new[] { 2, 3, 5, 7, 11, 13, 17, 19 };

var firstTwoItemsRange = new Range(Index.FromStart(0), Index.FromStart(2));
// O Más simple, utilizando conversiones implícitas:
// var firstTwoItemsRange = new Range(0, 2);
var firstTwoItems = primes[firstTwoItemsRange];
Console.WriteLine(string.Join(",", firstTwoItems)); // 2,3
Un aspecto sumamente importante en este punto, del que seguro que algunos os habéis dado cuenta, es el índice final en un objeto Range es no inclusivo (¡ojo con esto!). Por esa razón, el resultado mostrado por consola es "2,3" y no "2,3,5", a pesar de haber indicado como índices iniciales y finales 0 y 2, respectivamente.

Si quisiéramos incluir el último elemento, deberíamos especificar un índice superior por encima del mismo, como en el siguiente código, donde utilizamos Index.From() para obtener el índice del elemento que se encuentra virtualmente más allá del último ítem del array:
var primes = new[] { 2, 3, 5, 7, 11, 13, 17, 19 };

var lastThreeItemsRange = new Range(5, Index.FromEnd(0));
var lastThreeItems = primes[lastThreeItemsRange];
Console.WriteLine(string.Join(",", lastThreeItems)); // 13,17,19
Como ya sabemos gracias al post anterior, podemos utilizar el edulcorante sintáctico que nos ofrece C# para crear rangos de forma más compacta efectiva, gracias al uso de las conversiones implícitas y el hat operator:
var primes = new[] { 2, 3, 5, 7, 11, 13, 17, 19 };

var lastThreeItemsRange = new Range(^3, ^0);
var lastThreeItems = primes[lastThreeItemsRange];
Console.WriteLine(string.Join(",", lastThreeItems)); // 13,17,19

var allPrimesRange = new Range(0, ^0);
var allPrimes = primes[allPrimesRange];
Console.WriteLine(string.Join(",", allPrimes)); // 2, 3, 5, 7, 11, 13, 17, 19
Y como ocurría con la estructura Index, Ranage también ofrece algunos miembros estáticos para simplificar la codificación de rangos frecuentes, como Range.StartAt(), Range.EndAt() o Range.All:
var primes = new[] { 2, 3, 5, 7, 11, 13, 17, 19 };

var firstTwoItemsRange = Range.EndAt(2);
var firstTwoItems = primes[firstTwoItemsRange];
Console.WriteLine(string.Join(",", firstTwoItems)); // 2,3

var lastThreeItemsRange = Range.StartAt(5);
var lastThreeItems = primes[lastThreeItemsRange];
Console.WriteLine(string.Join(",", lastThreeItems)); // 13, 17, 19

var allPrimesRange = Range.All;
var allPrimes = primes[allPrimesRange];
Console.WriteLine(string.Join(",", allPrimes)); // 2, 3, 5, 7, 11, 13, 17, 19

Añadimos más edulcorante: el operador rango ".."

Con lo que sabemos hasta este momento, ya podríamos definir rangos de una forma bastante efectiva y fácil de leer, pero, de nuevo, los diseñadores de C# no podían dejarlo ahí y han añadido un atajo para hacerlo aún más sencillo.

El operador ".." permite definir rangos de forma más compacta, usando índices separados por estos caracteres para determinar los límites de éstos. Como vemos en el siguiente código, estos índices son opcionales, lo que permite especificar muy fácilmente los límites superiores e inferiores del rango:
var primes = new[] { 2, 3, 5, 7, 11, 13, 17, 19 };

var thirdAndFourthItems = primes[2..4];
Console.WriteLine(string.Join(",", thirdAndFourthItems)); // 5,7

var firstTwoItems = primes[..2];
Console.WriteLine(string.Join(",", firstTwoItems)); // 2,3

var lastThreeItems = primes[^3..];
Console.WriteLine(string.Join(",", lastThreeItems)); // 13, 17, 19

var allPrimes = primes[..];
Console.WriteLine(string.Join(",", allPrimes)); // 2, 3, 5, 7, 11, 13, 17, 19

Pero... ¿qué estamos haciendo realmente a obtener un rango de una colección?

Hasta ahora hemos visto cómo se definen los rangos, y cómo podemos usarlos para "extraer" un subconjunto de elementos de un array.

Pero en realidad no nos hemos detenido a pensar qué es lo que estamos haciendo realmente cuando obtenemos un rango. Es decir, la pregunta es: ¿estamos creando un nuevo array copiando los elementos del anterior? ¿O quizás estamos obteniendo un puntero a los elementos existentes inicialmente? Pues, como suele ocurrir, la respuesta es que depende.

Cuando obtenemos un rango desde un array, internamente se copiarán los elementos a un nuevo array. Esto podemos comprobarlo fácilmente con el siguiente código, donde podemos observar que la modificación de un elemento del rango no afecta a la colección original:
var primes = new [] { 2, 3, 5, 7, 11, 13, 17, 19 };

var firstTwoItems = primes[..2];
firstTwoItems[0] = 666;
Console.WriteLine(string.Join(",", firstTwoItems)); // 666,3

var allItems = primes[..];
Console.WriteLine(string.Join(",", allItems)); // 2, 3, 5, 7, 11, 13, 17, 19
Sin embargo, si lo aplicamos sobre zonas de memoria como las referenciadas por Span<T>, el rango representará un subconjunto indexado de las mismas, pero apuntando a los contenidos originales (es decir, no se creará una copia de los datos):
string ToCommaString(Span<int> values) => string.Join(",", values.ToArray());

var primes = new[] { 2, 3, 5, 7, 11, 13, 17, 19 };
var span = primes.AsSpan();

var thirdAndFourthItems = span[2..4]; // Points to ...[5,7]...
thirdAndFourthItems[0] = 666;
Console.WriteLine(ToCommaString(thirdAndFourthItems)); // 666,7

var allItems = span[..];
Console.WriteLine(ToCommaString(allItems)); // 2, 3, 666, 7, 11, 13, 17, 19

var allOriginalItems = primes[..];
Console.WriteLine(string.Join(",", allOriginalItems)); // 2, 3, 666, 7, 11, 13, 17, 19
Toolbelt. Picture by Bert MarshallBueno, y creo que con esto ya hemos dado un buen repaso a Index y Range, así que lo dejamos aquí :)  Espero que os haya resultado interesante para comprender estos nuevos tipos y añadirlos como herramientas a nuestro cinturón de desarrollador.

Publicado en: www.variablenotfound.com.

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

Variable not found

Enlaces interesantes 365

junio 10, 2019 11:01

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

Por si te lo perdiste...

.NET / .NET Core

ASP.NET / ASP.NET Core

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

Data

Machine learning / IA / Bots

Web / HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

Xamarin

Otros

Publicado en Variable not found.

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

Arragonán

KPIs para equipos de desarrollo de software

junio 10, 2019 12:00

Como parte de mi trabajo en el último año, he intentado empujar la cultura de mejora continua en los diferentes equipos con los que he ido trabajando. Tanto en cuestiones de herramientas y habilidades técnicas, como en las de comunicación y coordinación, con ciertas restricciones y dependencias que caen fuera de nuestro margen de influencia.

Partimos del supuesto de que cuanto mejores sean las prácticas del equipo, mejor capacidad de entrega tendrá. Eso se traduce en una mayor adaptabilidad a los cambios, una mejor mantenibilidad del software con el paso del tiempo y posiblemente una mayor motivación del equipo con su trabajo.

Además de las típicas sensaciones subjetivas de efecto de mejora, teníamos que pensar en indicadores que nos permitieran ir observándolo realmente. En algún momento, también se empezaría a querer tener visibilidad desde fuera de los equipos, así que tocaba darle una vuelta y ordenar ideas.

Estuve preparando una presentación para explicar internamente cómo estábamos trabajando, hacia dónde creía que debíamos ir a través de una mejora continua, los distintos KPIs que podríamos observar y las necesidades que se cubrirían con una buena capacidad de entrega.

Diapositiva de una presentación con la visión de lo que, como equipo deberíamos cubrir: adaptabilidad respecto a negocio, entregar pronto, evitar retrabajo, una buena UX y evitar bugs en lo posible

“Dime cómo me mides y te diré cómo me comporto”

Desconozco el origen, pero con estas cosas siempre me acuerdo de este dicho. Hay que andar con ojo con qué indicadores (que no objetivos) vamos a medir. Además, tendremos que usar varios para compensar el fomento de comportamientos extraños que falsean esas métricas.

Con el uso normal de las herramientas, sólo acordando algunas convenciones, se puede facilitar la explotación de datos posterior para extraer métricas. Los orígenes de datos para los indicadores son:

  • El código
  • El repositorio y servidor de automatización
  • Las herramientas de gestión
  • El propio producto

Indicadores del código

Las herramientas de análisis estático de código nos dan números sobre deuda técnica que existe. No debemos perder de vista estos indicadores y dedicar tiempo en analizar y hacer limpieza específica de vez en cuando. Unas veces se resuelven con soluciones simples y otras esconden problemas de diseño que no resultan tan evidentes.

La cobertura de test es uno de los indicadores más habituales, lo más interesante en este caso es ver qué NO está cubierto. Y como se suele comentar, hay que andar con cuidado porque es un indicador fácil de falsear si se busca como objetivo.

En un momento dado, además, se podría utilizar mutation testing (comprobar que se rompe algún test al modificar código de producción) para tener un indicador de la calidad de los tests unitarios.

Indicadores del repositorio y servidor de automatización

Hay bastantes indicadores interesantes que se pueden sacar de los repositorios y servidores de automatización, aunque en su mayoría son dependientes de las convenciones de uso.

Sin embargo, un indicador siempre válido y que pienso que debería observarse es la frecuencia de integración.

La integración continua es una práctica (que no herramienta) tan popular como malinterpretada, ya que hasta que no se une el trabajo que ha hecho o está haciendo una persona con la rama principal de desarrollo y se completa una build, no la estamos realizando.

Hasta que no ha terminado correctamente la construcción de un artefacto de software, no sabemos si todo está correcto. A mayor frecuencia, feedback más temprano y menor incertidumbre.

En caso de usar ramas, también es interesante ver la duración de vida de las ramas. A más tiempo, mayor riesgo de conflictos u otros problemas al integrar.

Y si se utilizan pull/merge requests, también hay un puñado de indicadores que en un momento dado puedan sacarnos olores relacionados con la capacidad de entrega: cantidad de comentarios, tiempo que quedan abiertas, cantidad de rechazos…

Indicadores de las herramientas de gestión

Las herramientas de gestión, además de servir como radiador de información para saber la situación actual de la construcción del producto y ayudar a coordinar el trabajo, son una buena fuente de información de indicadores del proceso de trabajo.

Para observar la capacidad de todo el equipo en conjunto de hacer vertical slicing es muy útil conocer el tiempo de ciclo. A menor tiempo de ciclo, mayor es nuestra capacidad de entrega. Es el tiempo que se tarda desde que se empieza a trabajar en algo que aporte valor (por ejemplo, una historia de usuario) hasta que pasa a estar hecho.

Es habitual que en algún punto de las herramientas de gestión se pueden observar los distintos despliegues que se han realizado, donde podamos obtener la frecuencia de despliegue. Evidentemente a mayor frecuencia, mejor.

Aunque nuestro tiempo de ciclo fuera corto y la frecuencia de despliegue alta, sería posible que nuestro producto fuera frágil debido a bugs. Por eso el indicador de bugs detectados y resueltos por versión/despliegue es otra métrica a tener siempre en cuenta.

Dependiendo del momento y escenario en el que se encuentre un producto, también me parece muy interesante el indicador del lead time, el tiempo que pasa desde que se pide algo nuevo hasta que está desplegado en producción. Que vendría a ser consecuencia de los 3 anteriores indicadores y del tamaño de la pila de producto.

Como supongo que haya quien pueda echarlo de menos, omito intencionadamente los indicadores al respecto de las estimaciones, tipo story points por iteración. En mi opinión, tienen un componente muy subjetivo y variable para ser utilizado como indicador de cambio en la capacidad de entrega de un equipo.

Indicadores del propio producto

Además de otro tipo de instrumentación mucho más minuciosa que necesitan los miembros del equipo de gestión de producto o diseño, el equipo de desarrollo debería poder observar el número y porcentaje de uso por funcionalidad, que al final define el éxito o no del trabajo de todos.

Lo más interesante de este indicador es que combinado con otras métricas puede ayudar a tomar decisiones sobre la evolución del producto. Como por ejemplo, este escenario:

  1. Detectamos problemas muy graves de rendimiento en una parte del producto.
  2. Observamos que el porcentaje de uso de la funcionalidad afectada por esos problemas es residual.
  3. Decidimos no resolverlo de momento, pero lo reflejamos en la pila del producto como algo poco prioritario.
  4. Configuramos una alerta para detectar cierto aumento en el porcentaje de uso de esa funcionalidad.

Otro indicador importante es el crash rate del producto, cuántas veces se detecta un fallo o error por cantidad de uso, que nos permite saber lo estable que es un producto. Y mezclado con el indicador de números de uso por funcionalidad, nos permite detectar los puntos problemáticos.

Dependiendo del contexto del producto y del negocio, posiblemente podamos sacar también otros indicadores relevantes de operaciones y soporte que sean consecuencia del uso del producto.

Medir sin perder el foco

Estos son muchos KPIs distintos. Es interesante observarlos porque nos pueden servir para detectar olores sobre problemas y oportunidades de mejora, pero son demasiados para tratar de mejorar todo a la vez.

Para evitar diluirnos y no terminar mejorando en nada, deberíamos elegir y enfocarnos en un par de esos indicadores cada vez, dependiendo de la situación de cada equipo.

Y aunque sea tentador hacerlo, evitaría usar alegremente estos indicadores para marcar objetivos, así como para evaluar a equipos distintos.

No olvidemos que el propósito de medir estos indicadores es observar la evolución en el tiempo de un equipo trabajando en un producto y contexto determinado.

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

Blog Bitix

Teses unitarios parametrizados con JUnit

junio 07, 2019 03:00

Hay múltiples lenguajes y librerías donde cada una publica nuevas versiones. Una vez elegida una esa decisión no tiene que ser para siempre si las circunstancias de un proyecto cambian o una nueva versión incorpora las funcionalidades por las que se eligió otra. Si se reconsideran de nuevo el conjunto de todos los parámetros la decisión puede ser distinta. Esto me ha ocurrido al evaluar de nuevo JUnit comparándolo con Spock, teniendo en cuenta que en JUnit 5 han incorporado los teses parametrizados y el lenguaje que utiliza cada una de ellas.

JUnit
Java

En mis preferencias de herramientas que elegiría para un proyecto basado en la plataforma Java estaba Spock, por la legibilidad de los teses con su lenguaje específico de dominio o DSL con sus diferentes secciones given, when, then. Otro motivo era la posibilidad de ejercitar un mismo test pero con diferentes parámetros para ejecutar todas las condiciones del sujeto bajo prueba con la sección where y las posibilidades de mocking incorporadas. Pero Spock usa el lenguaje Groovy. Es menos verboso, es dinámico pero que no posee igual la asistencia de código de los IDEs y por su naturaleza dinámica con posibilidad de errores de compilación no detectados hasta en tiempo de ejecución. En mis preferencias está el lenguaje Java así que he revisado si estás características de Spock son ofrecidas por JUnit desde la última vez que lo use.

El primer motivo de usar Spock sobre la legibilidad del test se puede suplir añadiendo un comentario de línea con la sección. El segundo motivo es que en JUnit también se pueden crear teses parametrizados con varios casos de prueba. Para los teses parametrizados se puede usar la anotación @ParameterizedTest con una serie de valores que en el test se reciben como un parámetro.

Aqui se compara el mismo test usando Spock y luego JUnit.

 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
import spock.lang.Specification
class StringLengthCalculator {
int length(String string) {
return string.length()
}
}
class StringLengthCalculatorSpock extends Specification {
def "calculate string length"() {
given:
def calculator = new StringLengthCalculator()
expect:
def result = calculator.length(a)
then:
expected == result
where:
a | expected
"" | 0
"java" | 4
"groovy" | 5
"go" | 2
}
}
 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
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.provider.Arguments;
public class StringLengthCalculator {
public int length(String string) {
return string.length()
}
}
public class StringLengthCalculatorTest {
@ParameterizedTest
@DisplayName("calculate string length")
@ValueSource(strings = { "", "java", "groovy", "go" })
void lengthOfStrings(String a) throws Exception {
// given
 StringLengthCalculator calculator = new StringLengthCalculator();
// expect
 int result = calculator.length(a);
// then
 Assertions.assertEquals(expected, result);
}
}

O si la parametrización es más compleja usando un método que devuelve una lista de parámetros en Junit.

 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
import spock.lang.Specification
class Calculator {
int add(int a, int b) {
return a + b
}
}
class CalculatorSpock extends Specification {
def "calculate sum"() {
given:
def calculator = new Calculator()
expect:
def result = calculator.add(a, b)
then:
expected == result
where:
a | b | expected
1 | 3 | 4
2 | -1 | 1
0 | 6 | 6
}
}
 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
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
public class Calculator {
public int add(int a, int b) {
return a + b;
}
}
public class CalculatorTest {
@ParameterizedTest
@DisplayName("calculate sum")
void lengthOfStrings(int a, int b, int expected) throws Exception {
// given
 Calculator calculator = new Calculator();
// when
 int result = calculator.add(a, b);
// then
 Assertions.assertEquals(expected, result);
}
static Stream<Arguments> lengthOfStrings() {
return Stream.of(
Arguments.arguments(1, 3, 4),
Arguments.arguments(2, -1, 1),
Arguments.arguments(0, 6, 6),
);
}
}

Con estas posibilidades de JUnit y para hacer mocking con Mockito realmente los dos motivos que tenía para usar Spock no son imprescindibles además de disponer de un lenguaje con buena asistencia de código en los IDEs. También para los teses igualmente se aplican las 10 razones que tengo para seguir usando Java.

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

Picando Código

Actualización del validador de Cédula de Identidad Uruguaya

junio 05, 2019 09:01

Hace unos años creé mi primera gema en Ruby: Un validador de cédulas de identidad uruguaya. Poco más de un año después, escribí una versión en JavaScript. Desde entonces han surgido un montón de versiones más del validador de cédulas de identidad en distintos lenguajes. Recientemente actualicé el sitio web del proyecto: Validación Cédula Identidad Uruguaya

Validador de Cédulas de Identidad Uruguaya

La idea principal de las bibliotecas en Ruby, JavaScript y demás, es validar números de cédula ingresador por un usuario en un sitio/aplicación. La motivación de escribir la primera versión fue la cantidad de sitios uruguayos que lo hacen mal (ver entrada). Así que este sitio web utiliza el JavaScript y sirve para:

  • Ver si un número de cédula es válido
  • Obtener el dígito verificador de un número de cédula
  • Generar números de cédula al azar.

Además de una lavada de cara (o CSS y markup) al sitio, agregué las dos versiones más recientes del validador de cédulas: PHP y Go. La versión de Go la escribí el año pasado como ejercicio mientras aprendía Go para el trabajo. Qué lenguaje que no me gustó… Tanto que me había olvidado que escribí el validador de cédulas en Go, hasta que me puse a escribir esto.

Así que por el momento hay implementaciones open source para: Ruby, Python, JavaScript, jQuery, Node, PHP, y Go.
Si falta la versión en su lenguaje de programación favorito, y se animan a escribirlo y publicarlo bajo código libre ¡contáctenme que lo agrego!

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

Picando Código

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

junio 05, 2019 07:36

NotPinkCon

NotPinkCon es un evento gratuito y abierto a todo público, donde se brindan conferencias técnicas en seguridad informática, impartidas por mujeres y dirigidas a estudiantes, profesionales y entusiastas del tema. 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.

El evento se realiza el 30 de agosto,  en Buenos Aires, Argentina.

El team de NotPinkCon está compuesto por un grupo de mujeres especialistas en seguridad informática, reconocidas tanto por sus investigaciones, como por sus presentaciones en diversos eventos alrededor del mundo y su gran ambición en lograr la igualdad de género en el área de la tecnología. Conocé al staff

Si querés ser oradora en NotPinkCon, está abierto el llamado a charlas con fecha límite el 15 de julio. Los temas para los que están buscando oradoras son: seguridad ofensiva y defensiva en todas las áreas de infosec. Por ejemplo: Seguridad en apps web, hacking móvil, hacking IoT, Reversing, escribir exploits, seguridad wireless, hacking de hardware, malware, ataques criptográficos y así. Ofrecen a las oradoras cubrir los gastos por el viaje hasta USD 1.500 y dos noches de alojamiento, entrada VIP, cena de oradoras y acceso a fiestas.

Ya podés registrar tu entrada, el evento es abierto a todo público, pero tiene una capacidad limitada.

Pueden seguir a @NotPinkCon en Twitter para estar al tanto de todas las novedades.

Objetivo de NotPinkCon

El principal objetivo de NotPinkCon es incentivar a más mujeres a participar como speakers en los eventos de seguridad informática. Creemos que todas tienen investigaciones interesantes para contar y queremos romper las barreras que les impidan hacerlo.

Para ello proponemos un escenario más confortante, que funcione como un escalón intermedio antes de dar el salto de presentarse en un escenario mixto, el cual -por la desafortunada desigualdad de género- podría resultar algo intimidante para comenzar.

Asimismo también queremos contar en nuestro escenario con oradoras experimentadas. Sin lugar a dudas, su experiencia en el escenario no solo será de ayuda para las nuevas speakers sino también de gran inspiración para la audiencia.

Nuestro segundo objetivo es incentivar a las mujeres a unirse al fascinante mundo de la seguridad informática. Hemos notado que el hecho de que las oradoras sean mujeres incrementa el interés del género, lo cual se ve reflejado positivamente en una audiencia de gran paridad entre hombres y mujeres.

En resumen, NotPinkCon tiene los siguientes objetivos:

  • Incentivar la participación de mujeres como speakers en los eventos.
  • Incrementar el interés de las mujeres por la Seguridad Informática.

Visitá el sitio web para ver más.

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

Variable not found

Índices y rangos en C# 8 (1 de 2)

junio 04, 2019 08:28

.NET Core Seguimos analizando las novedades que traerá C# 8, y esta vez vamos a detenernos en una característica que aportará algo más de agilidad a la hora de trocear o acceder a elementos de arrays y algunos tipos de colecciones similares, como Span<T>.

Como muchas otras características del lenguaje, se trata de algunos azucarillos sintácticos creados en torno a dos nuevos tipos añadidos a las bibliotecas básicas del framework: las estructuras System.Index y System.Range. Por esta razón, para utilizar estos elementos no sólo es necesario disponer de nuevos compiladores, sino también de nuevas versiones del framework.
Recordad que a día de hoy ya se puede probar C# 8 en Visual Studio 2019 o directamente desde la interfaz de línea de comandos de .NET Core.

Índices de arrays con System.Index

Hasta esta versión de C#, la forma natural de acceder a un elemento específico de un array era indicando el entero que representaba su posición en el mismo. Por ejemplo, con arr[0] podíamos acceder al primer elemento de la colección, o con arr[arr.Length-1] al último.

La nueva estructura Index proporciona una fórmula para almacenar y gestionar índices usando tipos específicamente diseñados para ello, lo que permite añadir algo más de flexibilidad al resultado. Su uso básico, que no parece demasiado útil, es el siguiente:
var primes = new[] { 2, 3, 5, 7, 11, 13, 17, 19 };

Index i0 = new Index(0);
Index i1 = new Index(1);
Index i7 = new Index(7);

Console.WriteLine(primes[i0]); // 2
Console.WriteLine(primes[i1]); // 3
Console.WriteLine(primes[i7]); // 19
Así al vistazo no parece algo que valga demasiado la pena, ¿verdad? Bueno, pues como aportación interesante, a la hora de crear un índice podemos indicar si la referencia la hacemos desde el principio o desde el final de la secuencia:
var primes = new[] { 2, 3, 5, 7, 11, 13, 17, 19 };

Index fromStart3 = new Index(3); // Por defecto, 'fromEnd' es falso
Index fromEnd1 = new Index(1, fromEnd: true);
Index fromEnd8 = new Index(8, fromEnd: true);

Console.WriteLine(primes[fromStart3]); // 7
Console.WriteLine(primes[fromEnd1]); // 19
Console.WriteLine(primes[fromEnd8]); // 2
¡Alto ahí! Fijaos en un detalle sumamente importante: cuando comenzamos por el final, el índice 1 es el primer elemento. O en otras palabras, un acceso desde el final al índice 0 daría un error, porque este elemento sería posterior al último item de la colección; sin embargo, cuando contamos desde el principio, el índice cero es el primero.
var ceroFromStart = new Index(0);
var ceroFromEnd = new Index(0, fromEnd: true);
Console.WriteLine(primes[ceroFromStart]); // 2
Console.WriteLine(primes[ceroFromEnd]); // Error: IndexOutOfRangeException
Por último, cabe añadir que la estructura Index dispone de algunos métodos estáticos para agilizar la creación de índices, como Index.FromStart() o Index.FromEnd(), o para obtener referencias al comienzo y final (recordad, ¡fuera del array!) con Index.Start e Index.End respectivamente:
Index fromEnd3A = Index.FromEnd(3);
Index fromEnd3B = new Index(3, fromEnd: true);
Console.WriteLine(fromEnd3A.Equals(fromEnd3A)); // True

var start = Index.Start;
var alsoStart = Index.FromStart(0);
Console.WriteLine(start.Equals(alsoStart)); // True

Conversiones implícitas y el operador hat ^

Anteriormente hemos visto lo fácil que es crear índices y utilizarlos para acceder a los elementos de una colección. Pero, obviamente, esto no podía quedar ahí.

En primer lugar, el tipo Index dispone de conversiones implícitas hacia y desde int, lo que quiere decir que normalmente podremos utilizar estos dos tipos indistintamente, como se muestra en el siguiente ejemplo:
var primes = new[] { 2, 3, 5, 7, 11, 13, 17, 19 };
Index fromStart3a = new Index(3);
Index fromStart3b = 3;
int fromStart3c = 3;

Console.WriteLine(primes[fromStart3a]); // 7
Console.WriteLine(primes[fromStart3b]); // 7
Console.WriteLine(primes[fromStart3c]); // 7
Console.WriteLine(fromStart3a.Equals(fromStart3c)); // True
Vale, esto simplifica la codificación de índices comenzando por el principio, igual que lo hemos hecho siempre con enteros, pero, ¿qué ocurre con la indexación desde el final?

Y aquí es donde entra en juego el nuevo uso del operador hat (sombrero) ^ de C# 8. Este operador es un mero azucarillo sintáctico para facilitar la creación de índices desde el final, como hacíamos con Index.FromEnd(), pero de forma más compacta. Veamos unos ejemplos:
var primes = new[] { 2, 3, 5, 7, 11, 13, 17, 19 };
var fromEnd2A = Index.FromEnd(2);
var fromEnd2B = ^2; // = Index.FromEnd(2)

Console.WriteLine(primes[fromEnd2A] == primes[fromEnd2B]); // True
Console.WriteLine(primes[fromEnd2B]); // 17
En el siguiente post veremos cómo la nueva estructura Range permite especificar rangos o extraer porciones de colecciones indicando los límites superiores e inferiores, y cómo se vale de Index para ello.

Publicado en: www.variablenotfound.com.

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

Variable not found

Enlaces interesantes 364

junio 03, 2019 06:12

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

Por si te lo perdiste...

.NET / .NET Core

ASP.NET / ASP.NET Core

Azure / Cloud

Conceptos / Patrones / Buenas prácticas

Data

Machine learning / IA / Bots

Web / HTML / CSS / Javascript

Visual Studio / Complementos / Herramientas

Xamarin

Otros

Publicado en Variable not found.

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

Navegapolis

Una buena idea sin un buen "product owner" es sólo una ilusión

mayo 31, 2019 10:49

poUna buena idea no es un buen producto.

Faltaría añadir: "si no hay detrás un buen propietario de producto". Escuchando a Steve Jobs explicar porqué Apple dejó de hacer buenos productos cuando él se fue, igual es "agile-tendencioso" pensar que perdieron al product owner, pero si lo que dejaron fue a la idea con el equipo, quitando a quien tenía la visión, conocía el producto y analizaba de forma continua su evolución, quizá no fuera el product owner, pero se le parecía mucho.

 

 

 

 

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

Navegapolis

Una buena idea sin un buen "product owner" no es un buen producto

mayo 31, 2019 10:49

poUna buena idea no es un buen producto.

Faltaría añadir: "si no hay detrás un buen propietario de producto". Escuchando a Steve Jobs explicar porqué Apple dejó de hacer buenos productos cuando él se fue, igual es "agile-tendencioso" pensar que perdieron al product owner, pero si lo que dejaron fue a la idea con el equipo, quitando a quien tenía la visión, conocía el producto y analizaba de forma continua su evolución, quizá no fuera el product owner, pero se le parecía mucho.

 

 

 

 

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

Blog Bitix

Implementar la paginación eficientemente en consultas SQL con seek

mayo 31, 2019 04:00

Las aplicaciones que muestran listados probablemente necesiten mostrarlos paginados. Sin embargo, implementar la paginación correctamente para que sea eficiente no pasa por emplear las clausulas limit ni offset que habitualmente se utilizan sino con seek. Además de que limit y offset da lugar a resultados no deseados si entre obtención de página y página se insertan filas en páginas anteriores.

jOOQ
Java

La paginación habitualmente se implementa con las palabras reservadas limit y offset del lenguaje SQL pero esto es ineficiente ya que para llegar los resultados de las últimas páginas la base de datos ha de recuperar antes todos los resultados anteriores. Cuando hay varios cientos de miles o millones de filas en una tabla esto es ineficiente y hace que las consultas sean lentas y añadan una importante carga al servidor de base de datos que al final afecta al rendimiento de la aplicación o su capacidad para atender a gran número de peticiones.

Además del rendimiento, otro problema de la paginación con limit y offset es que si se insertan filas en una página anterior mientras se están recorriendo los resultados, uno o varios resultados al avanzar por las páginas en la típica tabla de resultados en una aplicación web podría aparecer dos veces, en un proceso automatizado sería aún peor ya que un resultado podría procesarse dos veces.

La paginación con limit y offset permite ir a una página directamente en una búsqueda pero en los casos que hay miles de resultados realmente a un usuario no le interesa ir a una determinada página cuando hay cientos de páginas, en un proceso automatizado ir a una página en concreto tampoco suele ser un requerimiento.

jOOQ con la cláusula seek permite hacer la paginación eficiente de una forma cómoda. La técnica se basa en ordenar los resultados por unas determinadas columnas y filtrar por condición where sobre las mismas columnas los resultados anteriores, los valores por los que se filtra en la condición son los obtenidos de la última página.

Haciendo seek la paginación es más eficiente ya que la base de datos no necesita recuperar los datos de las páginas anteriores ya que los descarta utilizando la cláusula where que al final es en lo que se traduce la clausula seek. Y se elimina el problema de que se inserten datos en páginas anteriores y alguno se pudiese aparecer dos veces en los resultados ya que manteniendo la ordenación de la conlsulta si se insertan filas en páginas anteriores no afectarán a las páginas siguientes.

1
2
3
4
5
6
7
8
9
import org.jooq.DSLContext;
...
private DSLContext context;
...
context.selectFrom(Tables.PRODUCTO)
.orderBy(PRODUCTO.CANTIDAD, PRODUCTO.ID)
.seek(3l, 2l)
.limit(10)
.fetchInto(Producto.class);

La SQL generada por jOOQ es la siguiente donde la clausula seek se añade como una condición en la cláusula where. El campo de la clausula seek coincide con el campo del criterio de ordenación, el operador mayor que en la condición coincide también con el orden ascendente del order by.

1
2
3
4
 select "PRODUCTO"."ID", "PRODUCTO"."NOMBRE", "PRODUCTO"."DESCRIPCION", "PRODUCTO"."CANTIDAD", "PRODUCTO"."FECHA"
from "PRODUCTO"
where ("PRODUCTO"."CANTIDAD", "PRODUCTO"."ID") > (cast(? as bigint), cast(? as bigint))
order by "PRODUCTO"."CANTIDAD", "PRODUCTO"."ID" limit ?

Para que el seek sea correcto los valores de las filas para los campos que forman parte del seek han de ser únicos por eso entre los campos de los ejemplos se incluye el identificativo de la fila, que siempre se incluye como último campo si hubiese más criterios de ordenación y valores para el seek.

Si además se tiene un índice para los campos que forman parte del seek el rendimiento será muy alto y la diferencia entre la primera y la última página mínimo, además se puede considerar independiente del número de filas de la tabla. Son varias las ventajas perdiendo solo la capacidad de navegar a cierta página pero en la mayoría de los casos esto es asumible.

En los siguientes interesantes artículos se comenta detalladamente como implementar la paginación eficientemente y se dan más detalles.

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

Una sinfonía en C#

IoT bases: Introducción a MQTT

mayo 27, 2019 10:00

En este post vamos a comenzar a hablar sobre uno de los protocolos más utilizados para IoT, MQTT y conocer sus principales características, por qué es interesante para IoT, qué aporta, y cómo funciona.

https://www.penninkhof.com/wp-content/uploads/2015/03/MQTT.png

¿Qué es MQTT?

MQTT son las siglas de Message Queuing Telemetry Transport, es un protocolo pensado para telemetría, algunas cosas para remarcar:

  • Funciona sobre TCP.
  • Existen variantes sobre UDP y Bluetooth (por supuesto, con otras características de transporte).
  • Utiliza el patrón Publish/Suscribe.
  • Hace muy simple la comunicación entre múltiples dispositivos.
  • Diseñado para dispositivos pequeños y de bajo consumo de energía y ancho de banda.
  • Bidireccional.

Pubish/Suscribe

Bien, vamos por lo primero, decimos que MQTT es Publish/Suscriber, esto tiene varias implicaciones:

  • Si queremos recibir un mensaje tenemos que suscribirnos a un “tema”.
  • Si queremos publicar un mensaje lo hacemos a un “tema”.
  • No podemos enviar un mensaje directamente a otro cliente.
  • No podemos saber quién nos ha enviado un mensaje.
  • Si hay muchos dispositivos suscritos a un “tema” recibirán todos el mismo mensaje.
  • Si estamos suscritos a un “tema” recibimos todos los mensajes publicados por cualquier cliente a ese “tema”.

Por ejemplo, podemos suscribirnos al tema “casa” y cada vez que algún cliente publique un mensaje en ese tema lo recibiremos; del mismo modo, siempre que enviemos un mensaje tendremos que indicar el “tema” y este mensaje será recibido por todos los suscriptores (y nosotros no sabremos quiénes son).

Una de las ventajas de este modelo es que mucha responsabilidad no la gestionan los clientes y esto hace que sea simple desde el punto de vista del dispositivo.

El corazón de MQTT, el Broker.

Una cosa importante de señalar es cómo nos conectamos, como nos suscribimos y cómo publicamos; bien, los clientes MQTT nunca se conectan entre sí sino que se conectar a un Broker, que no es más que un intermediario que se encarga de conocer a todos los clientes y repartir los mensajes según las suscripciones. Hace algunas cosas más que iremos viendo.

Entonces, el Broker no es más que una aplicación que tiene que estar instalada y funcionando todo el tiempo, muchas de las ventajas de MQTT relacionadas con el bajo consumo de recursos tienen que ver con que el Broker es responsable de muchas cosas, y es donde reside la mayor parte de la complejidad del protocolo.

El diagrama de funcionamiento seria éste:

 

By Simon A. Eugster - Own work, CC BY-SA 4.0, https://commons.wikimedia.org/w/index.php?curid=70622928

Como vemos en el diagrama, el Client A se conecta al Broker y luego se suscribe al "tema” (Topic) temperature/roof, al lado derecho vemos el Client B que ya se encontraba conectado y publicó un mensaje sobre el mismo Topic, por lo tanto el Broker a ver que Client A se suscribe al Topic le entrega el mensaje. Más adelante Client A publica sobre el mismo Topic la temperatura actual (20º), recibe otro mensaje del Broker (30º), responde lo mismo y se desconecta.

Este diagrama podría perfectamente el funcionamiento de un termostado de una casa que recibe la configuración de temperatura de alguien más.

Nótese que el primer mensaje publicado por Client B dice “retain” esto es un flag especial que hace que ese mensaje quede retenido de modo que todos los nuevos suscriptores lo reciban ni bien se conectan el Topic, esto es especialmente útil cuando los clientes solo se conectan cuando necesitan interactuar con el Broker y de este modo ahorrar energía.

Topics

Los “temas” que nombramos antes se llaman Topics y se organiza en niveles, del siguiente modo:

sport/tennis/player1

De este modo podemos organizar los mensajes, ya que tenemos la posibilidad de suscribirnos a un Topic o a diferentes niveles gracias a los willcards, por ejemplo:

sport/# suscribe a todos los niveles debajo de sport

sport/#/ranking suscribe a todos los ranking de todos los deportes

por otro lado el + permite usar un willcard de un único nivel

sport/tennis/+/ranking suscribe a todos los rankings, es decir el + solo reemplaza un nivel

Hay mucho más detalles al respecto en la especificación.

Calidad de servicio QoS

Existen 3 niveles de calidad en MQTT 0,1 y 2.

Estos niveles determinan con que rigurosidad el Broker debe asegurarse que los mensajes han sido entregados correctamente.

  • 0: Se envían el mensaje y no se espera ninguna verificación (fire en forget).
  • 1: Se espera al menos un ACK de cada clientes que debe recibir el mensaje (si el cliente tarda en responder es posible que se envíe dos veces el mismo mensaje).
  • 2: Se garantiza que cada cliente recibe el mensaje una única vez.

Evidentemente a medida que subimos la calidad los mensajes son más costosos a nivel recursos de energía y tiempo, mayormente se usa el nivel 0 o 1, de hecho muchos Brokers y clientes no implementan el nivel 2.

El nivel de calidad se puede definir al conectarse al Broker (el cliente decide qué nivel de validad quiere) o en cada mensaje enviando (quien envía decide el nivel)

Retención de mensaje (retain message)

Como vimos en el diagrama, esto no es más que un flag que hace que un mensaje en particular (podemos agregarlo en todos, en tal caso siempre se considera el último mensaje por Topic) sea retenido por el Broker de modo de que al suscribirse un nuevo cliente el Topic reciba este mensaje como primer mensaje, esto es muy útil para valores por defecto y cosas o cuando quien publica mensaje lo hace cada cierto tiempo, entonces el mensaje queda ahí y todo el que se conecte al Topic lo recibirá por más que quien generó el mensaje no se encuentre operativo.

Sesiones

Lo último de lo que vamos a hablar es de sesiones, es otra característica avanzada y básicamente hace que el Broker le asigne un número de sesión a cada clente (que se conecte, independientemente de si se suscribe a un Topic) y recuerde este identificador de sesión, de modo que si el cliente se desconecta (otra vez, porque se apaga para ahorrar energía o ocurre algo) pueda informar su sesión y el Broker continue por donde quedó. Otra vez, no todos los Broker y clientes lo implementan.

En la próxima entrada vamos a ver qué software podemos usar para hacer las primeras pruebas con MQTT.

Nos leemos.

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

Blog Bitix

Registro y descubrimiento de servicios en contenedores de Docker con Consul y Registrator

mayo 26, 2019 09:30

En los microservicios se hace necesario un servicio de registro y descubrimiento como Eureka o Consul que permita conocer la ubicación de las instancias en cada momento. Las instancias de los servicios se pueden registrar ellas mismas o esta tarea se puede delegar en una en otro servicio. Al usar contenedores de Docker una herramienta que permite delegar el registro y desregistro en Consul de los servicios es GliderLabs Registrator.

Consul
Docker

El registro y descubrimiento de servicios permite a los servicios registrase y a los clientes descubrir la ubicación de otros servicios, la ubicación consiste en la dirección IP y el puerto en el que contactarles. Dado la naturaleza efímera de los servicios donde nuevas instancias de servicios se inician y se detienen en diferentes máquinas y puertos el servicio de descubrimiento es esencial.

La funcionalidad de registro y descubrimiento consiste en dos partes, por un lado cuando se inicia una instancia de un servicio se registra su ubicación en el servicio de registro y descubrimiento y por otro lado los clientes cuando requieren una instancia de un servicio la buscan en el servicio de descubrimiento.

El registro en el servicio de descubrimiento puede hacerse de dos formas, que sea el propio servicio el que se registra en el servicio de descubrimiento o que se sea otro servicio el que lo registra. Para el primer caso escribí un artículo con Consul como servicio de descubrimiento en una aplicación de Spring Boot que se registra al iniciarse. La ventaja es que es autosuficiente pero adquiere la tarea de autoregistrarse. Por el contrario delegar la trea de registro permite extraerla de los servicios y ofrecer esa funcionalidad por un servicio con esa misión específicamente.

En este artículo se usa GliderLabs Registrator como servicio que se encarga de registrar en un servicio de descubrimiento como Consul los servicios que se inicien en Docker, aunque soporta otros como etcd.

Registrator es un contenedor de Docker, su funcionamiento es escuchar los eventos del demonio de Docker y monitorizar cuando se inician nuevos contenedores o cuando se paran. La monitorización la hace a través del socket del servicio de Docker, para lo que hay que montar un volumen en este contenedor con el archivo /var/run/docker.sock del host.

Primero se inicia el servicio de Consul.

1
$ consul agent -dev

Luego se inicia el contenedor Registrator indicando la ubicación con dirección IP y puerto del servicio de Consul.

1
$ docker run --rm --name=registrator --net=host --volume=/var/run/docker.sock:/tmp/docker.sock gliderlabs/registrator:latest consul://localhost:8500

Iniciados estos dos servicios en la interfaz de estado de Consul se observa que no hay ningún servicio pero cuando se inicie un nuevo contenedor será registrado en Consul por Registrator.

En este caso se utiliza como servicio una base de datos PostgreSQL. Dado que el puerto en el que esté disponible el servicio de PostgreSQL es indiferente al utilizar un servicio de registro y descubrimiento se indica el -p sin indicar el puerto del host, de este modo Docker le asigna un puerto público aleatorio.

1
$ docker run --rm --name postgres -e POSTGRES_USER=user -e POSTGRES_PASSWORD=password -e POSTGRES_DB=database -p 5432 postgres:alpine
1
2
3
4
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cb7602605725 postgres:alpine "docker-entrypoint.s…" 54 seconds ago Up 53 seconds 0.0.0.0:32777->5432/tcp postgres
d286341148cb gliderlabs/registrator:latest "/bin/registrator co…" About a minute ago Up About a minute registrator

En la salida del contenedor de Registrator se emite una traza indicando que el servicio de postgres ha sido registrado en Consul.

1
2
3
4
5
6
7
8
2019/05/26 11:05:29 Starting registrator v7 ...
2019/05/26 11:05:29 Using consul adapter: consul://localhost:8500
2019/05/26 11:05:29 Connecting to backend (0/0)
2019/05/26 11:05:29 consul: current leader 127.0.0.1:8300
2019/05/26 11:05:29 Listening for Docker events ...
2019/05/26 11:05:29 Syncing services on 1 containers
2019/05/26 11:05:29 ignored: d286341148cb no published ports
2019/05/26 11:05:50 added: cb7602605725 archlinux:postgres:5432

Una vez iniciado el servicio de postgres en la consola de Consul se muestra con su dirección y puerto en el que se encuentra, en el contenedor utiliza su puerto por defecto 5432 pero hacia el exterior en este caso al no haber especificado uno Docker le asigna un puerto aleatorio en este caso el 32777. Este puerto aleatorio es con el que los clientes acceden a la base de datos.

Servicio de postgres registrado en Consul por Registrator

En vez de iniciar los servicios individualmente con comandos de Docker creando un archivo de Docker Compose con la definición de todos los contenedores se facilita iniciar todos los contenedores con un comando.

1
$ docker-compose up
 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
version: "3.7"
services:
 consul:
 image: consul:latest
 container_name: consul
 network_mode: "host"
 command: ["consul", "agent", "-dev", "-ui"]
 registrator:
 image: gliderlabs/registrator:latest
 container_name: registrator
 network_mode: "host"
 volumes:
 - /var/run/docker.sock:/tmp/docker.sock
 entrypoint: /bin/registrator consul://localhost:8500
 depends_on:
 - consul
 postgres:
 image: postgres:alpine
 container_name: postgres
 ports:
 - "5432"
 environment:
 - POSTGRES_USER=user
 - POSTGRES_PASSWORD=password
 - POSTGRES_DB=database
 depends_on:
 - registrator

El proyecto de Spring Cloud ofrece soporte para ambas tareas de registrar y descubrir servicios, aunque perfectamente la tarea de registro se puede delegar como en este caso a Registrator y utilizar en los servicios de Spring Boot únicamente la parte de descubrimiento.

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 ./docker-compose-up.sh.

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

Blog Bitix

Log de sentencias SQL, sentencias lentas y otra información en jOOQ

mayo 24, 2019 04:00

Algunas de las causas del bajo rendimiento de una aplicación que utiliza una base de datos son el número de sentencias que se realizan junto con las sentencias lentas por su coste de ejecución. Cada ejecución de una sentencia significa una comunicación por la red y ejecutar muchas de ellas significa un considerable y perceptible tiempo para el usuario. Por ello es conveniente saber que sentencias se ejecutan, si hay algún problema de 1+N o sentencias innecesarias que se repiten. Un log de las sentencias que se ejecutan es muy útil para detectar ineficiencias en la aplicación y corregirlas.

jOOQ
Java

La librería jOOQ devuelve al lenguaje SQL de consultas para bases de datos relacionales al primer plano en una aplicación Java. En vez de crear una capa de abstracción como realiza la popular librería Hibernate del modelo relacional al modelo orientado a objetos de Java permite construir con un DSL mediante su API fluída avanzadas sentencias SQL que soportan las versiones recientes de PostgreSQL y MySQL.

En Hibernate la configuración de statistics, el logger org.hibernate.SQL y con el parámetro show_sql permiten visualizar que sentencias SQL se están lanzando, útil para conocer si algún problema de 1+N al navegar relaciones que ocasionalmente se producen si no son tenidas en cuenta. Además de que sentencias se están lanzando es también interesante conocer que tiempo de ejecución está tomando cada sentencia para conseguir que el rendimiento de la aplicación no sea lento, para detectar sentencias lentas.

Usando Spring Boot y la dependencia de jOOQ hay que proporcionar una instancia que implemente la interfaz ExecuteListener o crear una instancia de DefaultExecuteListener. Esta clase contiene numerosos métodos que permiten conocer y realizar acciones, en este caso emitir trazas. Dos de los métodos son executeStart() y executeEnd() invocados por jOOQ antes y después de cada sentencia que lanza. Usando System.nanoTime() se mide el tiempo de ejecución.

 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
package io.github.picodotdev.plugintapestry.spring;
...
@Configuration
@ComponentScan({ "io.github.picodotdev.plugintapestry" })
@EnableTransactionManagement
public class AppConfiguration {
@Bean(destroyMethod = "close")
public DataSource dataSource() {
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName(Driver.class.getCanonicalName());
ds.setUrl("jdbc:h2:./misc/database/app");
ds.setUsername("sa");
ds.setPassword("sa");
return ds;
}
...
@Bean
public ConnectionProvider connectionProvider(DataSource dataSource) {
return new DataSourceConnectionProvider(dataSource);
}
@Bean
public ExecuteListenerProvider executeListenerProvider() {
return new ExecuteListenerProvider() {
@Override
public ExecuteListener provide() {
return new JooqExecuteListener();
}
};
}
@Bean
public org.jooq.Configuration config(ConnectionProvider connectionProvider, ExecuteListenerProvider executeListenerProvider) {
DefaultConfiguration config = new DefaultConfiguration();
config.set(connectionProvider);
config.set(SQLDialect.H2);
config.set(executeListenerProvider);
return config;
}
@Bean
public DSLContext dsl(org.jooq.Configuration config) {
return DSL.using(config);
}
...
}
 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
package io.github.picodotdev.plugintapestry.misc;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jooq.ExecuteContext;
import org.jooq.impl.DefaultExecuteListener;
import java.math.BigDecimal;
import java.math.MathContext;
public class JooqExecuteListener extends DefaultExecuteListener {
private static final Logger logger = LogManager.getLogger(JooqExecuteListener.class);
private long start;
private long end;
@Override
public void executeStart(ExecuteContext ctx) {
start = System.nanoTime();
}
@Override
public void executeEnd(ExecuteContext ctx) {
end = System.nanoTime();
logger.info("{} ({}ms)", ctx.sql(), getTime(start, end));
}
private String getTime(long start, long end) {
return new BigDecimal(end - start, new MathContext(4)).divide(new BigDecimal("1000000")).toString();
}
}

En las siguientes trazas de ejecución de sentencias se observa una inserción de un registro (1), una posterior muestra del listado para lo que se ralizan dos sentencias una que cuenta el número de elementos en la tabla con un select count(*) que junto con el número de elementos por página permite conocer cuantas páginas hay y un select con un limit ? para recupear los datos de la primera página (2). Con el suficiente número de elementos en la tabla se hace una consulta con un limit ? y un offset ? para los elementos de una página posterior a la primera (3). Al eliminar un elemento de la tabla se ejecuta una sentencia delete con el identificativo de la fila a eliminar en la clausula where (4), finalmente si se utiliza el botón Eliminar todos se elimina todas las filas con otra sentencia delete pero sin especificar la clausula where (5). En cada sentencia se muestra el tiempo que ha tardado.

Estas sentencias se ejecutan en unos pocos milisegundos, en una aplicación con tablas de algún millon de registros, varios joins, condiciones where complejas, ordenación y paginación las sentencias SQL pueden tardar varias segundos y decenas de segundos, conocer sus tiempos de ejecución es importante.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// (1)
2019-05-24 19:00:00,829 ... INFO ...JooqExecuteListener insert into "PLUGINTAPESTRY"."PRODUCTO" ("ID", "NOMBRE", "DESCRIPCION", "CANTIDAD", "FECHA") values (cast(? as bigint), cast(? as varchar), cast(? as varchar), cast(? as bigint), cast(? as timestamp)) (1.887ms)
// (2)
2019-05-24 19:01:00,843 ... INFO ...JooqExecuteListener select count(*) from "PLUGINTAPESTRY"."PRODUCTO" (0.1474ms)
2019-05-24 19:01:00,879 ... INFO ...JooqExecuteListener select "PLUGINTAPESTRY"."PRODUCTO"."ID", "PLUGINTAPESTRY"."PRODUCTO"."NOMBRE", "PLUGINTAPESTRY"."PRODUCTO"."DESCRIPCION", "PLUGINTAPESTRY"."PRODUCTO"."CANTIDAD", "PLUGINTAPESTRY"."PRODUCTO"."FECHA" from "PLUGINTAPESTRY"."PRODUCTO" limit ? (0.2915ms)
// (3)
2019-05-24 19:02:00,289 ... INFO ...JooqExecuteListener select count(*) from "PLUGINTAPESTRY"."PRODUCTO" (0.1092ms)
2019-05-24 19:02:00,291 ... INFO ...JooqExecuteListener select "PLUGINTAPESTRY"."PRODUCTO"."ID", "PLUGINTAPESTRY"."PRODUCTO"."NOMBRE", "PLUGINTAPESTRY"."PRODUCTO"."DESCRIPCION", "PLUGINTAPESTRY"."PRODUCTO"."CANTIDAD", "PLUGINTAPESTRY"."PRODUCTO"."FECHA" from "PLUGINTAPESTRY"."PRODUCTO" limit ? offset ? (0.1623ms)
// (4)
2019-05-24 19:03:00,224 ... INFO ...JooqExecuteListener delete from "PLUGINTAPESTRY"."PRODUCTO" where "PLUGINTAPESTRY"."PRODUCTO"."ID" = cast(? as bigint) (1.19ms)
// (5)
2019-05-24 19:04:00,037 ... INFO ...JooqExecuteListener delete from "PLUGINTAPESTRY"."PRODUCTO" (0.391ms)
Listado de elementos

La clase ExecuteContext proporciona numerosa información sobre la ejecución de la sentencia como número de filas afectadas, si se ha producido una excepción, el tipo de sentencia (READ, WRITE, DDL, BATCH, ROUTINE u OTHER), sentencias batch u obtener los parámetros a través del objeto Query.

Simplemente mostrando las trazas de sentencias me ha permitido detectar que en el ejemplo se estaba realizando una pequeña ineficiencia. La sentencia select count(*) se lanzaba dos veces en la página de listado, una al querer saber si hay alguna fila y otra usada por el componente Grid de Tapestry. Para resolverlo se cachea el resultado en la clase anónima JooqGridDataSource con el siguiente código.

 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
package io.github.picodotdev.plugintapestry.pages.admin;
...
public class ProductoAdmin {
...
public boolean hasProductos() {
return source.getAvailableRows() > 0;
}
...
private void setModo(Modo modo, Producto producto) {
switch (modo) {
...
case LISTA:
this.producto = null;
this.source = new JooqGridDataSource(context, Producto.class) {
private int count = -1;
private List list = null;
@Override
public int getAvailableRows() {
if (count == -1) {
count = (int) dao.countAll();
}
return count;
}
@Override
public List find(Pagination pagination) {
if (list == null) {
list = dao.findAll(pagination);
}
return list;
}
};
break;
}
this.modo = modo;
}
}

Las bases de datos MySQL y PostgreSQL también ofrecen la posibilidad de emitir en las sentencias en un log incluidas las sentencias lentas. Otra posibilidad es resaltar la sintaxis de las sentencias en la salida a la terminal para una mejor lectura dando color a las palabras claves.

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.

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

Fixed Buffer

Como instalar un servicio Net Core en Linux

mayo 21, 2019 08:00

Banner de la entrada "Como instalar un servicio NetCore en Linux"

Hace un par de semanas hablamos de como poder crear un servicio Net Core que fuese multiplataforma, y probamos a instalarlo en Windows. Hoy vamos a continuar con el proceso y vamos a instalarlo en Linux para comprobar que realmente es multiplataforma (y de paso ver lo realmente fácil que es).

Para poder hacerlo, solo necesitamos 2 cosas:

  • Una máquina Linux con .Net Core.
  • Una aplicación .Net Core programada como un servicio.

Para la primera de las dos, te dejo un enlace a mi entrada hablando sobre «Como instalar .Net Core en Linux» , para la segunda, vamos a reutilizar el código que vimos en la última entrada. Para ello, desde nuestra terminal (asumo que no todo el mundo usa git, por lo que voy a utilizar la descarga http), vamos a escribir los comandos:

wget https://codeload.github.com/FixedBuffer/ServicioNetCore/zip/master
unzip -a master

Una vez descomprimido, vamos a navegar hasta el .csproj con:

cd ServicioNetCore-master
cd PostServicioNetCore

Y una vez dentro, simplemente vamos a publicarlo:

dotnet publish --configuration Release

Con estos pasos, ya tenemos un binario listo para registrar como servicio. ¡Vamos a ello!

Instalando nuestro servicio Net Core en Linux

En primer lugar, la ruta donde hemos generado los binarios es poco amigable (además de ser poco práctica), así que vamos a mover la carpeta a una ruta más apropiada:

sudo mv /home/jorturfer/ServicioNetCore-master/PostServicioNetCore/bin/Release//netcoreapp2.2/publish /usr/local/NetCoreService

*Estamos moviendo la carpeta publish entera a la ruta «/usr/local/», que es una ruta mucho más apropiada para nuestro servicio, aunque no es obligatorio.

Una vez que tenemos todo puesto en su sitio vamos a instalar un servicio NetCore en Linux, para ello, solo hay que crear su fichero de definición en «/etc/systemd/system/» y registrarlo. Para crear la definición, vamos a escribir:

sudo nano /etc/systemd/system/netcoreservice.service

Y dentro del fichero, vamos a rellenar el template:

[Unit]
Description=DESCRIPCION DEL SERVICIO

[Service]
WorkingDirectory= RUTA DONDE ESTAN LOS BINARIOS
ExecStart=/usr/bin/dotnet RUTA HASTA LA DLL
Restart=always
RestartSec=10
SyslogIdentifier=IDENTIFICADOR DEL SISTEMA
User=USUARIO QUE LO VA A EJECUTAR
Environment=VARIABLES DE ENTORNO QUE VA A RECIBIR

[Install]
WantedBy=multi-user.target

Después de rellenar los datos, nos quedará algo como esto:

[Unit]
Description=Servicio para la entrada de "Como instalar un servicio NetCore en Linux"
[Service]
WorkingDirectory= /usr/local/NetCoreService
ExecStart=/usr/bin/dotnet /usr/local/NetCoreService/PostServicioNetCore.dll
Restart=always
RestartSec=10
SyslogIdentifier=dotnet-postservice
User=jorturfer
Environment=ASPNETCORE_ENVIRONMENT=Production

[Install]
WantedBy=multi-user.target

Por último, vamos a registrarlo con el comando:

sudo systemctl enable netcoreservice.service

Después de esto, ya podemos manejar nuestro servicio Net Core en Linux como cualquier otro servicio con los comandos:

sudo service nombreservicio start/stop/status/restart

Vamos a comprobar que funciona bien, ejecutando:

sudo service netcoreservice start

Después de eso, deberíamos encontrar un fichero llamado «PostServicioCore.txt» junto al binario con un registro por cada minuto que ha estado arrancado. Además, gracias a SSH, vamos a poder asociarnos al proceso y depurarlo como si estuviese en nuestra maquina. (Incluso con Visual Studio Code gracias al último anuncio de Microsoft sobre depuración remota en la MSBuild2019)

Como hemos podido comprobar, el servicio que hicimos multiplataforma, realmente lo es, e instalar un servicio Net Core en Linux no tiene ningún misterio.

**La entrada Como instalar un servicio Net Core en Linux se publicó primero en Fixed Buffer.**

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

Metodologías ágiles. De lo racional a la inspiración.

Open Space, para un roto o para un descosido

mayo 15, 2019 12:24

NOTA: Estoy escribiendo la guia de facilitación del Open Space. Hemos hablado varias veces aquí ya de los Open Space como un formato increible para la organización y facilitación de conferencias. En Agile-Spain hemos organizado ya tres a nivel nacional, y han surgido multitud de pequeños "opens" para tratar muchos temas alrededor del agilismo. Si no sabes qué es un Open Space, echa un ojo a esta

» 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

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