<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: @alonso_isidoro</title>
    <description>The latest articles on DEV Community by @alonso_isidoro (@alonsoir).</description>
    <link>https://dev.to/alonsoir</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F148397%2Fcf0daeb9-40b1-47a6-aa49-f2d1187f6ce5.jpeg</url>
      <title>DEV Community: @alonso_isidoro</title>
      <link>https://dev.to/alonsoir</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alonsoir"/>
    <language>en</language>
    <item>
      <title>Como conseguir y la consumición entregada y ordenada de mensajes con Kafka</title>
      <dc:creator>@alonso_isidoro</dc:creator>
      <pubDate>Mon, 04 Nov 2024 10:45:44 +0000</pubDate>
      <link>https://dev.to/alonsoir/como-conseguir-y-la-consumicion-entregada-y-ordenada-de-mensajes-con-kafka-120d</link>
      <guid>https://dev.to/alonsoir/como-conseguir-y-la-consumicion-entregada-y-ordenada-de-mensajes-con-kafka-120d</guid>
      <description>&lt;p&gt;Para asegurar que los eventos se envían y consumen en un orden totalmente consistente en Apache Kafka, es fundamental entender cómo funciona la partición de mensajes y la asignación de consumidores. &lt;/p&gt;

&lt;h2&gt;
  
  
  Uso de Particiones en Kafka
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Particionamiento de Temas&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kafka organiza los mensajes en &lt;strong&gt;particiones&lt;/strong&gt; dentro de un tema. Cada partición mantiene el orden de los mensajes que recibe, lo que significa que los mensajes se procesan en el orden en que fueron enviados a esa partición.&lt;/li&gt;
&lt;li&gt;Para garantizar el orden, es crucial que todos los mensajes relacionados con un mismo contexto (por ejemplo, un ID de usuario o un ID de transacción) se envíen a la misma partición. Esto se logra utilizando una &lt;strong&gt;clave de partición&lt;/strong&gt; al enviar mensajes. Kafka utiliza esta clave para determinar a qué partición enviar el mensaje mediante una función hash[1][5].&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Claves de Mensaje&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Al enviar un mensaje, se puede especificar una &lt;strong&gt;clave&lt;/strong&gt;. Todos los mensajes con la misma clave serán enviados a la misma partición, lo que asegura que sean consumidos en el mismo orden en que fueron producidos. Por ejemplo, si se usa el ID del usuario como clave, todos los eventos relacionados con ese usuario irán a la misma partición.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Grupos de Consumidores
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Asignación de Consumidores&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Los consumidores en Kafka se agrupan en &lt;strong&gt;grupos de consumidores&lt;/strong&gt;. Cada grupo puede tener múltiples consumidores, pero cada partición solo puede ser leída por un consumidor dentro del grupo a la vez.&lt;/li&gt;
&lt;li&gt;Esto significa que si se tienen más consumidores que particiones, algunos consumidores estarán inactivos. Para mantener el orden y maximizar la eficiencia, es recomendable tener al menos tantas particiones como consumidores en el grupo.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Manejo de Offsets&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kafka almacena el estado de lectura de cada consumidor mediante &lt;strong&gt;offsets&lt;/strong&gt;, que son identificadores numéricos incrementales para cada mensaje dentro de una partición. Esto permite que los consumidores retomen la lectura desde donde lo dejaron en caso de fallos. &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Estrategias Adicionales
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Evitar Sobrecargas&lt;/strong&gt;: Al elegir claves de partición, es importante considerar la distribución del tráfico para evitar que algunas particiones estén sobrecargadas mientras otras están subutilizadas. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replicación y Tolerancia a Fallos&lt;/strong&gt;: Asegúrate de configurar una replicación adecuada (mayor a 1) para las particiones, lo que no solo mejora la disponibilidad sino también la resiliencia del sistema ante fallos.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Para implementar un sistema de producción y consumo de mensajes en Kafka utilizando Avro, asegurando que los mensajes se procesen en orden y manejando posibles fallos, aquí tienes un ejemplo completo. Este incluye la definición del esquema Avro, el código del productor y consumidor, así como estrategias para manejar errores.&lt;br&gt;
Esquema Avro&lt;br&gt;
Primero, definimos el esquema Avro para nuestro payload. Crearemos un archivo llamado &lt;strong&gt;user_signed_up.avsc&lt;/strong&gt; que describe la estructura del mensaje.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "type": "record",
  "name": "UserSignedUp",
  "namespace": "com.example",
  "fields": [
    { "name": "userId", "type": "int" },
    { "name": "userEmail", "type": "string" },
    { "name": "timestamp", "type": "string" } // Formato ISO 8601
  ]
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generación de la Clave&lt;br&gt;
Para asegurar el orden en la producción y consumo de mensajes, utilizaremos una clave estructurada como tipo-mensaje-fecha, por ejemplo: user-signed-up-2024-11-04.&lt;br&gt;
Productor Kafka&lt;br&gt;
Aquí tienes el código del &lt;strong&gt;productor&lt;/strong&gt; que envía mensajes a Kafka utilizando el esquema Avro:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import org.apache.avro.Schema;
import org.apache.avro.generic.GenericData;
import org.apache.avro.generic.GenericRecord;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.serialization.StringSerializer;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Properties;

public class AvroProducer {
    private final KafkaProducer&amp;lt;String, byte[]&amp;gt; producer;
    private final Schema schema;

    public AvroProducer(String bootstrapServers) throws IOException {
        Properties properties = new Properties();
        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
        properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, "io.confluent.kafka.serializers.KafkaAvroSerializer");

// Establecer la propiedad de reintentos, Número de reintentos
        properties.put(ProducerConfig.RETRIES_CONFIG, 3); 
// Asegura que todos los réplicas reconozcan la escritura,
        properties.put(ProducerConfig.ACKS_CONFIG, "all"); 
// Solo un mensaje a la vez
properties.put(ProducerConfig.MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION, 1); 
// Habilitar idempotencia, no quiero enviar duplicados
properties.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true); 

        this.producer = new KafkaProducer&amp;lt;&amp;gt;(properties);
        this.schema = new Schema.Parser().parse(new File("src/main/avro/user_signed_up.avsc"));
    }

    public void sendMessage(String topic, int userId, String userEmail) {
        GenericRecord record = new GenericData.Record(schema);
        record.put("userId", userId);
        record.put("userEmail", userEmail);
        record.put("timestamp", java.time.Instant.now().toString());

        String key = String.format("user-signed-up-%s", java.time.LocalDate.now());

        ProducerRecord&amp;lt;String, byte[]&amp;gt; producerRecord = new ProducerRecord&amp;lt;&amp;gt;(topic, key, serialize(record));

        producer.send(producerRecord, (metadata, exception) -&amp;gt; {
            if (exception != null) {
                exception.printStackTrace();
**handleFailure(exception, producerRecord);
**            } else {
                System.out.printf("Mensaje enviado a la partición %d con offset %d%n", metadata.partition(), metadata.offset());
            }
        });
    }

private void handleFailure(Exception exception, ProducerRecord&amp;lt;String, byte[]&amp;gt; producerRecord) {
        // Log the error for monitoring
        System.err.println("Error sending message: " + exception.getMessage());

        // Implement local persistence as a fallback
        saveToLocalStorage(producerRecord);

        // Optionally: Notify an external monitoring system or alert
    }

    private void saveToLocalStorage(ProducerRecord&amp;lt;String, byte[]&amp;gt; record) {
        try {
            // Persist the failed message to a local file or database for later processing
            Files.write(new File("failed_messages.log").toPath(), 
                         (record.key() + ": " + new String(record.value()) + "\n").getBytes(), 
                         StandardOpenOption.CREATE, 
                         StandardOpenOption.APPEND);
            System.out.println("Mensaje guardado localmente para reenvío: " + record.key());
        } catch (IOException e) {
            System.err.println("Error saving to local storage: " + e.getMessage());
        }
    }

    private byte[] serialize(GenericRecord record) {
        // Crear un ByteArrayOutputStream para almacenar los bytes serializados
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    // Crear un escritor de datos para el registro Avro
    DatumWriter&amp;lt;GenericRecord&amp;gt; datumWriter = new GenericDatumWriter&amp;lt;&amp;gt;(record.getSchema());

    // Crear un encoder para escribir en el ByteArrayOutputStream
    Encoder encoder = EncoderFactory.get().binaryEncoder(outputStream, null);

    try {
        // Escribir el registro en el encoder
        datumWriter.write(record, encoder);
        // Finalizar la escritura
        encoder.flush();
    } catch (IOException e) {
        throw new AvroSerializationException("Error serializing Avro record", e);
    }

    // Devolver los bytes serializados
    return outputStream.toByteArray();
    }

    public void close() {
        producer.close();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Consideraciones sobre Reintentos&lt;br&gt;
**Es importante tener en cuenta que al habilitar reintentos, puede haber un riesgo de reordenamiento de mensajes si no se maneja adecuadamente. &lt;br&gt;
Para evitar esto:&lt;br&gt;
**max.in.flight.requests.per.connection&lt;/strong&gt;: Puedes establecer esta propiedad en 1 para garantizar que los mensajes se envíen uno a la vez y se procesen en orden. Sin embargo, esto puede afectar el rendimiento.&lt;br&gt;
Con esta configuración y manejo adecuado de errores, puedes asegurar que tu productor Kafka sea más robusto y capaz de manejar fallos en la producción de mensajes mientras mantienes el orden necesario.&lt;/p&gt;

&lt;p&gt;**Consumidor Kafka&lt;br&gt;
**El consumidor que lee y procesa los mensajes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import org.apache.avro.Schema;
import org.apache.avro.generic.GenericDatumReader;
import org.apache.avro.generic.GenericData;
import org.apache.avro.generic.GenericRecord;
import org.apache.avro.io.DecoderFactory;
import org.apache.avro.io.DatumReader;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.common.serialization.StringDeserializer;

import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.Properties;

public class AvroConsumer {
    private final KafkaConsumer&amp;lt;String, byte[]&amp;gt; consumer;
    private final Schema schema;

    public AvroConsumer(String bootstrapServers, String groupId) throws IOException {
        Properties properties = new Properties();
        properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
        properties.put(ConsumerConfig.GROUP_ID_CONFIG, groupId);
        properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
        properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, "io.confluent.kafka.serializers.KafkaAvroDeserializer");

        this.consumer = new KafkaConsumer&amp;lt;&amp;gt;(properties);
        this.schema = new Schema.Parser().parse(new File("src/main/avro/user_signed_up.avsc"));
    }

    public void consume(String topic) {
        consumer.subscribe(Collections.singletonList(topic));

        while (true) {
            ConsumerRecords&amp;lt;String, byte[]&amp;gt; records = consumer.poll(Duration.ofMillis(100));
            for (ConsumerRecord&amp;lt;String, byte[]&amp;gt; record : records) {
                try {
                    processMessage(record.value());
                } catch (Exception e) {
                    handleProcessingError(e, record);
                }
            }
        }
    }

    private void processMessage(byte[] data) throws IOException {
        DatumReader&amp;lt;GenericRecord&amp;gt; reader = new GenericDatumReader&amp;lt;&amp;gt;(schema);
        var decoder = DecoderFactory.get().binaryDecoder(data, null);
        GenericRecord record = reader.read(null, decoder);

        System.out.printf("Consumido mensaje: %s - %s - %s%n", 
            record.get("userId"), 
            record.get("userEmail"), 
            record.get("timestamp"));
    }

    private void handleProcessingError(Exception e, ConsumerRecord&amp;lt;String, byte[]&amp;gt; record) {
        System.err.println("Error processing message: " + e.getMessage());

        // Implement logic to save failed messages for later processing
        saveFailedMessage(record);
    }

    private void saveFailedMessage(ConsumerRecord&amp;lt;String, byte[]&amp;gt; record) {
        try {
            // Persist the failed message to a local file or database for later processing
            Files.write(new File("failed_consumed_messages.log").toPath(), 
                         (record.key() + ": " + new String(record.value()) + "\n").getBytes(), 
                         StandardOpenOption.CREATE,
                         StandardOpenOption.APPEND);
            System.out.println("Mensaje consumido guardado localmente para re-procesamiento: " + record.key());
        } catch (IOException e) {
            System.err.println("Error saving consumed message to local storage: " + e.getMessage());
        }
    }

    public void close() {
        consumer.close();
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ejemplo Realista de Claves&lt;br&gt;
En un entorno con muchos eventos distintos y muchas particiones distintas, una clave realista podría ser algo como:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user-signed-up-2024-11-04
order-created-2024-11-04
payment-processed-2024-11-04
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esto permite que todos los eventos relacionados con un tipo específico en una fecha específica se envíen a la misma partición y se procesen en orden. Además, puedes diversificar las claves incluyendo más detalles si es necesario (como un ID de sesión o transacción).&lt;br&gt;
Con esta implementación y estrategias para manejar fallos y asegurar el orden de los mensajes en Kafka utilizando Avro, puedes construir un sistema robusto y eficiente para gestionar eventos.&lt;/p&gt;

&lt;p&gt;Ahora un Productor y un consumidor Kafka algo más capaz.&lt;/p&gt;

&lt;p&gt;Productor Kafka con Circuit Breaker, Persistencia Local y DLQ.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import org.apache.avro.Schema;
import org.apache.avro.generic.GenericData;
import org.apache.avro.generic.GenericRecord;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.serialization.StringSerializer;
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.time.Duration;
import java.util.Properties;

public class AvroProducer {
    private final KafkaProducer&amp;lt;String, byte[]&amp;gt; producer;
    private final Schema schema;
    private final CircuitBreaker circuitBreaker;

    public AvroProducer(String bootstrapServers) throws IOException {
        Properties properties = new Properties();
        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
        properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, "io.confluent.kafka.serializers.KafkaAvroSerializer");

        properties.put(ProducerConfig.RETRIES_CONFIG, 3);
        properties.put(ProducerConfig.ACKS_CONFIG, "all");
        properties.put(ProducerConfig.MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION, 1);

        this.producer = new KafkaProducer&amp;lt;&amp;gt;(properties);
        this.schema = new Schema.Parser().parse(new File("src/main/avro/user_signed_up.avsc"));

        // Configuración del Circuit Breaker
        CircuitBreakerConfig config = CircuitBreakerConfig.custom()
                .failureRateThreshold(50) // porcentaje de fallos para abrir el circuito
                .waitDurationInOpenState(Duration.ofSeconds(30)) // tiempo en estado abierto
                .slidingWindowSize(10) // número de llamadas para calcular el porcentaje de fallos
                .build();

        CircuitBreakerRegistry registry = CircuitBreakerRegistry.of(config);
        this.circuitBreaker = registry.circuitBreaker("producerCircuitBreaker");
    }

    public void sendMessage(String topic, int userId, String userEmail) {
        GenericRecord record = new GenericData.Record(schema);
        record.put("userId", userId);
        record.put("userEmail", userEmail);
        record.put("timestamp", java.time.Instant.now().toString());

        String key = String.format("user-signed-up-%s", java.time.LocalDate.now());

        ProducerRecord&amp;lt;String, byte[]&amp;gt; producerRecord = new ProducerRecord&amp;lt;&amp;gt;(topic, key, serialize(record));

        circuitBreaker.executeRunnable(() -&amp;gt; {
            producer.send(producerRecord, (metadata, exception) -&amp;gt; {
                if (exception != null) {
                    handleFailure(exception, producerRecord);
                } else {
                    System.out.printf("Mensaje enviado a la partición %d con offset %d%n", metadata.partition(), metadata.offset());
                }
            });
        });
    }

    private byte[] serialize(GenericRecord record) throws AvroSerializationException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        DatumWriter&amp;lt;GenericRecord&amp;gt; datumWriter = new GenericDatumWriter&amp;lt;&amp;gt;(record.getSchema());
        Encoder encoder = EncoderFactory.get().binaryEncoder(outputStream, null);

        try {
            datumWriter.write(record, encoder);
            encoder.flush();
        } catch (IOException e) {
            throw new AvroSerializationException("Error serializing Avro record", e);
        }

        return outputStream.toByteArray();
    }

    private void handleFailure(Exception exception, ProducerRecord&amp;lt;String, byte[]&amp;gt; producerRecord) {
        System.err.println("Error al enviar el mensaje: " + exception.getMessage());

        // Guardar el mensaje en una cola de errores (DLQ)
        saveToDeadLetterQueue(producerRecord);

        // Guardar en almacenamiento local como respaldo
        saveToLocalStorage(producerRecord);
    }

    private void saveToDeadLetterQueue(ProducerRecord&amp;lt;String, byte[]&amp;gt; record) {
        try {
            Files.write(new File("dead_letter_queue.log").toPath(), 
                         (record.key() + ": " + new String(record.value()) + "\n").getBytes(), 
                         StandardOpenOption.CREATE,
                         StandardOpenOption.APPEND);
            System.out.println("Mensaje guardado en Dead Letter Queue: " + record.key());
        } catch (IOException e) {
            System.err.println("Error guardando en DLQ: " + e.getMessage());
        }
    }

    private void saveToLocalStorage(ProducerRecord&amp;lt;String, byte[]&amp;gt; record) {
        try {
            Files.write(new File("failed_messages.log").toPath(), 
                         (record.key() + ": " + new String(record.value()) + "\n").getBytes(), 
                         StandardOpenOption.CREATE,
                         StandardOpenOption.APPEND);
            System.out.println("Mensaje guardado localmente para reenvío: " + record.key());
        } catch (IOException e) {
            System.err.println("Error guardando en almacenamiento local: " + e.getMessage());
        }
    }

    public void close() {
        producer.close();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Consumidor Kafka con Manejo de DLQ.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import org.apache.avro.Schema;
import org.apache.avro.generic.GenericDatumReader;
import org.apache.avro.generic.GenericData;
import org.apache.avro.generic.GenericRecord;
import org.apache.avro.io.DecoderFactory;
import org.apache.avro.io.DatumReader;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.common.serialization.StringDeserializer;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Collections;
import java.util.Properties;

public class AvroConsumer {
    private final KafkaConsumer&amp;lt;String, byte[]&amp;gt; consumer;
    private final Schema schema;

    public AvroConsumer(String bootstrapServers, String groupId) throws IOException {
        Properties properties = new Properties();
        properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
        properties.put(ConsumerConfig.GROUP_ID_CONFIG, groupId);
        properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
        properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, "io.confluent.kafka.serializers.KafkaAvroDeserializer");

        this.consumer = new KafkaConsumer&amp;lt;&amp;gt;(properties);
        this.schema = new Schema.Parser().parse(new File("src/main/avro/user_signed_up.avsc"));
    }

    public void consume(String topic) {
        consumer.subscribe(Collections.singletonList(topic));

        while (true) {
            ConsumerRecords&amp;lt;String, byte[]&amp;gt; records = consumer.poll(Duration.ofMillis(100));
            for (ConsumerRecord&amp;lt;String, byte[]&amp;gt; record : records) {
                try {
                    processMessage(record.value());
                } catch (Exception e) {
                    handleProcessingError(e, record);
                }
            }
            processDeadLetterQueue(); // Procesar mensajes en la DLQ después de consumir del tópico principal
        }
    }

    private void processMessage(byte[] data) throws IOException {
        DatumReader&amp;lt;GenericRecord&amp;gt; reader = new GenericDatumReader&amp;lt;&amp;gt;(schema);
        var decoder = DecoderFactory.get().binaryDecoder(data, null);
        GenericRecord record = reader.read(null, decoder);

        System.out.printf("Consumido mensaje: %s - %s - %s%n", 
            record.get("userId"), 
            record.get("userEmail"), 
            record.get("timestamp"));
    }

    private void handleProcessingError(Exception e, ConsumerRecord&amp;lt;String, byte[]&amp;gt; record) {
        System.err.println("Error procesando el mensaje: " + e.getMessage());

        // Guardar el mensaje fallido en la DLQ
        saveFailedMessage(record);
    }

    private void saveFailedMessage(ConsumerRecord&amp;lt;String, byte[]&amp;gt; record) {
        try {
            Files.write(new File("dead_letter_queue.log").toPath(), 
                         (record.key() + ": " + new String(record.value()) + "\n").getBytes(), 
                         StandardOpenOption.CREATE,
                         StandardOpenOption.APPEND);
            System.out.println("Mensaje consumido guardado en Dead Letter Queue: " + record.key());

            // Opcionalmente puedes eliminar el mensaje del tópico original si es necesario
            // consumer.commitSync(); // Confirmar el procesamiento si es necesario

         } catch (IOException e) {
             System.err.println("Error guardando mensaje consumido en DLQ: " + e.getMessage());
         }
     }

     private void processDeadLetterQueue() {
         try {
             Files.lines(new File("dead_letter_queue.log").toPath()).forEach(line -&amp;gt; {
                 System.out.println("Procesando mensaje desde la Dead Letter Queue: " + line);
                 // Aquí puedes implementar la lógica para reintentar o procesar mensajes desde la DLQ.
             });
         } catch (IOException e) {
             System.err.println("Error procesando la Dead Letter Queue: " + e.getMessage());
         }
     }

     public void close() {
         consumer.close();
     }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"&amp;gt;
    &amp;lt;modelVersion&amp;gt;4.0.0&amp;lt;/modelVersion&amp;gt;

    &amp;lt;groupId&amp;gt;com.example&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;kafka-avro-example&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.0-SNAPSHOT&amp;lt;/version&amp;gt;
    &amp;lt;packaging&amp;gt;jar&amp;lt;/packaging&amp;gt;

    &amp;lt;properties&amp;gt;
        &amp;lt;java.version&amp;gt;17&amp;lt;/java.version&amp;gt;
        &amp;lt;kafka.version&amp;gt;3.5.1&amp;lt;/kafka.version&amp;gt;
        &amp;lt;avro.version&amp;gt;1.11.0&amp;lt;/avro.version&amp;gt;
        &amp;lt;resilience4j.version&amp;gt;1.7.1&amp;lt;/resilience4j.version&amp;gt;
        &amp;lt;slf4j.version&amp;gt;1.7.36&amp;lt;/slf4j.version&amp;gt;
    &amp;lt;/properties&amp;gt;

    &amp;lt;dependencies&amp;gt;
        &amp;lt;!-- Kafka Clients --&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.apache.kafka&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;kafka-clients&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;${kafka.version}&amp;lt;/version&amp;gt;
        &amp;lt;/dependency&amp;gt;

        &amp;lt;!-- Avro --&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.apache.avro&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;avro&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;${avro.version}&amp;lt;/version&amp;gt;
        &amp;lt;/dependency&amp;gt;

        &amp;lt;!-- Confluent Kafka Avro Serializer --&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;io.confluent&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;kafka-avro-serializer&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;7.5.0&amp;lt;/version&amp;gt; &amp;lt;!-- Asegúrate de que esta versión sea compatible con tu versión de Kafka --&amp;gt;
        &amp;lt;/dependency&amp;gt;

        &amp;lt;!-- Resilience4j --&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;io.github.resilience4j&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;resilience4j-spring-boot2&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;${resilience4j.version}&amp;lt;/version&amp;gt;
        &amp;lt;/dependency&amp;gt;

        &amp;lt;!-- SLF4J API --&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.slf4j&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;slf4j-api&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;${slf4j.version}&amp;lt;/version&amp;gt;
        &amp;lt;/dependency&amp;gt;

        &amp;lt;!-- Logging Implementation (Logback) --&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;ch.qos.logback&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;logback-classic&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;1.2.6&amp;lt;/version&amp;gt;
        &amp;lt;/dependency&amp;gt;

        &amp;lt;!-- JUnit for Testing --&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.junit.jupiter&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;junit-jupiter-engine&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;5.8.2&amp;lt;/version&amp;gt;
            &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
        &amp;lt;/dependency&amp;gt;

    &amp;lt;/dependencies&amp;gt;

    &amp;lt;build&amp;gt;
        &amp;lt;plugins&amp;gt;

            &amp;lt;!-- Maven Compiler Plugin --&amp;gt;
            &amp;lt;plugin&amp;gt;
                &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;
                &amp;lt;artifactId&amp;gt;maven-compiler-plugin&amp;lt;/artifactId&amp;gt;
                &amp;lt;version&amp;gt;3.11.0&amp;lt;/version&amp;gt;
                &amp;lt;configuration&amp;gt;
                    &amp;lt;source&amp;gt;${java.version}&amp;lt;/source&amp;gt;
                    &amp;lt;target&amp;gt;${java.version}&amp;lt;/target&amp;gt;
                &amp;lt;/configuration&amp;gt;
            &amp;lt;/plugin&amp;gt;

        &amp;lt;/plugins&amp;gt;
    &amp;lt;/build&amp;gt;

&amp;lt;/project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Explicación del Código&lt;br&gt;
&lt;strong&gt;Circuit Breaker:&lt;/strong&gt;&lt;br&gt;
Se utiliza Resilience4j para gestionar el circuito breaker del productor. Se configura un umbral de tasa de fallo y un tiempo de espera en estado abierto.&lt;br&gt;
&lt;strong&gt;Persistencia Local y DLQ:&lt;/strong&gt;&lt;br&gt;
Los mensajes fallidos se guardan tanto en un archivo local (failed_messages.log) como en una cola de errores (dead_letter_queue.log).&lt;br&gt;
&lt;strong&gt;Manejo de Errores:&lt;/strong&gt;&lt;br&gt;
En el productor y consumidor se manejan los errores adecuadamente y se registran.&lt;br&gt;
&lt;strong&gt;Procesamiento de la DLQ:&lt;/strong&gt;&lt;br&gt;
El consumidor también procesa los mensajes almacenados en la DLQ después de consumir mensajes del tópico principal.&lt;br&gt;
&lt;strong&gt;Logging:&lt;/strong&gt;&lt;br&gt;
Se utilizan mensajes System.err y System.out para registrar errores y eventos importantes.&lt;br&gt;
&lt;strong&gt;Consideraciones Finales&lt;/strong&gt;&lt;br&gt;
Con esta implementación:&lt;br&gt;
Se asegura que los mensajes sean enviados y procesados de manera resiliente.&lt;br&gt;
Se proporciona un manejo adecuado para errores temporales o persistentes.&lt;br&gt;
La lógica permite una recuperación efectiva mediante el uso de una Dead Letter Queue.&lt;br&gt;
El circuito breaker ayuda a evitar que el sistema se sature ante fallos prolongados.&lt;br&gt;
Este enfoque crea un sistema robusto que puede manejar problemas físicos y lógicos mientras mantiene la entrega ordenada de mensajes en Kafka.&lt;/p&gt;

</description>
      <category>kafka</category>
      <category>java</category>
    </item>
    <item>
      <title>IndexIVFFlat y IndexIVFPQ</title>
      <dc:creator>@alonso_isidoro</dc:creator>
      <pubDate>Wed, 09 Oct 2024 10:45:12 +0000</pubDate>
      <link>https://dev.to/alonsoir/indexivfflat-y-indexivfpq-2el8</link>
      <guid>https://dev.to/alonsoir/indexivfflat-y-indexivfpq-2el8</guid>
      <description>&lt;p&gt;Here's a comparison between the &lt;strong&gt;IndexIVFFlat&lt;/strong&gt; and &lt;strong&gt;IndexIVFPQ&lt;/strong&gt; indices, along with some alternatives for their use:&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison: IndexIVFFlat vs. IndexIVFPQ
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Characteristic&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;IndexIVFFlat&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;IndexIVFPQ&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Storage Type&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stores vectors in their original form.&lt;/td&gt;
&lt;td&gt;Utilizes product quantization (PQ) to compress vectors.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Precision&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High precision, as it performs exact searches within cells.&lt;/td&gt;
&lt;td&gt;May sacrifice some precision for compression, but still provides good results.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Search Speed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Slower on large datasets due to exhaustive search.&lt;/td&gt;
&lt;td&gt;Faster, especially on large sets, thanks to reduced search space.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Memory Usage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Consumes more memory as it stores all vectors without compression.&lt;/td&gt;
&lt;td&gt;Consumes significantly less memory due to compression (up to 97% less).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Configuration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Simpler, only requires defining the number of cells (nlist).&lt;/td&gt;
&lt;td&gt;Requires defining both the number of cells (nlist) and code size (code_size).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Training&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Needs to be trained to create cells before adding data.&lt;/td&gt;
&lt;td&gt;Also requires training, but the process is more complex due to quantization.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Pros and Cons
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Pros of IndexIVFFlat
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Precision&lt;/strong&gt;: Provides exact results when searching within each cell.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplicity&lt;/strong&gt;: Easy to understand and configure.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Cons of IndexIVFFlat
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Speed&lt;/strong&gt;: Can be very slow with large volumes of data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Usage&lt;/strong&gt;: Does not optimize memory usage, which can be problematic with large datasets.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Pros of IndexIVFPQ
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Speed&lt;/strong&gt;: Much faster in searches due to reduced search space.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Efficiency&lt;/strong&gt;: Significantly reduces memory usage, allowing for handling larger datasets.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Cons of IndexIVFPQ
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Precision&lt;/strong&gt;: There may be a slight loss in precision due to compression.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complexity&lt;/strong&gt;: Configuration and training are more complex than in IndexIVFFlat.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Alternatives
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;IndexFlatL2&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Performs an exhaustive search without compression. Ideal for small datasets where maximum precision is required.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;IndexPQ&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uses only product quantization without clustering. Useful when a balance between speed and precision is needed, but clustering is not required.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;IndexIVFScalarQuantizer&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Combines the inverted index with scalar quantization, offering a different approach to reduce memory usage and improve speed.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;IndexIVFPQR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A variant that combines IVF and PQ with code-based re-ranking, offering a balance between speed and improved precision.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Composite Indexes&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;index_factory&lt;/code&gt; to create composite indices that combine multiple techniques (e.g., OPQ + IVF + PQ) to further optimize performance.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These alternatives allow adapting the solution to different needs in terms of precision, speed, and memory usage according to the specific case being addressed.&lt;/p&gt;

&lt;p&gt;Citations:&lt;br&gt;
[1] &lt;a href="https://github.com/facebookresearch/faiss/wiki/Faiss-indexes/9df19586b3a75e4cb1c2fb915f2c695755a599b8" rel="noopener noreferrer"&gt;https://github.com/facebookresearch/faiss/wiki/Faiss-indexes/9df19586b3a75e4cb1c2fb915f2c695755a599b8&lt;/a&gt;&lt;br&gt;
[2] &lt;a href="https://ai.plainenglish.io/speeding-up-similarity-search-in-recommender-systems-with-faiss-advanced-concepts-part-ii-95e796a7db74?gi=ce57aff1a0c4" rel="noopener noreferrer"&gt;https://ai.plainenglish.io/speeding-up-similarity-search-in-recommender-systems-with-faiss-advanced-concepts-part-ii-95e796a7db74?gi=ce57aff1a0c4&lt;/a&gt;&lt;br&gt;
[3] &lt;a href="https://www.pinecone.io/learn/series/faiss/faiss-tutorial/" rel="noopener noreferrer"&gt;https://www.pinecone.io/learn/series/faiss/faiss-tutorial/&lt;/a&gt;&lt;br&gt;
[4] &lt;a href="https://faiss.ai/cpp_api/struct/structfaiss_1_1IndexIVFFlat.html" rel="noopener noreferrer"&gt;https://faiss.ai/cpp_api/struct/structfaiss_1_1IndexIVFFlat.html&lt;/a&gt;&lt;br&gt;
[5] &lt;a href="https://unfoldai.com/effortless-large-scale-image-retrieval-with-faiss-a-hands-on-tutorial/" rel="noopener noreferrer"&gt;https://unfoldai.com/effortless-large-scale-image-retrieval-with-faiss-a-hands-on-tutorial/&lt;/a&gt;&lt;br&gt;
[6] &lt;a href="https://www.pinecone.io/learn/series/faiss/product-quantization/" rel="noopener noreferrer"&gt;https://www.pinecone.io/learn/series/faiss/product-quantization/&lt;/a&gt;&lt;br&gt;
[7] &lt;a href="https://www.pinecone.io/learn/series/faiss/composite-indexes/" rel="noopener noreferrer"&gt;https://www.pinecone.io/learn/series/faiss/composite-indexes/&lt;/a&gt;&lt;br&gt;
[8] &lt;a href="https://github.com/facebookresearch/faiss/issues/1113" rel="noopener noreferrer"&gt;https://github.com/facebookresearch/faiss/issues/1113&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En Español, Soy Español, pero por respeto a la comunidad, pongo primero la traduccion al inglés.&lt;/p&gt;

&lt;p&gt;Aquí tienes una comparación entre los índices &lt;strong&gt;IndexIVFFlat&lt;/strong&gt; e &lt;strong&gt;IndexIVFPQ&lt;/strong&gt;, junto con algunas alternativas para su uso:&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparación: IndexIVFFlat vs. IndexIVFPQ
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Característica&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;IndexIVFFlat&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;IndexIVFPQ&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tipo de Almacenamiento&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Almacena vectores en su forma original.&lt;/td&gt;
&lt;td&gt;Utiliza cuantización de producto (PQ) para comprimir vectores.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Precisión&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Alta precisión, ya que realiza búsquedas exactas dentro de las celdas.&lt;/td&gt;
&lt;td&gt;Puede sacrificar algo de precisión por la compresión, pero aún proporciona buenos resultados.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Velocidad de Búsqueda&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Más lento en grandes conjuntos de datos debido a la búsqueda exhaustiva.&lt;/td&gt;
&lt;td&gt;Más rápido, especialmente en grandes conjuntos, gracias a la reducción del espacio de búsqueda.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Uso de Memoria&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Consume más memoria porque almacena todos los vectores sin compresión.&lt;/td&gt;
&lt;td&gt;Consume significativamente menos memoria debido a la compresión (hasta 97% menos).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Configuración&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Más simple, solo requiere definir el número de celdas (nlist).&lt;/td&gt;
&lt;td&gt;Requiere definir tanto el número de celdas (nlist) como el tamaño del código (code_size).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Entrenamiento&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Necesita ser entrenado para crear las celdas antes de añadir datos.&lt;/td&gt;
&lt;td&gt;También necesita entrenamiento, pero el proceso es más complejo debido a la cuantización.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Pros y Contras
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Pros de IndexIVFFlat
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Precisión&lt;/strong&gt;: Proporciona resultados exactos al buscar en cada celda.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplicidad&lt;/strong&gt;: Fácil de entender y configurar.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Contras de IndexIVFFlat
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Velocidad&lt;/strong&gt;: Puede ser muy lento con grandes volúmenes de datos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Uso de Memoria&lt;/strong&gt;: No optimiza el uso de memoria, lo que puede ser un problema con conjuntos de datos grandes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Pros de IndexIVFPQ
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Velocidad&lt;/strong&gt;: Mucho más rápido en búsquedas debido a la reducción del espacio de búsqueda.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Eficiencia de Memoria&lt;/strong&gt;: Reduce significativamente el uso de memoria, lo que permite manejar conjuntos de datos más grandes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Contras de IndexIVFPQ
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Precisión&lt;/strong&gt;: Puede haber una ligera pérdida en la precisión debido a la compresión.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complejidad&lt;/strong&gt;: La configuración y entrenamiento son más complejos que en IndexIVFFlat.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Alternativas
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;IndexFlatL2&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Realiza una búsqueda exhaustiva sin compresión. Ideal para conjuntos de datos pequeños donde se requiere precisión máxima.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;IndexPQ&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Utiliza solo cuantización de producto sin agrupación. Es útil cuando se necesita un balance entre velocidad y precisión, pero no se requiere agrupación.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;IndexIVFScalarQuantizer&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Combina el índice invertido con cuantización escalar, ofreciendo un enfoque diferente para reducir el uso de memoria y mejorar la velocidad.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;IndexIVFPQR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Una variante que combina IVF y PQ con re-ranking basado en códigos, ofreciendo un equilibrio entre velocidad y precisión mejorada.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Composite Indexes&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Usar &lt;code&gt;index_factory&lt;/code&gt; para crear índices compuestos que combinan múltiples técnicas (por ejemplo, OPQ + IVF + PQ) para optimizar aún más el rendimiento.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Estas alternativas permiten adaptar la solución a diferentes necesidades en cuanto a precisión, velocidad y uso de memoria según el caso específico que estés abordando.&lt;/p&gt;

&lt;p&gt;Citations:&lt;br&gt;
[1] &lt;a href="https://www.pinecone.io/learn/series/faiss/faiss-tutorial/" rel="noopener noreferrer"&gt;https://www.pinecone.io/learn/series/faiss/faiss-tutorial/&lt;/a&gt;&lt;br&gt;
[2] &lt;a href="https://www.pinecone.io/learn/series/faiss/product-quantization/" rel="noopener noreferrer"&gt;https://www.pinecone.io/learn/series/faiss/product-quantization/&lt;/a&gt;&lt;br&gt;
[3] &lt;a href="https://www.pinecone.io/learn/series/faiss/composite-indexes/" rel="noopener noreferrer"&gt;https://www.pinecone.io/learn/series/faiss/composite-indexes/&lt;/a&gt;&lt;br&gt;
[4] &lt;a href="https://github.com/facebookresearch/faiss/wiki/Faiss-indexes/9df19586b3a75e4cb1c2fb915f2c695755a599b8" rel="noopener noreferrer"&gt;https://github.com/facebookresearch/faiss/wiki/Faiss-indexes/9df19586b3a75e4cb1c2fb915f2c695755a599b8&lt;/a&gt;&lt;br&gt;
[5] &lt;a href="https://faiss.ai/cpp_api/struct/structfaiss_1_1IndexIVFFlat.html" rel="noopener noreferrer"&gt;https://faiss.ai/cpp_api/struct/structfaiss_1_1IndexIVFFlat.html&lt;/a&gt;&lt;br&gt;
[6] &lt;a href="https://pub.towardsai.net/unlocking-the-power-of-efficient-vector-search-in-rag-applications-c2e3a0c551d5?gi=71a82e3ea10e" rel="noopener noreferrer"&gt;https://pub.towardsai.net/unlocking-the-power-of-efficient-vector-search-in-rag-applications-c2e3a0c551d5?gi=71a82e3ea10e&lt;/a&gt;&lt;br&gt;
[7] &lt;a href="https://www.pingcap.com/article/mastering-faiss-vector-database-a-beginners-handbook/" rel="noopener noreferrer"&gt;https://www.pingcap.com/article/mastering-faiss-vector-database-a-beginners-handbook/&lt;/a&gt;&lt;br&gt;
[8] &lt;a href="https://wangzwhu.github.io/home/file/acmmm-t-part3-ann.pdf" rel="noopener noreferrer"&gt;https://wangzwhu.github.io/home/file/acmmm-t-part3-ann.pdf&lt;/a&gt;&lt;br&gt;
[9] &lt;a href="https://github.com/alonsoir/ubiquitous-carnival/blob/main/contextual-data-faiss-IndexIVFPQ.py" rel="noopener noreferrer"&gt;https://github.com/alonsoir/ubiquitous-carnival/blob/main/contextual-data-faiss-IndexIVFPQ.py&lt;/a&gt;&lt;br&gt;
[10] &lt;a href="https://github.com/alonsoir/ubiquitous-carnival/blob/main/contextual-data-faiss-indexivfflat.py" rel="noopener noreferrer"&gt;https://github.com/alonsoir/ubiquitous-carnival/blob/main/contextual-data-faiss-indexivfflat.py&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rag</category>
      <category>faiss</category>
      <category>indexes</category>
      <category>python</category>
    </item>
    <item>
      <title>Oracle-JDK23</title>
      <dc:creator>@alonso_isidoro</dc:creator>
      <pubDate>Thu, 03 Oct 2024 10:41:56 +0000</pubDate>
      <link>https://dev.to/alonsoir/oracle-jdk23-39bf</link>
      <guid>https://dev.to/alonsoir/oracle-jdk23-39bf</guid>
      <description>&lt;p&gt;*&lt;em&gt;JDK 23-Oracle.txt&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
Esto es una sesión con la jshell después de instalar el jdk 23-oracle con sdkman.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jshell --enable-preview
|  Welcome to JShell -- Version 23
|  For an introduction type: /help intro
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;La JDK 23 nos trae varias novedades:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Tipos Primitivos en Patterns (JEP 455)

2. Tipos Primitivos en Patterns (JEP 476)

3. Scoped Values (JEP 481)

4. Clases y Métodos de Instancia Declarados Implícitamente (JEP 477)

5. Class-File API (JEP 466)

6. Cuerpos de Constructor Flexibles (JEP 482)

7. Comentarios en Markdown para Documentación (JEP 467)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;*&lt;em&gt;1. Tipos Primitivos en Patterns (JEP 455)&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;&lt;br&gt;
Veamoslo en un ejemplo.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jshell&amp;gt; 
System.out.println("Pattern Matching New Model.... Primitive Supported");
       Object o = 127;
        switch (o) {
            case int i -&amp;gt; System.out.println("Integer: " + i);
            case long l -&amp;gt; System.out.println("Long: " + l);
            default -&amp;gt; System.out.println("Other: " + o);
        }
Pattern Matching New Model.... Primitive Supported
o ==&amp;gt; 127
Integer: 127
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;*&lt;em&gt;2. Tipos Primitivos en Patterns (JEP 476)&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
         jshell&amp;gt; &lt;br&gt;
        // New way of Java&lt;br&gt;
        import static java.lang.System.out;&lt;br&gt;
        import module java.base;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    public class _3_ModuleImport {

        public static void main (String[] args) throws Exception{
            // Multiple API's are called with a single import.  JEP 476
            // import module java.base
            System.out.println("java --enable-preview _3_ModuleImport.java");
            System.out.println("Array of Values  = "+List.of("Hello ", "Module Import ", "World!"));
            File file = new File("README.MD");
            System.out.println("File README.MD = "+file.canRead());
            System.out.println("Reading the file....");
            try (BufferedReader br = new BufferedReader(new FileReader(file))) {
                while(br.ready()) {
                    System.out.println(br.readLine());
                }
            }
            System.out.println("File closed ....");

        }
    }

Me aseguro que en el directorio donde he generado .java anterior tiene un README.md

    ls README.md
    README.md

Compilo y ejecuto el nuevo fichero, ojito que hay que activar los flags.

    javac --enable-preview --release 23 _3_ModuleImport.java
    java --enable-preview _3_ModuleImport
    java --enable-preview _3_ModuleImport.java

Por lo visto, da igual usar el .java que no usarlo. Demasiado tiempo con python, supongo...

    Array of Values = [Hello , Module Import , World!]
    File README.MD = true
    Reading the file....
    hola esto es un fichero README de pruebas para que spark-shell-3.5.0 pueda leerlo.
    File closed ....
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;*&lt;em&gt;3. Scoped Values (JEP 481)&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
    Concepto principal&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Un "scoped value" es un objeto contenedor que permite compartir un valor de datos inmutable entre un método y sus llamadas directas e indirectas dentro del mismo hilo, así como con hilos secundarios2. Se declara típicamente como un campo estático final de tipo ScopedValue.

Ventajas sobre variables thread-local
    Los Scoped Values ofrecen varias ventajas sobre las variables thread-local tradicionales:

    Mayor facilidad de razonamiento sobre el flujo de datos

    Menor costo en espacio y tiempo, especialmente al usarse con hilos virtuales y concurrencia estructurada

    Garantía de inmutabilidad de los datos compartidos

    Ciclo de vida más predecible y acotado

Ejemplo: Sistema de Comercio Electrónico

Configuración de Scoped Values

    public class ApplicationContext {
        public static final ScopedValue&amp;lt;UserSession&amp;gt; USER_SESSION = ScopedValue.newInstance();
        public static final ScopedValue&amp;lt;AuditInfo&amp;gt; AUDIT_INFO = ScopedValue.newInstance();
    }

Filtro de Servlet para inicializar el contexto

    @WebFilter("/*")
    public class ContextInitializationFilter implements Filter {
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
                throws IOException, ServletException {
            HttpServletRequest httpRequest = (HttpServletRequest) request;
            UserSession userSession = authenticateAndCreateUserSession(httpRequest);
            AuditInfo auditInfo = new AuditInfo(UUID.randomUUID().toString());

            ScopedValue.where(ApplicationContext.USER_SESSION, userSession)
                .where(ApplicationContext.AUDIT_INFO, auditInfo)
                .run(() -&amp;gt; chain.doFilter(request, response));
        }

        private UserSession authenticateAndCreateUserSession(HttpServletRequest request) {
            // Lógica de autenticación
            // ...
        }
    }

Servicio de Carrito de Compras

    @Service
    public class ShoppingCartService {
        @Autowired
        private ProductRepository productRepository;
        @Autowired
        private CartRepository cartRepository;

        public void addToCart(String productId, int quantity) {
            UserSession userSession = ApplicationContext.USER_SESSION.get();
            AuditInfo auditInfo = ApplicationContext.AUDIT_INFO.get();

            Product product = productRepository.findById(productId)
                .orElseThrow(() -&amp;gt; new ProductNotFoundException(productId));

            Cart cart = cartRepository.findByUserId(userSession.getUserId())
                .orElseGet(() -&amp;gt; new Cart(userSession.getUserId()));

            cart.addItem(new CartItem(product, quantity));
            cartRepository.save(cart);

            logAuditEvent(auditInfo, "ADD_TO_CART", Map.of(
                "userId", userSession.getUserId(),
                "productId", productId,
                "quantity", quantity
            ));
        }

        private void logAuditEvent(AuditInfo auditInfo, String eventType, Map&amp;lt;String, Object&amp;gt; details) {
            // Lógica para registrar el evento de auditoría
            // ...
        }
    }

Controlador REST

    @RestController
    @RequestMapping("/api/cart")
    public class ShoppingCartController {
        @Autowired
        private ShoppingCartService shoppingCartService;

        @PostMapping("/add")
        public ResponseEntity&amp;lt;String&amp;gt; addToCart(@RequestBody AddToCartRequest request) {
            shoppingCartService.addToCart(request.getProductId(), request.getQuantity());
            return ResponseEntity.ok("Producto añadido al carrito");
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;**  Explicación del ejemplo&lt;br&gt;
**&lt;br&gt;
    Configuración: Definimos dos ScopedValue: uno para la sesión del usuario y otro para la información de auditoría.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Filtro de Servlet: Inicializa los ScopedValue para cada solicitud HTTP. Esto asegura que la información de sesión y auditoría esté disponible para todo el procesamiento de la solicitud.

Servicio de Carrito: Utiliza los ScopedValue para acceder a la información de sesión del usuario y los detalles de auditoría sin necesidad de pasarlos explícitamente como parámetros.

Controlador: Simplemente llama al servicio, sin preocuparse por pasar información de contexto.

Beneficios en este escenario

Limpieza del código: Los métodos no necesitan pasar explícitamente la información de sesión o auditoría.

Seguridad: La información de sesión está contenida y no puede ser modificada accidentalmente.

Facilidad de testing: Es más fácil simular diferentes contextos en pruebas unitarias.

Rendimiento: Especialmente útil con hilos virtuales, ya que no hay sobrecarga de almacenamiento por hilo.

Este ejemplo muestra cómo los Scoped Values pueden simplificar significativamente el manejo de información contextual en una aplicación web, mejorando la legibilidad del código y la seguridad de los datos.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;*&lt;em&gt;4. Clases y Métodos de Instancia Declarados Implícitamente (JEP 477)&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
    Esta característica, que se encuentra en su tercera versión preliminar en el JDK 23, está diseñada para hacer que Java sea más accesible para los principiantes y simplificar el código para todos los desarrolladores5.&lt;/p&gt;

&lt;p&gt;**  Objetivo principal&lt;br&gt;
**&lt;br&gt;
    El propósito es permitir declaraciones de clase más simples y una expansión más fácil de los programas, especialmente para los nuevos programadores, un tanto más vagos que las anteriores generaciones. En mi opinión, han introducido esto para atraer a los programadores python que se atragantan con tanta verbosidad. :)&lt;/p&gt;

&lt;p&gt;**  Características clave&lt;br&gt;
**&lt;br&gt;
    Declaraciones de clase simplificadas: Permite omitir la declaración explícita de la clase en programas simples.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Métodos de instancia main: Introduce la posibilidad de tener un método main de instancia en lugar del tradicional método estático.

Importaciones automáticas: El JDK 23 incluye la importación automática de tres métodos estáticos para E/S de texto y todas las clases e interfaces públicas de nivel superior de los paquetes exportados por el módulo java.base5.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;**  Ejemplo de uso&lt;br&gt;
**  Antes de esta característica, un programa simple en Java requería una estructura como esta:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    public class HelloWorld {
        public static void main(String[] args) {
            System.out.println("Hello, World!");
        }
    }

Con las clases y métodos de instancia declarados implícitamente, el mismo programa podría escribirse así:

    void main() {
        System.out.println("Hello, World!");
    }

**Beneficios**

**Reducción de la verbosidad**: Elimina la necesidad de declarar explícitamente la clase y el método estático main para programas simples.

**Facilita el aprendizaje**: Hace que Java sea más accesible para principiantes, permitiéndoles centrarse en la lógica del programa sin preocuparse por la estructura de clases en etapas iniciales.

**Evolución gradual**: Permite a los programadores comenzar con programas simples y expandirlos gradualmente a medida que crecen en complejidad.

**Alineación con otros lenguajes**: Se asemeja a la estructura de programas en lenguajes como Python o JavaScript, lo que puede facilitar la transición a Java para desarrolladores de otros lenguajes5.

**Contexto y comparación**
Esta característica se inspira en enfoques similares adoptados por otros lenguajes y frameworks populares entre los principiantes, como Node.js. C# implementó una funcionalidad parecida hace algunas versiones con el mismo objetivo de simplificar la entrada al lenguaje5.

**Conclusión**
Las Clases y Métodos de Instancia Declarados Implícitamente representan un esfuerzo significativo por parte de Java para modernizar su sintaxis y hacerla más accesible, especialmente para nuevos programadores. Aunque está en su tercera versión preliminar, esta característica promete cambiar la forma en que se escriben los programas Java simples, allanando el camino para una curva de aprendizaje más suave y una transición más fácil desde otros lenguajes de programación.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Class-File API (JEP 466)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;**  Propósito y funcionalidad&lt;br&gt;
**  La Class-File API está diseñada para parsear, generar y transformar archivos de clase Java (.class). Se define en el paquete java.lang.classfile y proporciona una interfaz estándar para procesar archivos de clase que se alinea con el formato definido en la Especificación de la Máquina Virtual de Java12.&lt;/p&gt;

&lt;p&gt;**  Características principales&lt;br&gt;
**&lt;br&gt;
    &lt;strong&gt;Representación inmutable&lt;/strong&gt;: Todos los elementos del archivo de clase (campos, métodos, atributos, instrucciones de bytecode, etc.) se representan como objetos inmutables12.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**Estructura de árbol**: Refleja la naturaleza jerárquica de los archivos de clase2.

**Navegación dirigida por el usuario**: Permite una análisis eficiente1.

**Parseo perezoso**: Procesa solo las partes del archivo de clase que el usuario requiere1.

**Transformación como propiedad emergente**: No requiere un modo especial o una superficie de API significativamente nueva para las transformaciones1.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;**  Abstracciones principales&lt;br&gt;
**&lt;br&gt;&lt;br&gt;
    &lt;strong&gt;Elementos&lt;/strong&gt;: Descripciones inmutables de componentes del archivo de clase.&lt;br&gt;
    &lt;strong&gt;Constructores (Builders)&lt;/strong&gt;: Facilitan la construcción de archivos de clase.&lt;br&gt;
    &lt;strong&gt;Transformaciones&lt;/strong&gt;: Funciones que modifican elementos durante el proceso de construcción2.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**Beneficios**

**Evolución conjunta**: La API evolucionará junto con el formato de archivo de clase, facilitando la adopción rápida de nuevas características del lenguaje y la JVM24


**Estandarización**: Proporciona una API estándar para el procesamiento de archivos de clase, reduciendo la dependencia de bibliotecas de terceros2.

**Soporte automático**: Las herramientas y frameworks que usen esta API soportarán automáticamente los archivos de clase de las últimas versiones del JDK1.

**Aplicaciones**

Esta API es particularmente útil para:

Frameworks y bibliotecas que necesitan manipular bytecode.
Herramientas de análisis de código.
Generación dinámica de clases en tiempo de ejecución.

Es decir, es probable que esta característica no vaya a ser útil para un developer normal si no para creadores de frameworks, como si hubiera pocos.
Con suerte, veremos integrada esta feature en una RELEASE de spring-boot cuando adopten el JDK23, al igual que la anterior feature.

Estado actual

La Class-File API es una característica en preview en el JDK 23, lo que significa que su diseño y especificación están completos, pero puede cambiar en futuras versiones de Java1.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;6. Cuerpos de Constructor Flexibles (JEP 482)&lt;br&gt;
**&lt;br&gt;
    **Objetivo&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;El objetivo principal de esta JEP es permitir que los cuerpos de los constructores contengan declaraciones antes de la invocación explícita de otro constructor (super(...) o this(...)). Esto facilita la inicialización de campos en la misma clase antes de llamar a un constructor de la superclase, lo que puede ser crucial para evitar que el código en el constructor de la superclase acceda a valores predeterminados (como 0, false o null) de los campos en la subclase.

**Cambios Clave**
Estructura del Cuerpo del Constructor: Se permite que el cuerpo del constructor contenga declaraciones antes de una invocación explícita, lo que se denomina prologue. Las declaraciones después de la invocación se denominan epilogue.

    ConstructorBody:
        { [BlockStatements] ExplicitConstructorInvocation [BlockStatements] }

**Inicialización Previo a la Invocación**: Los constructores pueden inicializar campos antes de llamar a super(...), permitiendo así que los métodos en la superclase operen sobre valores ya inicializados.

**Validación y Manejo de Errores**: Permite realizar validaciones y lanzar excepciones antes de llamar al constructor de la superclase, lo que mejora el manejo de errores y permite un estilo más limpio y directo.

**Ejemplo Práctico**

Consideremos un ejemplo donde queremos validar un valor antes de pasarle al constructor de la superclase:

    class Vehicle {
        private final int value;

        Vehicle(int value) {
            this.value = value;
        }
    }

    class EngineValueSensor extends Vehicle {
        private final String type = "engine_sensor";

        EngineValueSensor(Integer value) {
            if (value &amp;lt;= 0) {
                throw new IllegalArgumentException("Value must be greater than zero: " + value);
            }
            super(value); // Llamada al constructor de Vehicle
        }
    }

En este ejemplo, si el valor es inválido, lanzamos una excepción antes de llamar al constructor super(value), asegurando así que no se cree un objeto con un estado inválido.

**Beneficios**

**Simplicidad y Legibilidad**: Reduce la necesidad de métodos auxiliares para preparar argumentos antes de las llamadas al constructor.

**Mejor Mantenimiento**: Facilita el mantenimiento del código al permitir una estructura más lógica y menos fragmentada.

**Flexibilidad**: Permite patrones de diseño más simples y efectivos, como el patrón Factory, donde se pueden crear instancias con configuraciones complejas sin complicaciones adicionales.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;*&lt;em&gt;7. Comentarios en Markdown para Documentación (JEP 467)&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/// Example Class
    /// 
    /// This class demonstrates the use of Markdown in JavaDoc comments.
    /// 
    /// #Features
    /// - Easy to read and write
    /// - Supports Markdown syntax for formatting
    /// 
    /// ##Methods
    /// 
    /// - `void exampleMethod(int param)` - An example method that does something.
    /// 
    /// #Usage
    /// To use this class, create an instance and call the method:
    ///
    /// ```

java
    /// ExampleClass example = new ExampleClass();
    /// example.exampleMethod(10);
    ///

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/// 
/// ##Note
/// This feature simplifies documentation by allowing Markdown syntax instead of HTML.
/// 
/// @param param an integer parameter for the example method
public class ExampleClass {
    /// This is an example method.
    /// 
    /// @param param an integer parameter for the method
    public void exampleMethod(int param) {
        // Implementation goes here
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


**  Explicación del Código
**  
    **Encabezados y Formato**:
    Se utilizan encabezados (#, ##, ###) para estructurar la documentación, lo que facilita la lectura.
    Se pueden crear listas usando guiones (-) para enumerar características.

    **Código en Bloques**:
    El uso de tres acentos graves (

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
) permite incluir ejemplos de código, lo que es útil para mostrar cómo utilizar la clase o método.&lt;/p&gt;

&lt;p&gt;**  Parámetros y Notas:&lt;br&gt;
**  Se pueden incluir descripciones de parámetros utilizando la etiqueta &lt;a class="mentioned-user" href="https://dev.to/param"&gt;@param&lt;/a&gt;, manteniendo la compatibilidad con las convenciones de JavaDoc.&lt;/p&gt;

&lt;p&gt;**  Markdown vs. HTML:&lt;br&gt;
**  Este enfoque evita la complejidad del HTML, haciendo que los comentarios sean más accesibles y fáciles de mantener.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**Ventajas**:
    Simplicidad: La sintaxis Markdown es más fácil de escribir y leer en comparación con HTML.

    Compatibilidad: Las etiquetas JavaDoc tradicionales siguen siendo utilizables dentro de los comentarios en Markdown.

    Flexibilidad: Permite una mejor presentación de la documentación, incluyendo listas, enlaces y bloques de código.

**Conclusión**:

    El uso de comentarios en Markdown para documentación en Java (JEP 467) mejora significativamente la experiencia del desarrollador al escribir y mantener documentación. Este enfoque permite a los desarrolladores aprovechar la simplicidad y claridad del Markdown mientras mantienen la funcionalidad completa de JavaDoc.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Stream Gatherers (JEP 473)&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.Gatherers;

public class StreamGatherersExample {
    public static void main(String[] args) {
        // Generar un flujo de números enteros
        List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; sample = Stream.iterate(0, i -&amp;gt; i + 2)
            .gather(Gatherers.windowFixed(2)) // Usar un gatherer para agrupar elementos en ventanas fijas
            .limit(5) // Limitar el flujo a 5 grupos
            .collect(Collectors.toList()); // Recoger el resultado en una lista

        // Imprimir el resultado
        System.out.println(sample);
    }
}

javac --enable-preview --release 23 StreamGatherersExample.java
Note: StreamGatherersExample.java uses preview features of Java SE 23.
Note: Recompile with -Xlint:preview for details.

java --enable-preview StreamGatherersExample
[[0, 2], [4, 6], [8, 10], [12, 14], [16, 18]]

Explicación del Código

Importaciones:
Se importan las clases necesarias para trabajar con flujos y colectores.

Generación del Flujo:
Stream.iterate(0, i -&amp;gt; i + 2) genera un flujo infinito de números enteros comenzando desde 0 y aumentando de 2 en 2.

Uso del Gatherer:
    gather(Gatherers.windowFixed(2)) utiliza un gatherer predefinido que agrupa los elementos en ventanas fijas de tamaño 2. Esto significa que cada grupo contendrá dos elementos del flujo original.

Limitación del Flujo:
    limit(5) restringe el flujo a solo 5 grupos, lo que resulta en un total de 10 elementos.

Colección del Resultado:
collect(Collectors.toList()) convierte el flujo resultante en una lista de listas.

Impresión del Resultado:
Finalmente, se imprime la lista resultante, que debería mostrar grupos de números enteros.

Salida Esperada
Al ejecutar el código anterior, la salida será:
text
[[0, 2], [4, 6], [8, 10], [12, 14], [16, 18]]

Esto indica que los números han sido agrupados correctamente en ventanas de tamaño 2.

Conclusión

El uso de Stream Gatherers permite a los desarrolladores crear operaciones intermedias personalizadas que no son fácilmente alcanzables con las operaciones integradas existentes. Esto proporciona mayor flexibilidad y expresividad al trabajar con flujos de datos en Java.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;*&lt;em&gt;Z Garbage Collector (ZGC) en Modo Generacional por Defecto (JEP 474)&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
    java -XX:+UseZGC -XX:+ZGenerational -jar tu_aplicacion.trabajar&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Netcat pivot relay</title>
      <dc:creator>@alonso_isidoro</dc:creator>
      <pubDate>Wed, 20 Jul 2022 10:17:55 +0000</pubDate>
      <link>https://dev.to/alonsoir/netcat-pivot-relay-4fgp</link>
      <guid>https://dev.to/alonsoir/netcat-pivot-relay-4fgp</guid>
      <description>&lt;p&gt;The idea is that you are redirecting traffic from a port not controlled by the firewall (40) to one that is controlled by the firewall (23),ie you want to send something to the port closed by the firewall. To do this, once you have access to the destination machine, you are going to raise a netcat service listening for 23.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; nc -lvp 23
listening on [any] 23 ...
connect to [127.0.0.1] from localhost [127.0.0.1] 42662
hello
redirecting traffic from port 40 that will be outside the control of the firewall to port 23 that will be controlled by the firewall

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, on another machine or on the same vulnerable machine, you are going to create a file of special characters (pivot) that will serve as a stack where to send the exploit&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;man mknod
NAME
mknod - make block or character special files
...


&amp;gt; mknod pivot p
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the bridge from unfiltered port 40 to fw-filtered port 23, using the stack.&lt;br&gt;
When I write to port 40, I read from the stack, when I read from 23, I write to the stack.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; nc -lvp 40 0&amp;lt;pivot | nc 127.0.0.1 23 &amp;gt; pivot
listening on [any] 40 ...
connect to [127.0.0.1] from localhost [127.0.0.1] 44386
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally create the connection to the vulnerable port. &lt;br&gt;
What you write here is eventually written to the port supposedly filtered by the firewall.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; nc 127.0.0.1 40
hello
redirecting traffic from port 40 that will be outside the control of the firewall to port 23 that will be controlled by the firewall.
This could be some exploit to vuln port 23...

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thanks Santiago ;)&lt;/p&gt;

</description>
      <category>netcat</category>
    </item>
  </channel>
</rss>
