viernes, 14 de octubre de 2022

[eBook] Microservices: From Design to Deployment

 

EBOOK

Ebook: Microservices: From Design to Deployment | Learn about building & deploying microservices

You probably already know that developing your apps using a microservices approach makes them more flexible, more stable, and faster. This eBook provides a useful set of resources to get started in your transition to microservices.

In this free eBook you will learn:

  • When and why it makes sense to adopt microservices
  • How to implement an API gateway to route traffic to microservices
  • Pros and cons of different service discovery patterns
  • Different strategies for refactoring a monolith to microservices

Learning go


Dejo este link que es una recopilación de recursos sobre go

https://go.dev/doc/#learning

martes, 11 de octubre de 2022

Transformadores de mónadas en cats


Las mónadas son como los burritos, lo que significa que una vez que adquieres un sabor, te encontrarás regresando a ellas una y otra vez. Esto no está exento de problemas. Así como los burritos pueden inflar la cintura, las mónadas pueden inflar la base del código a través de comprensiones anidadas. Imagina que estamos interactuando con una base de datos. Queremos buscar un registro de usuario. El usuario puede estar presente o no, por lo que devolvemos un Option[User]. Nuestra comunicación con la base de datos podría fallar por muchas razones (problemas de red, problemas de autenticación, etc.), por lo que este resultado se envuelve en un Either, dándonos un resultado final de Either[Error, Option[User]]. Para usar este valor, debemos anidar llamadas flatMap (o de manera equivalente, para comprensiones):

def lookupUserName(id: Long): Either[Error, Option[String]] =

    for {

        optUser <- lookupUser(id)

    } yield {

        for { user <- optUser } yield user.name

   }


Cada transformador de mónadas es un tipo de datos, definido en cats.data, que nos permite envolver pilas de mónadas para producir nuevas mónadas. Usamos las mónadas que hemos construido a través de la clase de tipo Monad. Los principales conceptos que tenemos que cubrir para entender los transformadores de mónadas son:

  • las clases de transformadores disponibles;
  • cómo construir pilas de mónadas usando transformadores;
  • cómo construir instancias de una pila de mónadas; y
  • cómo separar una pila para acceder a las mónadas envueltas.
Continuará... 

lunes, 10 de octubre de 2022

Probando tus test con stryker4s


Professor X: For someone who hates mutants... you certainly keep some strange company.

William Stryker: Oh, they serve their purpose... as long as they can be controlled.


Si tenemos que probar los test de mi aplicación entramos en una recursividad que nos puede mantener haciendo test de test hasta los fines de los tiempos. 

Pero pero esta copado, saber si realmente tus test prueban, lo que uno desea probar y no esta bueno darlo por sentado y tampoco es productivo, ir debugeando test por test para ver si realmente prueba. 

Por ende una herramienta como stryker4s esta buena, lo que hace esto es mutar tu código un toque y espera que los test fallen, si los test no fallan esa parte del código que muto no esta siendo testeada. 

La idea, me parece de lo más lógica, y no entiendo bien como no se nos ocurrio antes :( 

Para utilizarlo agregamos la siguiente dependencia en nuestro proyecto sbt : 

addSbtPlugin("io.stryker-mutator" % "sbt-stryker4s" % stryker4sVersion)

Después de agregar el plugin, se puede usar Stryker4s ejecutando sbt stryker en la raíz del proyecto y la magia comienza. 

No solo este framework existe para scala, tambien lo podemos usar en C# o javascript. 

Dejo link : 

https://stryker-mutator.io/

https://stryker-mutator.io/docs/stryker4s/getting-started/

https://github.com/stryker-mutator/stryker4s

viernes, 7 de octubre de 2022

Definir monadas con Cats

Podemos definir una Monad para un tipo personalizado proporcionando implementaciones de tres métodos: flatMap, pure y un método llamado tailRecM. Aquí hay una implementación de Monad para Option como ejemplo:


import cats.Monad

import scala.annotation.tailrec


val optionMonad = new Monad[Option] {

def flatMap[A, B](opt: Option[A]) (fn: A => Option[B]): Option[B] = opt flatMap fn

def pure[A](opt: A): Option[A] = Some(opt)


@tailrec

def tailRecM[A, B](a: A) (fn: A => Option[Either[A, B]]): Option[B] =

     fn(a) match {

          case None => None

          case Some(Left(a1)) => tailRecM(a1)(fn)

          case Some(Right(b)) => Some(b)

     }

}

El método tailRecM es una optimización utilizada en Cats para limitar la cantidad de espacio de pila que consumen las llamadas anidadas a flatMap. viene la técnica de un artículo de 2015 del creador de PureScript, Phil Freeman. El método se llama recursivamente a sí mismo hasta que el resultado de fn devuelve un Right.

Supongamos que queremos escribir un método que llama a una función hasta que la función indica que debe detenerse. los devolverá una instancia de mónada porque, como sabemos, las mónadas representan secuenciación y muchas mónadas tienen alguna noción de fin de ejecución.

Podemos escribir este método en términos de flatMap.

import cats.syntax.flatMap._ // For flatMap

def retry[F[_]: Monad, A](start: A)(f: A => F[A]): F[A] =

     f(start).flatMap{ a =>

          retry(a)(f)

}


Desafortunadamente, no es seguro. Funciona para entradas pequeñas.


import cats.instances.option._

retry(100)(a => if(a == 0) None else Some(a - 1))

// res1: Option[Int] = None


pero si intentamos una entrada grande, obtenemos un StackOverflowError.


retry(100000)(a => if(a == 0) None else Some(a - 1))


En su lugar, podemos reescribir este método usando tailRecM.


import cats.syntax.functor._ // for map

def retryTailRecM[F[_]: Monad, A](start: A)(f: A => F[A]): F[A] =

Monad[F].tailRecM(start){ a =>

     f(a).map(a2 => Left(a2))

}


Ahora se ejecuta con éxito sin importar cuántas veces recurramos.


retryTailRecM(100000)(a => if(a == 0) None else Some(a - 1))

// res2: Option[Int] = None


Es importante tener en cuenta que tenemos que llamar explícitamente a tailRecM. No hay una transformación de código que convierta el código recursivo sin cola en código recursivo de cola que use tailRecM. Sin embargo, la clase de tipos Monad proporciona varias utilidades que facilitan la escritura de este tipo de métodos.

Por ejemplo, podemos reescribir retry en términos de iterateWhileM y no tenemos que llamar explícitamente a tailRecM.


import cats.syntax.monad._ // for iterateWhileM

def retryM[F[_]: Monad, A](start: A)(f: A => F[A]): F[A] = start.iterateWhileM(f)(a => true)

retryM(100000)(a => if(a == 0) None else Some(a - 1))

// res3: Option[Int] = None


Todas las mónadas incorporadas en Cats tienen implementaciones recursivas de cola de tailRecM, aunque escribir una para mónadas personalizadas puede ser un desafío... 

lunes, 3 de octubre de 2022

Cambio de licencia de Akka


Les cuento, hace unas semanas me puse a estudiar Akka, que es un conjunto de herramientas para escribir aplicaciones distribuidas concurrentes basadas en el modelo actor, fue creado hace trece años por Jonas Bonér, fundador y director ejecutivo de Lightbend. La compañía ha anunciado recientemente un nuevo modelo de licencia Akka que ha cambiado de Apache 2.0 de código abierto a Business Source License (BSL) 1.1.

Esto significa que el código base de Akka solo es de uso gratuito para el desarrollo y en sistemas que no son de producción. La licencia BSL 1.1 permite que los proyectos elijan otra licencia después de un período de tiempo específico que reemplazará la licencia BSL 1.1. Lightbend decidió revertir la licencia BSL 1.1 a una licencia Apache 2.0 después de tres años. Varios otros proyectos de código abierto, como Couchbase, Sentry y MariaBD, se han movido a la licencia BSL 1.1 en los últimos años.

La nueva licencia afecta a todas las empresas con ingresos superiores a 25 millones de dólares que deseen utilizar la última versión de Akka. Las versiones actuales aún se pueden usar libremente, pero solo se publican actualizaciones y parches de seguridad críticos para la versión 2.6.x bajo la licencia actual de Apache 2.0 hasta septiembre de 2023. Los proyectos de código abierto pueden comunicarse con Lightbend para obtener una subvención de uso adicional.

En resumen, me parece que indicios de que el producto no va por buen camino y desean exprimirlo. 

Que piensan de estos cambios de licencia?

Dejo link: https://www.infoq.com/news/2022/09/akka-no-longer-open-source/#:~:text=The%20company%20has%20recently%20announced,and%20on%20non%2Dproduction%20systems.

sábado, 1 de octubre de 2022

Primeros pasos con Akka


Ante de empezar veamos este link, para ponernos en contexto : 
https://emanuelpeg.blogspot.com/2022/09/por-que-usar-akka.html

Implementamos el trait Actor en una clase simple llamada Contador. 

import akka.actor.Actor


class Counter extends Actor {

  var count = 0

  def receive = {

    case "incr" ⇒ count += 1

    case "get"  ⇒ sender() ! count

  }

}


Este tiene un campo count y debe implementar el método de receive del trait Actor y debe devolver una función parcial. Esa es la función parcial con una declaración de pattern matching con el mensaje entrante. 

Entonces, si recibimos un mensaje con una cadena incr, entonces queremos incrementar el contador. Lo que hacemos aquí. Si queremos que tenga estado, queremos permitir que otros actores averigüen cuál es el valor del contador. Y por lo tanto los actores pueden enviar mensajes a direcciones que conocen. Las direcciones en Akka están modeladas por el tipo ActorRef. Entonces, escribimos otro caso, y si obtenemos una tupla con la cadena get, y algo con tipo ActorRef, que llamamos cliente, entonces podemos enviar al cliente, el conteo como un mensaje. 

El operador de signo de exclamación que se usa para enviar mensajes y se pronuncia tell en Akka. Cada actor conoce su propia dirección. Es un ActorRef llamado self. Y está implícitamente disponible. ActorRef es una cláusula abstracta que tiene un método llamado tell o signo de exclamación. Y si usa el signo de exclamación que es la buena sintaxis de Scala, tell que es más para Java. Esto requiere un argumento implícito. 

El argumento implícito de tipo ActorRef se recoge del entorno. Entonces, si usa el operador Tell dentro de un actor, implícitamente seleccionará al remitente como la referencia propia de ese actor. Dentro del actor receptor, este valor está disponible como remitente, que devuelve la referencia del actor que ha enviado el mensaje que se está procesando actualmente.

La referencia junto con el mensaje al actor receptor debe hacerse explícitamente en otras implementaciones de actor como erlang. Pero como es un patrón tan común, existe el metodo sender()  que es la referencia al que me envia el mensaje.

Un actor puede hacer más cosas que simplemente enviar mensajes. Puede crear otros actores y puede cambiar su comportamiento. Para acceder a estas funciones, necesitamos conocer el contexto del actor. 

El tipo de actor en sí solo tiene el método de recepción, por lo que solo describe el comportamiento del actor. La maquinaria de ejecución la proporciona el contexto del actor. 

Se tienes acceso al contexto dentro del actor simplemente diciendo contexto. Ahora veamos esto en acción. Podemos reformular nuestro contador en uno que no tenga barra. Será un objeto con estado sin una variable explícita. 

Primero, necesitamos definir un método, que nos dé un comportamiento. Este método toma un argumento, cuál es el estado actual del contador. Empezamos con el contador en cero. Si recibimos un mensaje incr aquí, cambiamos nuestro comportamiento para que sea el de un contador de n más 1.  Dentro de este comportamiento del contador, en cualquier momento podemos obtener una solicitud, donde solo respondemos con el valor actual de los contadores. 

import akka.actor.Actor


class Counter extends Actor {

  def counter(n: Int): Receive = {

    case "incr" ⇒ context.became(counter(n + 1))

    case "get"  ⇒ sender() ! n

  }

  def receive = counter(0)

}

Puede ver aquí que se parece un poco a una función recursiva de cola, porque la llamamos dentro de sí misma, pero es asíncrona, porque context.become evalúa lo que se proporciona aquí solo cuando se procesa el siguiente mensaje. 

Esto es funcionalmente equivalente a la versión anterior del contador, pero tiene algunas ventajas. En primer lugar, solo hay un lugar donde se cambia el estado, y ese es el llamado a convertirse, y esto es muy explícito. La otra es que el estado se limita al comportamiento actual, por lo que no hay ninguna variable que podamos dejar en algún lugar en un estado desconocido. Siempre tenemos el estado actual aquí. 

Lo último que uno puede hacer fundamentalmente con los actores es crearlos y detenerlos. El contexto del actor tiene métodos para eso. Primero, actorOf, una descripción sobre cómo crear un actor y un nombre, y devuelve un ActorRef. Además, cada actor puede detener a otros actores. 

Una cosa a tener en cuenta aquí es que los actores siempre son creados por actores. Esto significa que naturalmente forman una jerarquía porque un actor es creado exactamente por otro actor. Y otra cosa a tener en cuenta es que detener aquí a menudo se aplica a la auto referencia, lo que significa que el actor decide que quiere terminar. Probemos todo esto en una aplicación ejecutable. 


import akka.actor.Actor

import akka.actor.Props


class CounterMain extends Actor {

  val counter = context.actorOf(Props[Counter], "counter")


  counter ! "incr"

  counter ! "incr"

  counter ! "get"


  def receive = {

    case count: Int ⇒

      println(s"count was $count")

      context.stop(self)

  }

}


Por supuesto, una aplicación también se modela como un actor en este modelo. Entonces lo llamamos CounterMain. Este es un actor y usa el contexto actorOf para crear un contador. Podemos decir Props Counter para crear un actor que no tome argumentos de constructor. Y como todas las cosas buenas. Necesita un nombre, lo llamamos contador en este caso. 

Luego enviamos mensajes a este contador, en este caso lo enviamos incr tres veces y luego lo enviamos get. Recuerde que enviar con el operador de tell dentro de un actor seleccionará la referencia propia como remitente. Y como el contador responderá, lo recibiremos en este actor. Aquí en nuestro comportamiento, recibimos la cuenta, que es de tipo int. Lo imprimiremos y luego nos detendremos. 

Para ejecutar esto, debemos decirle a Eclipse cómo ejecutar las aplicaciones de actor porque actualmente aún no lo sabe. Entonces creamos una configuración de ejecución. Utiliza la clase principal akka.Main que espera como primer argumento, el nombre de la clase, el nombre completo de la clase de actor que se va a instanciar. Entonces, si ejecutamos esto. Veremos que la cuenta imprime tres. Esta impresión se hizo con esta línea, así que recibimos el conteo del contador y recibimos tres porque incrementamos el contador tres veces.

Ahora hemos visto todas las cosas básicas que pueden hacer los actores. Cuando reciben un mensaje, pueden enviar mensajes, crear actores. Y pueden cambiar su comportamiento para el próximo mensaje,  este es el modelo actor de computación.

Dejo link : https://akka.io/

jueves, 29 de septiembre de 2022

[O'Reilly eBook] Container Networking: From Docker to Kubernetes

 

EBOOK

Ebook: Container Networking | From Docker to Kubernetes

Companies that begin working with containerized applications often aren’t prepared for the challenge of container networking. In this new eBook, Michael Hausenblas, a member of Red Hat’s OpenShift team, provides a detailed look at the many challenges of container networking, container orchestration, and service discovery, and shares several available solutions. Along the way, you’ll learn the capabilities of many open source tools, including Kubernetes.

Download this eBook to:

  • Get an introduction to container networking by exploring Docker networking modes
  • Maintain a map of running containers and their locations with service discovery tools such as ZooKeeper and etcd
  • Configure network interfaces in Linux containers by using plugins with the Container Network Interface (CNI)
  • Learn how the Kubernetes orchestration system approaches container networking

Concurnas, un lenguaje pensado para la concurrencia para la jvm


Concurnas es un lenguaje de programación de la JVM de open source, diseñado para construir sistemas concurrente, distribuidos y en paralelos, confiables, escalables y de alto rendimiento.

El uso de Concurnas ayuda a las organizaciones a aprovechar al máximo el hardware moderno de GPU y CPU multinúcleo al facilitar la programación concurrente y en paralela.

Concurnas es interoperable con Java (y otros lenguajes JVM): las organizaciones pueden aprovechar el software Java existente en un entorno concurrente.

En comparación con otros lenguajes, con Concurnas los desarrolladores necesitan escribir menos líneas de código. Ese código es más seguro y más fácil de probar, lo que aumenta la calidad y aumenta la productividad de los desarrolladores.

Entre las caracteristicas de Corcunas podemos nombrar: 

Fácil de aprender: Inspirado en lenguajes como Python y Java, Concurnas es un lenguaje de tipado estatico que utiliza la inferencia de tipos con una sintaxis fácil de aprender.

Alto rendimiento: Concurnas es un lenguaje compilado que se ejecuta en la máquina virtual de Java y, como tal, tiene acceso al increíble rendimiento que ofrece la JVM.

Fácil de escalar: Concurnas hace posible usar el mismo código desde un pequeño prototipo de investigación hasta una solución de producción distribuida a escala planetaria

Open Source: Concurnas es una tecnología de código abierto, libre de usar y modificar. Con desarrollo continuo y soporte comercial proporcionado por Concurnas Ltd.

La verdad que quiero poner un ejemplo pero hay tantos en la pagina y tan interesantes que sería incompleto, igual tiro uno para que tengan idea: 

//factorial

def factorial(i int) int {

    match(i){

        0 => 1

        n => n * factorial(n-1)

    }

}


Como se ve, es un lenguaje completo que le viene bien cualquier cosa, si queres podes programar imperativo, orientado a objeto, funcional, lo que pinte. Eso esta bueno pero es un problema si no entendes todos los conceptos. Un lenguaje de programación bien escrito te debería hacer fácil hacer las cosas bien y difícil hacer las cosas mal. Tanta libertad puede ser una desventaja. Esto ultimo es mi opinión. 

Dejo link: https://concurnas.com/

martes, 27 de septiembre de 2022

Por que usar Akka?


Las transmisiones reactivas fluyen los datos desde los productores hasta los consumidores. Pero todo va más o menos en una dirección. Los actores te dan más libertad que eso. Porque pueden colaborar y comunicarse como lo hacemos los humanos.

En particular, hablaremos sobre cómo los actores pueden crear otros actores. Cómo cambian su comportamiento con el tiempo, cómo intercambian mensajes. 

Antes de sumergirnos en lo que son los Actores, primero echemos un vistazo a por qué queremos investigarlos y de dónde provienen.

El formalismo Actor fue publicado por primera vez por Hewitt, Bishop y Steiger en 1973. Y lo que querían lograr es crear un modelo en el que puedan formular los programas para su investigación de inteligencia artificial.

Uno de los estudiantes de Hewitt publicó su tesis doctoral en 1986. Gul Agha formuló lenguajes de actores, cómo escribir programas de actores, cómo razonar sobre ellos. Describió los patrones de comunicación entre los Actores y cómo usarlos para resolver problemas reales.

En el mismo año, Ericsson comenzó a desarrollar un lenguaje de programación llamado Erlang. Este es un lenguaje de programación puramente funcional, cuyo modelo de concurrencia se basa en Actors. Y que luego se utilizó posteriormente en productos comerciales.

En 1995, Ericsson presentó su nueva plataforma de telecomunicaciones. El cual tuvo un gran éxito. Hubo alrededor de 30 milisegundos de tiempo de inactividad por año. Esta solidez y resiliencia fue posible gracias al modelo Actor.

Inspirado por el éxito de Erlang de Ericsson, Philipp Haller agregó Actors a la biblioteca estándar de Scala en 2006.

Luego, Jonas Bonér fue influenciado por Erlang, así como por Scala Actors para crear Akka en 2009. Akka es un framework de la JVM con APIs para Java y Scala que hace que el modelo Actor esté disponible para una amplia gama de desarrolladores.

Los actores son aplicables en una amplia gama de problemas. En el pasado, los programas se volvieron más rápidos al usar la próxima generación de CPU. Esto se debió a una frecuencia central cada vez mayor. Pero esto se detuvo alrededor del año 2005. Desde entonces, las CPU no son cada vez más rápidas, sino más anchas. Esto significa que, en lugar de hacer que un núcleo de ejecución sea más potente, se incorporan múltiples núcleos de este tipo dentro de un chip, accediendo a la memoria compartida.

Y en algunos de estos, los núcleos incluso están virtualizados, de modo que un núcleo de ejecución física puede albergar múltiples subprocesos de ejecución lógica. Hay diferentes formas de beneficiarse de estas CPU más amplias.

La primera es que puede ejecutar varios programas en paralelo en la misma computadora. Esto se llama multitarea y se ha hecho desde las primeras versiones de Unix. Pero si tiene un solo programa que necesita ejecutar más rápido y no tiene más remedio que ejecutar partes del mismo programa en paralelo. Y esto se llama subprocesos múltiples.

Para lograr subprocesos múltiples, su programa debe estar escrito de una manera diferente a la forma secuencial tradicional.

La diferencia entre ejecutar programas separados en paralelo y usar subprocesos del mismo programa en paralelo es que estos subprocesos colaboran en una tarea común.

Y si piensas en un grupo de personas haciendo algo juntas, necesitarán sincronizar sus acciones. De lo contrario, se pisan. Lo mismo puede suceder en un programa de subprocesos múltiples. Hay una nueva clase de errores y problemas que te esperan allí. Y esta es la razón por la cual los programas deben formularse de manera diferente para estar listos para subprocesos múltiples. Para entender lo que eso significa, echemos un vistazo a una cuenta bancaria:

class BankAccount {

private val balance = 0

def deposit(amount: Int): Unit = if (amount > 0) balance = balance + amount

def withdraw(amount: Int): Int = 

    if (amount  > 0 && amount <  balance) {

        balance = balance - amount 

        balance

    } else throw new Error("Fondo insuficiente")

}


Contiene un campo para el saldo y tiene dos métodos para depositar una cantidad y retirar una cantidad.

Ahora veamos el método de retiro en detalle. Entonces, echemos un vistazo a lo que sucede si dos subprocesos ejecutan este código al mismo tiempo.

Digamos que este es el subproceso 1 y este es el subproceso 2. Son ejecutados por diferentes CPU, por lo que pueden ejecutarse en paralelo.

Entran al método con una cantidad. Digamos que el hilo 1 quiere retirar 50 francos suizos, por ejemplo. Y el hilo 2 quiere retirar 40. Lo primero que harán ambos será leer el saldo actual. En ambos casos, digamos el saldo ahora mismo es 80, por lo que ambos verán 80. Luego, ambos ingresarán la declaración if, del chequeo. ¿La cantidad es positiva? Sí, lo es. ¿Y hay realmente suficientes fondos en la cuenta? Sí hay. Así ambos continuarán. Ellos calcularán el nuevo saldo.

El nuevo saldo en el primer subproceso será 30, y en el segundo será 40. Lo siguiente que harán los subprocesos será escribir el nuevo saldo en el campo de saldo de la cuenta bancaria, objeto.

El primer subproceso escribirá 30 y el segundo escribirá 40. Esto está claramente en conflicto, porque solo uno de los escritores puede ganar al final. El que viene en último lugar, sobrescribirá al que vino antes.

Este es el primer problema que vemos aquí. Es que en realidad se pierde una de las actualizaciones del saldo.

El otro problema es que se viola el invariante de la cuenta bancaria. En eso hemos retirado 50 y 40 francos suizos que son 90 en total, y el saldo era solo 80, lo que no debería haber sido posible. Uno de los hilos debería haber fallado. Y eso, eso no sucedió es el otro problema con este código.

Ahora, ¿qué podemos hacer para solucionar este problema? Necesitamos agregar sincronización. Cuando varios subprocesos trabajan con los mismos datos, necesitan sincronizar sus acciones.

Porque de lo contrario, serían propensos a pisarse. Lo que debemos hacer es asegurarnos de que cuando un subproceso esté trabajando con los datos, los demás se mantengan al margen. Como si pusieras un cartel de no molestar en la puerta de tu hotel.

Entonces, digamos que en este ejemplo estamos viendo el saldo, como los datos que se protegerán. Y lo que tenemos que hacer es poner una valla alrededor, de modo que cuando un subproceso esté trabajando con los datos, digamos el subproceso 1. Que este tenga acceso exclusivo a él. Lo que significa que la transacción 2, si intenta acceder a los datos, en realidad se le negará el acceso en este momento. Y tiene que esperar hasta que la transacción 1 termine con ella. De esta manera, el saldo estará protegido. Y todas las modificaciones realizadas en él se realizan de manera consistente, una tras otra. También decimos serializado.

Las herramientas principales para lograr este tipo de sincronización son lock o mutex. Que es básicamente el mismo concepto que se mostró anteriormente. O un semáforo donde la diferencia es que múltiples pero solo un número definido de subprocesos pueden ingresar a esta región.

En Scala, cada objeto tiene un candado asociado. Al que puede acceder llamando al método sincronizado en él. Y acepta un bloqueo de código que se ejecutará en esta región protegida.

¿Cómo aplicamos esto a la cuenta bancaria para sincronizarla?


def withdraw(amount: Int): Int = this.synchronized {

        if (amount  > 0 && amount <  balance) {

            balance = balance - amount 

            balance

        } else throw new Error("Fondo insuficiente")

}


Bueno, aquí tenemos el método de retiro. Y si lo ponemos todo dentro de un bloque sincronizado, entonces leemos el balance aquí. Realizar el cheque y volver a escribirlo, todo se hará como una acción atómica. Que no puede ser perturbado por otro hilo que ejecuta retirar, al mismo tiempo. Ese otro hilo tendrá que esperar.

Pero, ¿debemos sincronizar también aquí en el método de depósito? El método de depósito también modifica el saldo. Y si no esta sincronizado, entonces podría modificarlo sin protección. Y una vez que el retiro devuelve el saldo aquí, anularía la anulación de la actualización realizada por depósito al mismo tiempo.

Esto es para ilustrar que todos los accesos al saldo deben estar sincronizados, y no solo el que hemos demostrado que es problemático.

Ahora, intentemos transferir algo de dinero de una cuenta bancaria a otra.

Lo que debemos hacer es sincronizar ambos objetos, de modo que estén en un estado consistente. Porque de lo contrario, alguien leyendo el saldo de las cuentas podría encontrar el dinero en fuga. Retiramos de una cuenta. Depositamos después en otro.

Durante este tiempo, el dinero básicamente no está en ninguna parte. Y si la invariante que debe cumplirse es que la suma de from y to debe ser la misma, esto se violará. Por lo tanto, necesitamos sincronizar.

Entonces, primero tomamos el bloqueo en la cuenta de origen. Luego tomamos el bloqueo en la cuenta. Ahora estamos seguros de que ningún otro hilo puede modificar estas dos cuentas, pero nosotros podemos. Una propiedad de los bloqueos en Scala es que son reentrantes, lo que significa que el mismo subproceso puede tomarlo dos veces o cualquier cantidad de veces.

Hay un problema con este código, ya que introduce la posibilidad de un interbloqueo.

Digamos que un hilo quiere transferir de la cuenta A a la cuenta B en un hilo.

Y otro hilo intenta transferir en la dirección opuesta. Si ambos comienzan al mismo tiempo, toman el primer candado, este en la cuenta A, este en la cuenta B.

Luego van a tomar la otra cerradura. Este no logrará tomar el bloqueo, porque ya fue tomado por el otro subproceso para la cuenta B. Lo mismo es cierto para la cuenta A en el otro subproceso.

Esto significa que ninguno de los subprocesos puede progresar, ambos se atascarán para siempre. Porque no hay posibilidad de que ninguno de los dos ceda el candado que ya tienen. Esto se llama interbloqueo o deadlock

Hay soluciones. Por ejemplo, llevar siempre las transacciones en el mismo orden. Debe definir un orden para las cuentas, etc. y entonces podrías potencialmente resolver esto.

Descubrirá que la mayoría de las veces hay soluciones de ese tipo, pero se agregarán y harán que su código sea mucho más complicado con el tiempo. Y en este caso es sencillo, porque ambas son cuentas bancarias. Pero, ¿qué sucede si desea que colaboren objetos que no provienen de la misma base de código, por ejemplo, en los que no puede modificar?

En ese sentido, sería mucho mejor que nuestros objetos no requirieran sincronización o bloqueo. Porque el bloqueo es lo que realmente hace que ocurra el interbloqueo.

El otro problema con el bloqueo es que es malo para la utilización de la CPU. Si hay otros subprocesos para ejecutar, el sistema operativo los ejecutará. Pero de lo contrario, la CPU estará inactiva. Y despertarlo o hacer que un subproceso vuelva a ejecutarse cuando otro subproceso lo ha interrumpido lleva mucho tiempo. Por lo tanto, su programa se ejecutará más lentamente si usa el bloqueo.

Otro problema con el bloqueo de objetos es que la comunicación sincrónica une al emisor y al receptor con bastante fuerza. Porque el remitente debe esperar hasta que el receptor esté listo. Entonces, si llamo a una cuenta bancaria que está sincronizada, esa cuenta bancaria me bloqueará hasta que esté lista. Los objetos que no bloquean son exactamente lo que son los Actores.

Los actores representan objetos y el modelo de actor describe cómo estos objetos interactúan, y todo esto está inspirado en cómo nos organizamos los humanos y respeta especialmente las leyes de la física. ¿Qué quiero decir con esto? Digamos que una persona sin reloj que quiere saber la hora actual y hay otra persona con un reloj. Ahora la primera persona se hará la pregunta, ¿qué hora es?  Y la segunda persona podría responder, son las 12:43. Este es un intercambio simple entre dos humanos y todos hemos hecho algo así antes, así que espero que todos puedan identificarse con este ejemplo. Pero hay más de lo que es visible en la superficie. En primer lugar, tomamos nota de que transmitimos información hablando y escuchando las palabras. Eso significa que la primera persona envía un mensaje a la segunda persona. La segunda persona luego piensa un poco, mira el reloj y responde con otro mensaje, viajando hacia atrás, en ondas de sonido. Las dos cualidades fundamentales aquí son, en primer lugar, que se basa únicamente en mensajes y, en segundo lugar, los mensajes tardan en viajar de un objeto a otro. Es útil pensar en los actores no como estos objetos abstractos a los que llamas métodos. Pero en lugar de visualizarlos como personas que hablan entre sí. Una cosa también a tener en cuenta es que cuando los humanos hablamos, no nos metemos en el cerebro de los demás, por ejemplo, leyendo la mente. nos basamos únicamente en los mensajes, y eso es una parte muy fundamental de los actores. Más formalmente, un actor, tal como lo definen Hewitt, Bishop y Steiger, es un objeto con identidad. Y también tiene un comportamiento, que ya hemos encontrado, y solo interactúa mediante el paso de mensajes. Lo que sigue siendo compatible con la definición de objeto normal, lo especial de los actores es que su paso de mensajes siempre es asíncrono. 

Tenemos un actor, llamémoslo Actor A. Y este quiere enviar un mensaje al actor B. El actor A enviará el mensaje y luego puede continuar haciendo lo que quiera después de eso, sin tener que esperar por el  mensaje, este viajará a B y será procesado por B, especialmente el procesamiento ocurre en un contexto diferente, en un momento posterior diferente según lo determine el sistema en el que se ejecutan los actores. 

Esta es la propiedad de las más importante de los actores. Para esto, usamos los trait de actor en Akka. El tipo de actor define un método abstracto que se llama recibir y este recibir devuelve algo del tipo Receive. Recibir es una función parcial de Any a Unit y describe la respuesta del actor a un mensaje.

Cualquier mensaje podría entrar, por lo tanto es Any, y el actor puede hacer muchas cosas, pero al final, no devuelve nada, debido al paso asincrónico del mensaje.

Por ahora vamos a ver solo estos conseptos, y luego los profundizaremos en post posteriores. 

Dejo link : https://akka.io/

domingo, 25 de septiembre de 2022

Sealed classes de Kotlin: definición de jerarquías de clases restringidas


Veamos un ejemplo para entender mejor para que utilizar seald classess. La superclase Expr tiene dos subclases: Num, que representa un número; y Sum, que representa la suma de dos expresiones. Es conveniente manejar todas las subclases posibles en una expresión when. Pero tenemos que escribir el  else para especificar qué debería suceder si no tenemos coincidencias:


interface Expr

    class Num(val value: Int) : Expr

    class Sum(val left: Expr, val right: Expr) : Expr


fun eval(e: Expr): Int =

    when (e) {

        is Num -> e.value

        is Sum -> eval(e.right) + eval(e.left)

        else ->

            throw IllegalArgumentException("Unknown expression")

}


Cuando evalúas una expresión usando la construcción when, el compilador de Kotlin te obliga a especificar todas las opciones, pero nosotros sabemos que no es posible que haya otra opción. 

Tener que agregar siempre el else no es conveniente. Además, si agrega una nueva subclase, el compilador no detectará que algo ha cambiado y va a ir por el else. 

Kotlin proporciona una solución a este problema: Sealed classes  o clases selladas. Marcas una superclase con el modificador sellado, y eso restringe la posibilidad de crear subclases. Todas las subclases directas deben anidarse en la superclase:


sealed class Expr {

    class Num(val value: Int) : Expr()

    class Sum(val left: Expr, val right: Expr) : Expr()

}


fun eval(e: Expr): Int =

    when (e) {

        is Expr.Num -> e.value

        is Expr.Sum -> eval(e.right) + eval(e.left)

    }


Y listo!


El modificador sealed implica que la clase es abierta por lo tanto, no necesitamos agregar modificador open de forma explícita.

Otra cosa importante es que no puede declarar una interfaz sellada. ¿Por qué? Si pudiera, el compilador de Kotlin no podría garantizar que alguien no pueda implementar esta interfaz en el código Java.


sábado, 24 de septiembre de 2022

Recursos para aprender scala

 


  • Scala By Example, para ver más ejemplos que ilustran los conceptos tratados en las conferencias.

  • Scala Cheatsheet, para una referencia rápida que cubre la sintaxis de scala.


Libros
:

martes, 20 de septiembre de 2022

Spring boot con jetty




Tomcat y Jetty son los servidores web más populares en el mundo Java, pero Spring boot nos brinda la funcionalidad de servidor embebido con tomcat. Por que? ni idea, pero podemos cambiar esto y utilizar jetty de la siguiente manera. 

Generamos nuestro proyecto en : https://start.spring.io/ o como quieran pero tienen que elegir spring web como dependencia. 

Si ya funciona el entorno, vamos a la dependencia de spring web y excluimos tomcat y luego agregamos jetty : 


implementation ('org.springframework.boot:spring-boot-starter-web') {

exclude group: 'org.springframework.boot', module: 'spring-boot-starter-tomcat'

}


implementation 'org.springframework.boot:spring-boot-starter-jetty'


Y listo, cuando inicie spring lo va hacer con un jetty embebido. Van a poder ver la siguiente linea en el log : 


2022-09-20 08:06:28.788  INFO 1304813 --- [           main] o.s.b.web.embedded.jetty.JettyWebServer


lunes, 19 de septiembre de 2022

Clases internas y anidadas en Kotlin


Al igual que en Java, en Kotlin puedes declarar una clase en otra clase. Hacerlo puede ser útil para encapsular una clase auxiliar o colocar el código más cerca de donde se usa. La diferencia es que las clases anidadas de Kotlin no tienen acceso a la instancia de la clase externa, a menos que lo solicite específicamente. Veamos un ejemplo que muestra por qué esto es importante.

Imagine que desea definir un elemento View, cuyo estado se puede serializar. Puede que no sea fácil serializar una vista, pero puede copiar todos los datos necesarios en otra clase auxiliar. Declaras la interfaz State que implementa Serializable. La interfaz View declara los métodos getCurrentState y restoreState que se pueden usar para guardar el estado de una vista.

interface State: Serializable

interface View {

    fun getCurrentState(): State

    fun restoreState(state: State) {}

}

Es útil definir una clase que guarde el estado de un botón en la clase Botón. Veamos cómo se puede hacer en Java (el código Kotlin similar se mostrará en un momento).

/* Java */

public class Button implements View {

    @Override

    public State getCurrentState() {

        return new ButtonState();

    }

    @Override

    public void restoreState(State state) { /*...*/ }

    public class ButtonState implements State { /*...*/ }

}

La clase ButtonState que implementa la interfaz State y contiene información específica para Button. En el método getCurrentState, crea una nueva instancia de esta clase. En un caso real, inicializaría ButtonState con todos los datos necesarios.

¿Qué tiene de malo este código? ¿Por qué se obtiene un java.io.NotSerializableException? Eso puede parecer extraño al principio: ButtonState guarda una referencia a Button que no es Serializable.

Todo queda claro cuando recuerdas que en Java, cuando declaras una clase en otra clase, se convierte en una clase interna por defecto. La clase ButtonState del ejemplo almacena implícitamente una referencia a su clase Button externa. Eso explica por qué ButtonState no se puede serializar: Button no se puede serializar y la referencia a él interrumpe la serialización de ButtonState.

Para solucionar este problema, debe declarar la clase ButtonState como estática. Declarar una clase anidada como estática elimina la referencia implícita de esa clase a su clase adjunta. En Kotlin, el comportamiento predeterminado de las clases internas es lo opuesto a lo que acabamos de describir.

class Button : View {

override fun getCurrentState(): State = ButtonState()

override fun restoreState(state: State) { /*...*/ }

class ButtonState : State { /*...*/ }

}

Una clase anidada en Kotlin sin modificadores explícitos es lo mismo que una clase anidada estática en Java. Para convertirlo en una clase interna para que contenga una referencia a una clase externa se puede usar el modificador inner. 

La sintaxis para hacer referencia a una instancia de una clase externa en Kotlin también difiere de Java. Se escribe this@Outer para acceder a la clase Outer desde la clase Inner:

class Outer {

    inner class Inner {

        fun getOuterReference(): Outer = this@Outer

    }

}