<script data-pm-proxy="intercept"></script><?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[SACAViX's Substack]]></title><description><![CDATA[Hablamos de microservicios, diseño de software, java, spring y programación del lado del backend.]]></description><link>https://sacavix.substack.com</link><image><url>https://substackcdn.com/image/fetch/$s_!Ftre!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8c3095c-b9ab-435c-877e-13d25f3e12ac_900x900.png</url><title>SACAViX&apos;s Substack</title><link>https://sacavix.substack.com</link></image><generator>Substack</generator><lastBuildDate>Fri, 04 Sep 2026 18:07:31 GMT</lastBuildDate><atom:link href="/__u/sacavix.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Yoandy]]></copyright><language><![CDATA[es]]></language><webMaster><![CDATA[sacavix@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[sacavix@substack.com]]></itunes:email><itunes:name><![CDATA[Yoandy]]></itunes:name></itunes:owner><itunes:author><![CDATA[Yoandy]]></itunes:author><googleplay:owner><![CDATA[sacavix@substack.com]]></googleplay:owner><googleplay:email><![CDATA[sacavix@substack.com]]></googleplay:email><googleplay:author><![CDATA[Yoandy]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Cuando una instancia de MySQL no es suficiente]]></title><description><![CDATA[Un MySQL no es suficiente cuando comienzas a escalar. Ac&#225; aprender&#225;s como funciona el Database Sharding con shards virtuales.]]></description><link>https://sacavix.substack.com/p/cuando-una-instancia-de-mysql-no</link><guid isPermaLink="false">https://sacavix.substack.com/p/cuando-una-instancia-de-mysql-no</guid><dc:creator><![CDATA[Yoandy]]></dc:creator><pubDate>Mon, 27 Jul 2026 12:01:54 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!A7wE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87eb5cbe-d1f4-4454-856a-f01dba8dcdcc_2280x1197.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Cuando se inicia un proyecto nuevo, mayoritariamente se adoptan tecnolog&#237;as simples, de amplio dominio p&#250;blico y que funcionan perfectamente en el 95% de los proyectos; el problema aparece cuando empieza a escalar el negocio. Las bases de datos como MySQL o PostgreSQL ofrecen de forma nativa mecanismos de replicaci&#243;n basados en el patr&#243;n primario/replicas. Eso significa que todas las escrituras van sobre una &#250;nica instancia y eventualmente puede realizar lecturas sobre cualquiera de las replicas. </p><p>Cuando se alcanza la cifra de docena de millones de usuarios y cientos o miles de transacciones por segundo, comienzan a aparecer poblemas, como por ejemplo temas de contenci&#243;n, lag, consultas lentas, los datos ya no caben en memoria RAM y comienzan las lecturas frecuentes sobre el disco, etc. Todo esto comienza a degradar significativamente la calidad del servicio. Siempre se puede escalar verticalmente agregando m&#225;s RAM/CPU, etc. El problema es que eso tiene un l&#237;mite, verticalmente no existe el infinito.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Suscribirse&quot;,&quot;language&quot;:&quot;es&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">&#161;Gracias por leer SACAViX's Substack! Suscr&#237;bete gratis para recibir nuevos posts y apoyar mi trabajo.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Escribe tu correo electr&#243;nico..." tabindex="-1"><input type="submit" class="button primary" value="Suscribirse"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>En 2011, Instagram ten&#237;a tres ingenieros y m&#225;s de 25 millones de usuarios. Corr&#237;an sobre una &#250;nica instancia primaria de PostgreSQL, el punto de quiebre no fue el disco: fue la RAM. Cuando los registros dejan de caber en memoria, cada escritura empieza a pegarle al disco, la latencia p95/99 se dispar&#243;, ni con replicas pudieron resolver el problema.</p><p>Pinterest hab&#237;a chocado contra la misma pared con MySQL, pero peor: intentaron escalar agregando tecnolog&#237;as diferentes (Cassandra, MongoDB, Membase). Su conclusi&#243;n: cada sistema nuevo tra&#237;a modos de fallo y tradeoffs diferentes. </p><p>Volvieron a MySQL nuevamente, pero fragmentado, aplicando el patr&#243;n <strong>Database</strong> <strong>Sharding.</strong></p><p>Entender el sharding es fundamental si trabajar con sistemas a escala.</p><h3>&#191;Qu&#233; problema resuelve el sharding?</h3><p>Antes de llegar a sharding en una base de datos relacional hay una serie de &#8220;medidas&#8220; que se toman previamente, el sharding es el &#250;ltimo recurso.</p><ol><li><p>Escalar: Agregar CPU, RAM, mejorar el hardware del disco SSD, NVM, mejorar el sistema de sistema de archivos, etc.</p></li><li><p>Replicas: Separar las lecturas a instancias dedicadas que mediante replicaci&#243;n tomen los datos de la instancia de escritura y los disponibilicen. No todas las lecturas pueden ir a una replica, los datos en las r&#233;plicas est&#225;n disponibles eventualmente, entonces puede haber cierto &#8220;lag&#8220; o delay en los datos.</p></li><li><p>Particiones: Sigue siendo una misma instancia pero la data internamente se particiona (verticalmente), por ejemplo por fecha, entonces toda la data de julio &#8220;cae&#8220; en una misma partici&#243;n, esto genera &#237;ndices m&#225;s peque&#241;os locales por partici&#243;n y permite que los sistemas puedan aplicar optimizaciones como el prunning o el wise join, entre otros. </p></li><li><p>Sharding: Permite partir las filas de una misma tabla entre nodos independientes, tambi&#233;n se conoce como sharding horizontal.</p></li></ol><p>Cuando se aplica sharding se resuelve el problema de poder tener diferentes instancias de escritura y lectura que absorven por separado la carga de escrituras y lecturas, permitiendo redistribuir el volumen de operaciones entre distintos nodos.</p><blockquote><p>Si quieres aprender m&#225;s sobre dise&#241;o de sistemas y patrones te invito a visitar <a href="https://systemdesign.sacavix.com/">SACAViX System Design</a>. Hay ejercicios reales para dise&#241;ar sistemas, preguntas de entrevistas t&#233;cnicas, est&#225;n los t&#233;rminos m&#225;s usados en system design y una gu&#237;a completa de 95 patrones explicados que debes conocer.</p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!qJz-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa40eaa61-ccd5-4024-a42c-6b9253f38190_1321x576.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!qJz-!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa40eaa61-ccd5-4024-a42c-6b9253f38190_1321x576.png 424w, /__u/substackcdn.com/image/fetch/$s_!qJz-!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa40eaa61-ccd5-4024-a42c-6b9253f38190_1321x576.png 848w, /__u/substackcdn.com/image/fetch/$s_!qJz-!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa40eaa61-ccd5-4024-a42c-6b9253f38190_1321x576.png 1272w, /__u/substackcdn.com/image/fetch/$s_!qJz-!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa40eaa61-ccd5-4024-a42c-6b9253f38190_1321x576.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!qJz-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa40eaa61-ccd5-4024-a42c-6b9253f38190_1321x576.png" width="1321" height="576" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a40eaa61-ccd5-4024-a42c-6b9253f38190_1321x576.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:576,&quot;width&quot;:1321,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:169127,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sacavix.substack.com/i/208508798?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa40eaa61-ccd5-4024-a42c-6b9253f38190_1321x576.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!qJz-!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa40eaa61-ccd5-4024-a42c-6b9253f38190_1321x576.png 424w, /__u/substackcdn.com/image/fetch/$s_!qJz-!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa40eaa61-ccd5-4024-a42c-6b9253f38190_1321x576.png 848w, /__u/substackcdn.com/image/fetch/$s_!qJz-!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa40eaa61-ccd5-4024-a42c-6b9253f38190_1321x576.png 1272w, /__u/substackcdn.com/image/fetch/$s_!qJz-!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa40eaa61-ccd5-4024-a42c-6b9253f38190_1321x576.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>La shard key: la decisi&#243;n m&#225;s importante</h3><p>El elemento fundamental y m&#225;s importante para poder aplicar sharding es determinar la clave de partici&#243;n. La elecci&#243;n es fundamental porque determinar&#225; posteriormente si las consultas que se realicen sean simples o extremadamente complejas.</p><p>La elecci&#243;n de la clave debe permitir consultas &#8220;triviales&#8220; y evitar lo que t&#233;cnicamente se conoce como &#8220;scatter-gather&#8221;.</p><ul><li><p>Triviales: Solo necesitan ir a un nodo especifico, se evita el cross-join, son simples de ejecutar.</p></li><li><p>Scatter-Gather: No hay forma de saber en que nodo esta el dato, hay que buscarlo en varios nodos y luego unir los resultados, aumentando la complejidad del sistema y degradando su performance.</p></li></ul><p>Ejemplos: </p><p>Si el shard key es el <code>user_id</code> y necesita la informaci&#243;n de un usuario, solo tienes que ir al shard donde esta ese usuario alojado y listo:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;951c5d68-5d95-406d-abb1-5c5f1ade683e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">SELECT * FROM orders WHERE user_id = 456</code></pre></div><p>En cambio, si la clave es <code>user_id</code>, pero por cuestiones del negocio tienes que hacer una consulta como la siguiente, entonces la shard key correcta no ser&#237;a <code>user_id</code>.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;79a6478c-a39d-479c-9761-6c18b852658d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">SELECT * FROM orders WHERE status = 'PENDING'</code></pre></div><p>En este &#250;ltimo caso, para buscar las ordenes pendientes de todos los usuarios tendrias que ejecutar la query en todos los shards y luego unificar los datos.</p><h4>Propiedades de una buena shard key</h4><p>Las tres principales propiedades al elegir una shard key son: </p><ol><li><p><strong>Alta cardinalidad:</strong> Muchos valores distintos para repartir en shards es la mejor decisi&#243;n, ejemplos malos: country (son solo unos 200), status (active, inactivo), ejemplos de buenas claves: user_id, customer_id, account_number, etc.</p></li><li><p><strong>Distribuci&#243;n uniforme:</strong> Los datos que se generan y el tr&#225;fico deben poder distribuirse lo m&#225;s equitativamente posible, sino el sistema genera lo que se llaman hotspots (toda la carga concentrada en unos pocos shards).</p></li><li><p><strong>Alineaci&#243;n con el patr&#243;n de acceso dominante:</strong> Las queries m&#225;s frecuentes llevan la <code>key</code> en el <code>WHERE</code>.</p></li><li><p><strong>Inmutabilidad:</strong> Una vez insertada la clave de partici&#243;n no puede actualizarse / cambiarse.</p></li></ol><p>El pecado capital es la hacer consultas<strong> cross-shard</strong>. Si para cargar el feed del usuario hay que preguntar a 4.000 nodos y hacer merge en la app, cambiamos un problema de escala por uno de latencia (la query tarda lo que tarda el shard m&#225;s lento) y de disponibilidad (si 1 de 4.000 est&#225; ca&#237;do, la query falla). Evita SIEMPRE el cross-shard y cross-join.</p><h3>&#191;C&#243;mo elegir a que shard va un registro?</h3><p>Las estrategias m&#225;s comunes son:</p><ul><li><p><strong>Por rango:</strong> usuarios 1&#8211;1M en el Shard A, de 1M&#8211;2M en el Shard B. F&#225;cil de razonar, escaneos por rango eficientes, pero produce hotspots: los usuarios nuevos (los m&#225;s activos probablemente) caen todos en el &#250;ltimo shard.</p></li><li><p><strong>Por hash: </strong>Aplicar una funci&#243;n <code>hash(key) % N</code>, con esto se reparte uniforme y son hotspots, pero aplicar m&#243;dulo de N tiene un defecto: si N es el n&#250;mero de servidores y agregas uno, el m&#243;dulo cambia para casi todas las filas y tendr&#237;as que remigrar casi toda la base; esto es un gran problema.</p></li></ul><p>Para resolver el problema de poder pasar de N a N+1 nodos se pueden aplicar dos mecanismos para el rebalanceo: <em>Consistent hashing</em> y <em>Jump Consistent Hash</em>, pero quedan fuera de este art&#237;culo.</p><h3>Shards virtuales</h3><p>Tanto Instagram como Pinterest resolvieron el problema de agregar nuevos nodos usando el concepto de shards virtuales. Los shards virtuales permite evitar mapear las claves directamente a nodos f&#237;sicos.</p><p>Sin shards virtuales se hace algo simple, ejemplo: </p><p>Tenemos 8 servidores, se enruta as&#237;:</p><pre><code><code>servidor = user_id % 8</code></code></pre><p>El usuario 31341 &#8594; <code>31341 % 8 = 5</code> &#8594; vive en el servidor 5. </p><p>Ahora el negocio crece y agregamos un noveno nodo. Cambiamos a <code>% 9</code>. El usuario 31341 ahora da <code>31341 % 9 = 6</code>. Se movi&#243; de servidor<strong>.</strong> Y no es solo &#233;l: al pasar de <code>% 8</code> a <code>% 9</code>, <em>casi todas</em> las filas cambian de destino. Concretamente, solo ~1/9 de las filas se quedan donde estaban; el resto hay que f&#237;sicamente moverlo entre nodos. Agregar un servidor te obliga a mover la base de datos, con el sistema en producci&#243;n esto es inviable. Cada vez que escalas, cambias la funci&#243;n de mapeo, y cambiar la funci&#243;n de mapeo reubica casi todo.</p><p><strong>La soluci&#243;n: poner una capa en medio que nunca cambia</strong></p><p>En vez de mapear la clave directamente al servidor, la mapeamos a un <strong>shard virtual</strong>, y ese n&#250;mero, por ejemplo 8192 se fija para siempre y no se vuelve a tocar jam&#225;s:</p><pre><code><code>shard_virtual = user_id % 8192      // FIJO. Nunca cambia.</code></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!A7wE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87eb5cbe-d1f4-4454-856a-f01dba8dcdcc_2280x1197.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!A7wE!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87eb5cbe-d1f4-4454-856a-f01dba8dcdcc_2280x1197.png 424w, /__u/substackcdn.com/image/fetch/$s_!A7wE!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87eb5cbe-d1f4-4454-856a-f01dba8dcdcc_2280x1197.png 848w, /__u/substackcdn.com/image/fetch/$s_!A7wE!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87eb5cbe-d1f4-4454-856a-f01dba8dcdcc_2280x1197.png 1272w, /__u/substackcdn.com/image/fetch/$s_!A7wE!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87eb5cbe-d1f4-4454-856a-f01dba8dcdcc_2280x1197.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!A7wE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87eb5cbe-d1f4-4454-856a-f01dba8dcdcc_2280x1197.png" width="1456" height="764" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/87eb5cbe-d1f4-4454-856a-f01dba8dcdcc_2280x1197.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:764,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:208565,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sacavix.substack.com/i/208508798?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87eb5cbe-d1f4-4454-856a-f01dba8dcdcc_2280x1197.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!A7wE!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87eb5cbe-d1f4-4454-856a-f01dba8dcdcc_2280x1197.png 424w, /__u/substackcdn.com/image/fetch/$s_!A7wE!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87eb5cbe-d1f4-4454-856a-f01dba8dcdcc_2280x1197.png 848w, /__u/substackcdn.com/image/fetch/$s_!A7wE!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87eb5cbe-d1f4-4454-856a-f01dba8dcdcc_2280x1197.png 1272w, /__u/substackcdn.com/image/fetch/$s_!A7wE!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F87eb5cbe-d1f4-4454-856a-f01dba8dcdcc_2280x1197.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>El divisor ahora es una constante del sistema, no el n&#250;mero de m&#225;quinas. El usuario 31341 da <code>31341 % 8192 = 6981</code>, y dar&#225; 6981 <strong>hoy con 8 servidores y ma&#241;ana con 800</strong>. La funci&#243;n clave&#8594;shard es inmutable.</p><p>Ahora, &#191;d&#243;nde vive f&#237;sicamente el shard 6981? Eso lo dice una <strong>segunda tabla</strong>, peque&#241;a y mutable:</p><pre><code><code>shard   0    &#8211; 1023  &#8594;  mysql-01
shard 1024   &#8211; 2047  &#8594;  mysql-02
...
shard 6144   &#8211; 7167  &#8594;  mysql-07 ## &lt;&lt;&lt;&lt;&lt;
shard 7168   &#8211; 8191  &#8594;  mysql-08</code></code></pre><p>El shard 6981 cae en el rango de mysql-07. Cuando agreguemos mysql-09, no tocaremos el <code>% 8192</code> de nadie. Solo editaremos la tabla de ruteos: &#8220;los shards 6912&#8211;6981 ahora viven en mysql-09&#8221;. Movemos esas datos f&#237;sicamente (por replicaci&#243;n, async) y cambiamos unas l&#237;neas del mapa, todo ocurre online.</p><p><strong>Por qu&#233; 8192 y no 8.</strong></p><p>El n&#250;mero de shards virtuales es el techo de cu&#225;ntas m&#225;quinas f&#237;sicas podremos tener en el futuro. Con 8192 shards podremos escalar hasta 8192 servidores (1 shard por m&#225;quina en el extremo). Se elige grande y potencia de 2 para que el <code>% 8192</code> sea un simple AND de bits y para que siempre haya granularidad para repartir. Cada m&#225;quina simplemente aloja <em>muchos</em> shards: con 8 m&#225;quinas, 1024 shards cada una; con 64 m&#225;quinas, 128 cada una. El shard virtual es la unidad indivisible de migraci&#243;n.</p><p><strong>Por qu&#233; el mapa es barato.</strong></p><p>Ese mapa de 8192 entradas (shard &#8594; servidor) es diminuto: unos pocos KB. Cabe entero en memoria, se cachea en cada app server, y se guarda en un store altamente disponible (ZooKeeper, etcd) que notifica cambios por <em>watch</em>. Resolver &#8220;&#191;d&#243;nde est&#225; el shard 6981?&#8221; es un lookup en un <code>ConcurrentHashMap</code> local, sin ir a la red. Y como cambia rar&#237;simo (solo al reshardear), la cach&#233; casi nunca se invalida.</p><h3>&#191;Qu&#233; pasa cuando el shard no es derivable del objeto?</h3><p>Supongamos ahora un escenario en el que no podemos derivar el shard directamente, por ejemplo supongamos el siguiente endpoint: </p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;b4ab4988-795b-443a-a4c1-208c3d82d8bd&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">GET /orders/129474739390030303</code></pre></div><p>En este caso, suponiendo el sistema est&#225; particionado por <code>user_id</code> no es posible a partir del id de la orden derivar el shard hacia donde est&#225; la orden, este problema tiene dos enfoques de soluci&#243;n:</p><ul><li><p><strong>Directorio</strong>: Crear una tabla donde se asocie el <code>order_id</code> con el shard y antes de cada consulta siempre ir a este directorio global, buscar el shard y luego ir directamente. Funciona, pero agrega una llama adicional y necesita mantener consistencia entre la fuente principal y una segunda que mantenga la relaci&#243;n.</p></li><li><p><strong>Embeber en el order_id el shard:</strong> En este caso el order_id continua sigue siendo un n&#250;mero, pero tendr&#225; adentro, embebido el shard. Esto se logra usando un generador de secuencia que dentro tenga ciertos bits dedicados al numero del shard, luego haciendo decoding a nivel de bits se puede acceder de forma sencilla al shard id.</p></li></ul><h2>&#191;C&#243;mo se implementa esto en la pr&#225;ctica?</h2><p>La implementaci&#243;n en realidad cualquier asistente de IA puede hacerla, voy a explicar solo temas generales para que se pueda comprender a escala general.</p><p>Cuando ya tenemos la key elegida y/o es derivable del id, entonces lo primero que tenemos que hacer es definir el mecanismo de shards virtuales, para ello se implementa una estructura en base de datos como la siguiente: </p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;c1fc0a1d-4188-47ca-b430-6b50fabdc272&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">CREATE DATABASE IF NOT EXISTS shard_topology;
USE shard_topology;

-- Inventario de servidores MySQL f&#237;sicos donde viven los shards.
CREATE TABLE physical_server (
  server_id   VARCHAR(32)  NOT NULL,            -- 'mysql-01'
  host        VARCHAR(255) NOT NULL,            -- 'mysql-01.internal'
  port        INT          NOT NULL DEFAULT 3306,
  role        ENUM('PRIMARY','REPLICA') NOT NULL DEFAULT 'PRIMARY',
  status      ENUM('ACTIVE','DRAINING','OFFLINE') NOT NULL DEFAULT 'ACTIVE',
  updated_at  TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
                                  ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (server_id)
) ENGINE=InnoDB;

-- El mapa que hace toda la magia: cada uno de los 8192 shards virtuales
-- apunta a UN servidor f&#237;sico y a UNA base de datos dentro de &#233;l.
CREATE TABLE shard_map (
  virtual_shard SMALLINT UNSIGNED NOT NULL,     -- 0 .. 8191
  server_id     VARCHAR(32) NOT NULL,
  db_name       VARCHAR(64) NOT NULL,           -- 'shard6981'
  updated_at    TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
                                  ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (virtual_shard),
  KEY idx_server (server_id),
  CONSTRAINT fk_shard_server
    FOREIGN KEY (server_id) REFERENCES physical_server(server_id)
) ENGINE=InnoDB;

-- Un contador de versi&#243;n global: se incrementa en CADA reshard.
-- La app lo consulta cada pocos segundos; si cambi&#243;, recarga el mapa.
CREATE TABLE topology_version (
  id      TINYINT NOT NULL DEFAULT 1,
  version BIGINT UNSIGNED NOT NULL DEFAULT 1,
  PRIMARY KEY (id),
  CHECK (id = 1)
) ENGINE=InnoDB;
INSERT INTO topology_version (id, version) VALUES (1, 1);</code></pre></div><p><code>topology_version</code> es el mecanismo de invalidaci&#243;n de cach&#233;. La app cachea el mapa en memoria; para saber cu&#225;ndo recargar, solo hace la query de abajo (una query simple cada pocos segundos) en vez de recargar las 8192 filas constantemente.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;077756c1-82bb-4e64-909f-c852fb973fd6&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">SELECT version FROM topology_version</code></pre></div><p>A nivel de aplicaci&#243;n este ser&#225; el flujo: </p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;41daa321-5954-4ca9-a479-a38e39ad0286&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">user_id  --(% 8192)--&gt;  virtual_shard  --(shard_map)--&gt;  (server_id, db_name)
                                              |
                              server_id --&gt; HikariDataSource (pool cacheado)
                                              |
                              getConnection() + setCatalog(db_name)</code></pre></div><p>La pieza fundamental es el ShardRouter, dado un identificador es capaz de devolver la conexi&#243;n a usar para acceder a los datos requeridos: </p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;7cf72070-8c90-4593-bc45-97d63d1907b0&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">@Component
public class ShardRouter {

    public static final int NUM_VIRTUAL_SHARDS = 8192;

    private final TopologyRepository topology;
    private final ShardProperties props;

    // UN pool por servidor f&#237;sico. serverId -&gt; DataSource.
    private final Map&lt;String, HikariDataSource&gt; pools = new ConcurrentHashMap&lt;&gt;();
    // El mapa cacheado: virtual_shard -&gt; mapping.  Se reemplaza at&#243;mico al recargar.
    private volatile Map&lt;Integer, ShardMapping&gt; shardMap = Map.of();
    private volatile long loadedVersion = -1;

    public ShardRouter(TopologyRepository topology, ShardProperties props) {
        this.topology = topology;
        this.props = props;
    }

    @PostConstruct
    public void init() { reload(); }

    /** Paso 1: user_id -&gt; shard virtual. Determinista, nunca cambia. */
    public int virtualShard(long userId) {
        return (int) (Long.remainderUnsigned(userId, NUM_VIRTUAL_SHARDS));
    }

    /** Ruteo por objeto: el shard viaja embebido en los bits altos del id. */
    public int virtualShardOfObject(long objectId) {
        return (int) ((objectId &gt;&gt;&gt; 46) &amp; 0xFFFF);
    }

    /**
     * Paso 2 y 3: shard virtual -&gt; (servidor, db) -&gt; conexi&#243;n ya posicionada.
     * El caller usa la Connection y la cierra (vuelve al pool).
     */
    public Connection connectionForUser(long userId) throws SQLException {
        return connectionForShard(virtualShard(userId));
    }

    public Connection connectionForObject(long objectId) throws SQLException {
        return connectionForShard(virtualShardOfObject(objectId));
    }

    private Connection connectionForShard(int vshard) throws SQLException {
        ShardMapping m = shardMap.get(vshard);
        if (m == null) throw new IllegalStateException("Shard sin mapear: " + vshard);

        HikariDataSource pool = pools.get(m.serverId());
        if (pool == null) throw new IllegalStateException("Sin pool: " + m.serverId());

        Connection c = pool.getConnection();
        c.setCatalog(m.dbName());   // &lt;-- el "USE shardNNNN": salta a la base del shard
        return c;
    }

    /** Recarga mapa y pools si la versi&#243;n de topolog&#237;a cambi&#243; (reshard). */
    @Scheduled(fixedDelayString = "${sharding.refresh-interval-ms}")
    public void refreshIfChanged() {
        long v = topology.currentVersion();
        if (v != loadedVersion) reload();
    }

    private synchronized void reload() {
        // 1. Asegura un pool por cada servidor activo.
        for (PhysicalServer s : topology.loadServers()) {
            pools.computeIfAbsent(s.serverId(), id -&gt; buildPool(s));
        }
        // 2. Reemplaza el mapa de shards de forma at&#243;mica.
        Map&lt;Integer, ShardMapping&gt; fresh = topology.loadShardMap().stream()
            .collect(Collectors.toMap(ShardMapping::virtualShard, m -&gt; m));
        this.shardMap = fresh;
        this.loadedVersion = topology.currentVersion();
        // (opcional) cerrar y quitar pools de servidores ya sin shards asignados.
    }

    private HikariDataSource buildPool(PhysicalServer s) {
        HikariConfig cfg = new HikariConfig();
        // Sin base por defecto en la URL: elegimos la BD por conexi&#243;n con setCatalog.
        cfg.setJdbcUrl(String.format(
            "jdbc:mysql://%s:%d/?rewriteBatchedStatements=true", s.host(), s.port()));
        cfg.setUsername(props.getShardUser());
        cfg.setPassword(props.getShardPass());
        cfg.setMaximumPoolSize(props.getPoolSizePerServer());
        cfg.setPoolName("shardpool-" + s.serverId());
        return new HikariDataSource(cfg);
    }
}</code></pre></div><p>Ejemplo de uso del Router en una app Spring Boot.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;91f3e56d-20bb-43b9-8fd9-8fde511674cc&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">@Repository
public class PinRepository {
    private final ShardRouter router;
    private final IdGenerators ids;   // un IdGenerator por shard virtual

    public PinRepository(ShardRouter router, IdGenerators ids) {
        this.router = router; this.ids = ids;
    }

    public long create(long userId, long boardId, String title) {
        int vshard = router.virtualShard(userId);
        long pinId = ids.forShard(vshard).nextId(); // shard embebido en el id
        try (Connection c = router.connectionForUser(userId);
             PreparedStatement ps = c.prepareStatement(
                 "INSERT INTO pins(local_id, user_id, board_id, title, created_at) " +
                 "VALUES (?,?,?,?, NOW())")) {
            ps.setLong(1, pinId); ps.setLong(2, userId);
            ps.setLong(3, boardId); ps.setString(4, title);
            ps.executeUpdate();
            return pinId;
        } catch (SQLException e) {
            throw new DataAccessResourceFailureException("insert pin", e);
        }
    }

    //.....
}</code></pre></div><p>En este ejemplo se usa Connection para ejecutar consultas nativas directamente, pero rompe con la integraci&#243;n en Spring con @Transactional, JPA etc; estos esperan un DataSource. Existe otra forma que es usando un ThreadLocal en el contexto que contenga un DataSource, para esto hay que settearlo a nivel de Thread para que pueda ser usado. En otro momento explicaremos mejor este approach (ya es un poco largo este art&#237;culo).</p><p>Espero te haya servido lo explicado ac&#225;, es realmente un lindo patr&#243;n, pero implementarlo es realmente complejo.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Suscribirse&quot;,&quot;language&quot;:&quot;es&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">&#161;Gracias por leer SACAViX's Substack! Suscr&#237;bete gratis para recibir nuevos posts y apoyar mi trabajo.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Escribe tu correo electr&#243;nico..." tabindex="-1"><input type="submit" class="button primary" value="Suscribirse"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Procesamiento de tarjetas de crédito a gran escala: ¿Cómo los sistemas distribuidos evitan cargos duplicados a miles de TPS?]]></title><description><![CDATA[Realizar una compra por internet es un proceso complejo, evitar pagos duplicados es escencial para la confianza, las grandes empresas aplican idempotencia y bloqueo distribuido.]]></description><link>https://sacavix.substack.com/p/procesamiento-de-tarjetas-de-credito</link><guid isPermaLink="false">https://sacavix.substack.com/p/procesamiento-de-tarjetas-de-credito</guid><dc:creator><![CDATA[Yoandy]]></dc:creator><pubDate>Mon, 01 Jun 2026 11:04:17 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!m-q2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f013b9e-e31e-4c87-9348-2f0ecf31c1e0_868x635.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Una vez conversando con un directivo senior del sector fintech me dijo una frase que se me qued&#243; grabada: &#8220;Nada es peor para una empresa que procesa pagos que cobrar dos veces a un mismo cliente por una misma orden&#8220;; en ese momento me puse a pensar el enojo que me dar&#237;a si fuera a comprar algo por internet y me cobraran dos veces o m&#225;s por un mismo producto o servicio. Evitar cargos duplicados es un elemento fundamental para la confianza.</p><p>Seg&#250;n datos, Visa proces&#243; durante 2023 un promedio de 250 millones de transacciones por d&#237;a, en fechas especiales el pico de tr&#225;fico super&#243; los 24000 TPS (Transacciones por segundo).  El problema de evitar pagos duplicados no es solo responsabilidad de Visa y Master como card brands/schemes, es responsabilidad de toda la cadena de sistemas que procesan el pago, desde el comerciante hasta el banco emisor.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Suscribirse&quot;,&quot;language&quot;:&quot;es&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">&#161;Gracias por leer SACAViX's Substack! Suscr&#237;bete gratis para recibir nuevos posts y apoyar mi trabajo.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Escribe tu correo electr&#243;nico..." tabindex="-1"><input type="submit" class="button primary" value="Suscribirse"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Manejar altos volumenes de pago lleva a las empresas de procesamiento a manejar arquitecturas distribuidas; eso implica a menudo m&#250;ltiples aplicaciones que se despliegan y escalan de forma independiente, sistemas de bases de datos donde proiedades como ACID ya no son toleradas, procesamiento asincrono, etc. </p><p>Las arquitecturas distribuidas traen consigo un reto inherente que es el manejo de la transaccionalidad, pasamos a los problemas de consistencia eventual, que son potencialmente m&#225;s dificiles de manejar. </p><p>En t&#233;rminos pr&#225;cticos un monolito o una base de datos SQL &#250;nica no tienen la capacidad de escalar verticalmente para manejar altas tasas transaccionales, por ello es necesario el entorno distribuido.</p><h2>Idempotencia: El coraz&#243;n para evitar duplicidad</h2><p>Para resolver pagos duplicados, el elemento fundamental de toda entidad de procesamiento de pagos es garantizar aplicar el concepto de idempotencia al momento de recibir un pago.</p><p>Un operaci&#243;n se considera idempotente si cumple que al aplicarla N veces siempre se produce el mismo resultado. En matem&#225;ticas: <code>f(f(x)) = f(x)</code>. En sistemas de pagos: procesar la misma solicitud 10 veces debe cobrarle al cliente exactamente una vez.</p><p>Para que esto funcione los sistemas que procesan los pagos deben recibir un ID &#250;nico junto con la solicitud del cliente, mientras una solicitud con ese ID se este procesando NO podr&#225; procesarse otra con el mismo ID, luego de que la operaci&#243;n se procesa se almacena el resultado del procesamiento del pago junto con ese ID. Si se intenta nuevamente otro pago con ese mismo ID, entonces no se procesa nuevamente y simplemente se devuelve el resultado que previamente se guard&#243;. Decirlo como se menciona ac&#225; es simple, pero hacerlo en un sistema distribuido requiere de expertise t&#233;cnico del equipo de desarrollo de la soluci&#243;n. </p><h3>Arquitectura &#8220;est&#225;ndar&#8220; de un sistema de procesamiento de pagos</h3><p>El siguiente gr&#225;fico representa en t&#233;rminos generales la arquitectura de un sistema de procesamiento de pagos:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!4jwb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a744452-dffb-41db-901d-e979d6a4ba38_832x1001.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!4jwb!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a744452-dffb-41db-901d-e979d6a4ba38_832x1001.png 424w, /__u/substackcdn.com/image/fetch/$s_!4jwb!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a744452-dffb-41db-901d-e979d6a4ba38_832x1001.png 848w, /__u/substackcdn.com/image/fetch/$s_!4jwb!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a744452-dffb-41db-901d-e979d6a4ba38_832x1001.png 1272w, /__u/substackcdn.com/image/fetch/$s_!4jwb!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a744452-dffb-41db-901d-e979d6a4ba38_832x1001.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!4jwb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a744452-dffb-41db-901d-e979d6a4ba38_832x1001.png" width="832" height="1001" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3a744452-dffb-41db-901d-e979d6a4ba38_832x1001.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1001,&quot;width&quot;:832,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:79908,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sacavix.substack.com/i/200013145?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a744452-dffb-41db-901d-e979d6a4ba38_832x1001.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!4jwb!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a744452-dffb-41db-901d-e979d6a4ba38_832x1001.png 424w, /__u/substackcdn.com/image/fetch/$s_!4jwb!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a744452-dffb-41db-901d-e979d6a4ba38_832x1001.png 848w, /__u/substackcdn.com/image/fetch/$s_!4jwb!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a744452-dffb-41db-901d-e979d6a4ba38_832x1001.png 1272w, /__u/substackcdn.com/image/fetch/$s_!4jwb!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a744452-dffb-41db-901d-e979d6a4ba38_832x1001.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>Los clientes o comerciantes del sistema de procesamiento de pagos realizan solicitudes para crear pagos con tarjetas, el procesador de pagos recibe esa solicitud, luego de los controles de seguridad, acceso, tokenizaci&#243;n, etc en el API Gateway la enruta hacia el sistema de procesa los pagos con tarjetas, este interactuar&#225; con sistemas externos para ejecutar el pago en s&#237; y guardar&#225; en su base de datos la informaci&#243;n del resultado del procesamiento.</p><p>Si el cliente env&#237;a un ID junto con su solicitud el Card Processor deber&#225; guardar en el StorageDB el resultado de la transacci&#243;n junto con el ID. Si se intenta procesar otro pago con ese mismo ID entonces el Card Processor no ir&#225; al sistema externo, simplemente devuelve el resultado guardado previamente. Parece simple, pero este dise&#241;o tiene problemas que no lo hacen idempotente y por lo tanto poco confiable.</p><p>Situaciones que demuestran fragilidad de este dise&#241;o:</p><ol><li><p>El cliente env&#237;a por error con diferencia de apenas unos milisegundos la solicitud para la misma orden (con el mismo ID), pero es tan r&#225;pido que la solicitud 1 a&#250;n no se ha procesado. En estos sistemas es usual usar escalabilidad horizontal y balanceadores de carga (con Round Robbin), por lo tanto Card Processor tiene m&#250;ltiples instancias, el request 1 entr&#243; por Card Processor 1 y el request 2 entr&#243; por Card Processor 3. C&#243;mo a&#250;n no se ha comenzado a procesar en 1 entonces cuando llega a 3 este no tiene conocimiento e inicia su procesamiento, cayendo en la ejecuci&#243;n de un pago duplicado.</p></li><li><p>Otro escenario que demuestra fragilidad en este dise&#241;o es que puede tener lugar un error transitorio de red en la comunicaci&#243;n entre el API Gateway y el Card Processor al enviar una solictud, como API Gateway no recibe respuesta entonces realiza un retry siguiendo una estrategia de resiliencia, sin embargo el request 1 si hab&#237;a llegado pero nunca supo, en este caso caemos en un escenario similar al descrito en el punto 1.</p></li><li><p>Otros escenarios adicionales son: El StorageDB no maneja consistencia inmediata (Recomiendo leer sobre Teorema <a href="/__u/sacavix.substack.com/p/ep02-pacelc-en-los-sistemas-de-bases">CAP/PACELC</a>), se va a procesar el pago al sistema externo antes de peristirlo como &#8220;en curso&#8220; en el StorageDB, etc.</p></li></ol><p>Estos escenarios demuestran que nuestro sistema no es del todo idempotente. </p><h3>Exlusi&#243;n mututa distribuida </h3><p>Si el sistema anterior fuera un monolito usariamos un mecanismo de exclusi&#243;n mutua local (mutex), en Java algo como esto: </p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;ded3dc38-a840-4500-9f9b-5c5509073e17&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier;

public class IdempotencyLocker {

    // Concurrent map to store a dynamic lock per each idempotency key
    private final ConcurrentHashMap&lt;String, ReentrantLock&gt; locks = new ConcurrentHashMap&lt;&gt;();

    /**
     * Executes a critical section by ensuring mutual exclusion based on an idempotency key.
     *
     * @param key    The unique transaction key (e.g., request UUID from HTTP headers)
     * @param action The business logic or code block to be executed exclusively
     * @param &lt;T&gt;    The return type of the action
     * @return The result produced by the action
     */
    public &lt;T&gt; T executeWithLock(String key, Supplier&lt;T&gt; action) {
        if (key == null) {
            throw new IllegalArgumentException("Idempotency key cannot be null");
        }

        // Atomically retrieves the existing lock or maps a new one if it's absent
        ReentrantLock lock = locks.computeIfAbsent(key, k -&gt; new ReentrantLock());

        // Attempts to acquire the mutual exclusion lock
        lock.lock();
        try {
            // Execute the critical section (e.g., database verification and API third-party calls)
            return action.get();
        } finally {
            // Crucial: always release the lock inside the finally block to avoid deadlocks
            lock.unlock();
            
            // Memory leak mitigation: remove the key from the map 
            // only if no other threads are currently waiting in the queue for this lock
            if (!lock.hasQueuedThreads()) {
                locks.remove(key, lock);
            }
        }
    }
}</code></pre></div><p>Cuando una transacci&#243;n llega lo primero que hacemos es generar un &#8220;bloqueo&#8220;, b&#225;sicamente ponemos un candado por esa clave de idempotencia y comenzamos a procesar la transacci&#243;n (Creamos el pago en el StorageDB, vamos al procesador, actualizamos el resultado de la respuesta), cuando terminamos de procesar &#8220;liberamos&#8220; el bloqueo, b&#225;sicamente el candado se abre. Si un segundo intento mientra alguna transacci&#243;n tiene el bloqueo entonces se pondr&#225; en espera hasta que termine la que esta en curso. Una vez termine la que est&#225; en curso esa se inicia a ejecutar, pero como ya esta previamente guardada en la base de datos el Card Processor no lo mandar&#225; al sistema externo, simplemente devolver&#225; el resultado que hay en la base de datos para el pago asociado a esa clave de idempotencia.</p><p>La explicaci&#243;n y el ejemplo de c&#243;digo anterior son para cuando se usa exclusi&#243;n mutua local, en sistemas como el de nuestro gr&#225;fico anterior no alcanza con eso, hay que ir a un esquema de exclusi&#243;n mutua distribuida, para ellos la soluci&#243;n m&#225;s popular es usar Redis aplicando el patr&#243;n de microservicios llamado Distributed Lock, la implementaci&#243;n ser&#237;a as&#237;:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;9f975e2d-bec7-41cb-902c-e1ea2799f66d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

public class DistributedIdempotencyLocker {

    private final RedissonClient redissonClient;
    private static final String LOCK_KEY_PREFIX = "lock:idempotency:";

    public DistributedIdempotencyLocker(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    /**
     * Executes a critical section by queuing concurrent requests with the same key.
     *
     * @param key    The unique transaction key (e.g., UUID from HTTP headers)
     * @param action The business logic or code block to be executed exclusively
     * @param &lt;T&gt;    The return type of the action
     * @return The result produced by the action
     */
    public &lt;T&gt; T executeWithLock(String key, Supplier&lt;T&gt; action) {
        if (key == null) {
            throw new IllegalArgumentException("Idempotency key cannot be null");
        }

        String redisLockKey = LOCK_KEY_PREFIX + key;
        RLock lock = redissonClient.getLock(redisLockKey);

        boolean isAcquired = false;
        try {
            // PARAMETERS:
            // waitTime = 15s -&gt; Concurrent requests will now QUEUE and WAIT up to 15 seconds.
            // leaseTime = 30s -&gt; Safety lease time in Redis if the instance completely crashes.
            isAcquired = lock.tryLock(15, 30, TimeUnit.SECONDS);

            if (!isAcquired) {
                // This only triggers if the queue wait time exceeds 15 seconds (e.g., a massive timeout downstream)
                throw new IllegalStateException("Lock timeout exceeded for key: " + key + ". Queue wait time expired.");
            }

            // Execute the critical section (e.g., check DB -&gt; call API -&gt; save DB)
            return action.get();

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("Thread interrupted while waiting in Redis queue", e);
        } finally {
            // Only release if this specific thread holds the lock active
            if (isAcquired &amp;&amp; lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
}</code></pre></div><p>La explicaci&#243;n de como funciona es exactamente la misma que para la exclusi&#243;n mutua local, la diferencia es que ahora el Mapa de claves de idempotencia radica en el Redis y todos los nodos de Card Processor ven el mismo valor, de esta forma no importa si el request entra por el Card Processor 1 o por el 3.</p><h4>La consistencia de la base de datos</h4><p>Este es un punto importante que tiene un efecto directo en las transacciones idempotentes, es importante que la fuente de datos que almacena la clave de idempotencia y el resultado tengo consistencia fuerte, si la consistencia es eventual se corre el riesgo de que Redis libere el bloqueo, se propague a procesar un request con la misma clave de idempotencia previamente encolado y al intentar leer de una replica del StorageDB el dato a&#250;n no se encuentre sincronizado. </p><p>El &#233;xito de los sistemas idempotentes est&#225; en un potente sistema de exclusi&#243;n mutua + una capa de persistencia con consistencia inmediata.</p><p>El esquema general de funcionamiento es de la siguiente manera.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!m-q2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f013b9e-e31e-4c87-9348-2f0ecf31c1e0_868x635.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!m-q2!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f013b9e-e31e-4c87-9348-2f0ecf31c1e0_868x635.png 424w, /__u/substackcdn.com/image/fetch/$s_!m-q2!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f013b9e-e31e-4c87-9348-2f0ecf31c1e0_868x635.png 848w, /__u/substackcdn.com/image/fetch/$s_!m-q2!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f013b9e-e31e-4c87-9348-2f0ecf31c1e0_868x635.png 1272w, /__u/substackcdn.com/image/fetch/$s_!m-q2!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f013b9e-e31e-4c87-9348-2f0ecf31c1e0_868x635.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!m-q2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f013b9e-e31e-4c87-9348-2f0ecf31c1e0_868x635.png" width="868" height="635" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2f013b9e-e31e-4c87-9348-2f0ecf31c1e0_868x635.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:635,&quot;width&quot;:868,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:73640,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sacavix.substack.com/i/200013145?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f013b9e-e31e-4c87-9348-2f0ecf31c1e0_868x635.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!m-q2!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f013b9e-e31e-4c87-9348-2f0ecf31c1e0_868x635.png 424w, /__u/substackcdn.com/image/fetch/$s_!m-q2!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f013b9e-e31e-4c87-9348-2f0ecf31c1e0_868x635.png 848w, /__u/substackcdn.com/image/fetch/$s_!m-q2!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f013b9e-e31e-4c87-9348-2f0ecf31c1e0_868x635.png 1272w, /__u/substackcdn.com/image/fetch/$s_!m-q2!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f013b9e-e31e-4c87-9348-2f0ecf31c1e0_868x635.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Pasos: </p><ol><li><p>El usuario/cliente env&#237;a una solicitud para crear un pago, como header env&#237;a el Idempotency Key.</p></li><li><p>Una instancia de Card Processor recibe la solicitud y solicita un bloqueo exclusivo en Redis.</p></li><li><p>Redis entrega la exclusividad de procesar el request asociado a es clave de idempotencia.</p></li><li><p>El Card Processor guarda en el StorageDB la clave de idempotencia junto a los datos del pago y un estado, por ejemplo: &#8220;Running&#8220;.</p></li><li><p>El Card Processor llama a los sistemas externos necesarios.</p></li><li><p>El Card Processor guarda el estado final en la base de datos.</p></li><li><p>El Card Processor solicita liberar la clave en el Redis, perdiendo la condici&#243;n de exclusividad.</p></li></ol><h2>Casos marginales de error</h2><p>Si bien esta es una arquitectura bien s&#243;lida para manejar idempotencia en sistemas distribuidos, existe un punto al menos donde aunque con muy baja probabilidad pueden darse ciertas condiciones que propicien un error en la adquisici&#243;n del bloqueo exclusivo y junto a ella una posibilidad de ejecuci&#243;n de pago duplicado: Redis.</p><p>Si bien Redis es un sistema extremadamente probado no dejar de ser un sistema que puede fallar, la arquitectura m&#225;s extendida que se usa es tener Redis en un esquema Master/Slave, donde los datos se escriben sobre el maestro y pueden leerse desde el maestro o de la(s) replica. En este sentido pueden fallar dos cosas 1) la replicaci&#243;n de Redis es async, puede que se intente leer un dato de la r&#233;plica y aun no est&#233; disponible y 2) el primario puede tener un failover y perder datos no replicados.</p><p>Algoritmos m&#225;s potentes, pero con otra complejidad como Raft puede solucionar esto, en otro art&#237;culo abordaremos sobre este tema.</p><p>Gracias por leer hasta ac&#225;.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Suscribirse&quot;,&quot;language&quot;:&quot;es&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">&#161;Gracias por leer SACAViX's Substack! Suscr&#237;bete gratis para recibir nuevos posts y apoyar mi trabajo.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Escribe tu correo electr&#243;nico..." tabindex="-1"><input type="submit" class="button primary" value="Suscribirse"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Los 7 asesinos de la arquitectura microservicios]]></title><description><![CDATA[Exploramos los antipatrones que arruinan la implementaci&#243;n de una arquitectura microservicios]]></description><link>https://sacavix.substack.com/p/los-7-asesinos-de-la-arquitectura</link><guid isPermaLink="false">https://sacavix.substack.com/p/los-7-asesinos-de-la-arquitectura</guid><dc:creator><![CDATA[Yoandy]]></dc:creator><pubDate>Sun, 29 Mar 2026 20:20:53 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!jDSg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e2b2bbb-c536-4081-9e66-ba1c9c7ff3fc_1057x736.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>La promesa de los microservicios siempre es seductora: escalabilidad infinita, despliegues independientes, equipos separados, mejor gobernabilidad, etc . Sin embargo, para muchos equipos, esta promesa se convierte en un problema operativo. No es que el estilo arquitect&#243;nico falle, sino que la forma en como se implementa act&#250;a como un asesino silencioso que erosiona los beneficios hasta que el sistema es m&#225;s fr&#225;gil que el monolito que intentaba reemplazar. </p><p>En muchos escenarios estos problemas operativos no salen el primer d&#237;a, sino con el tiempo y se manifiestan mediante fallos operativos, aumento de latencia, dificultades de coordinaci&#243;n entre equipos, fallos en un componente que terminan generando problemas en cascada, etc. En la mayor&#237;a de lo escenarios esto se debe a la implementaci&#243;n de malas pr&#225;cticas a lo ancho de la plataforma y no a un componente en particular.</p><h2>&#191;Qu&#233; problema resuelven los microservicios?</h2><p>Para entender mejor esto, es importante primero revisar que problema resuelven los microservicios.</p><p>La mejor definici&#243;n en mi criterio sobre el concepto de este estilo la ofrece <a href="https://martinfowler.com/articles/microservices.html">Martin Fowler</a>. Simplifico en el siguiente p&#225;rrafo:</p><blockquote><p>Es un estilo arquitect&#243;nico que estructura una aplicaci&#243;n como un <strong>conjunto de servicios peque&#241;os, aut&#243;nomos y acoplados de forma flexible</strong>, organizados en torno a capacidades de negocio y que se comunican unos con otros usando a menudo mecanismos ligeros de comunicaci&#243;n.</p></blockquote><p>Este estilo viene a resolver los siguientes problemas ofreciendo las ventajas que se ilustran a continuaci&#243;n.</p><ul><li><p><strong>Escalabilidad independiente:</strong> Permite escalar solo los servicios que lo necesitan, sin afectar toda la aplicaci&#243;n.</p></li><li><p><strong>Despliegues m&#225;s r&#225;pidos y seguros:</strong> Cada servicio se puede actualizar sin redeployar todo el sistema, reduciendo riesgos. Mejora significativa del <strong>Time-to-Market.</strong></p></li><li><p><strong>Mejor mantenimiento del c&#243;digo:</strong> Divide aplicaciones grandes en partes peque&#241;as, m&#225;s f&#225;ciles de entender y modificar. </p></li><li><p><strong>Equipos aut&#243;nomos:</strong> Diferentes equipos pueden trabajar en distintos servicios sin bloquearse entre s&#237;.</p></li><li><p><strong>Aislamiento de fallos:</strong> Un error en un servicio no necesariamente impacta a todo el sistema.</p></li><li><p><strong>Flexibilidad tecnol&#243;gica:</strong> Cada servicio puede usar el lenguaje o tecnolog&#237;a m&#225;s adecuada para su problema.</p></li><li><p><strong>Evoluci&#243;n &#225;gil del negocio:</strong> Permite implementar cambios r&#225;pidamente sin afectar otras &#225;reas del sistema.</p></li><li><p><strong>Mejor modelado del dominio:</strong> Facilita aplicar conceptos como Domain-Driven Design, alineando servicios con capacidades de negocio.</p></li><li><p><strong>Gesti&#243;n de la complejidad:</strong> Divide sistemas grandes en componentes m&#225;s manejables.</p></li><li><p><strong>Optimizaci&#243;n de recursos:</strong> Permite usar infraestructura de forma m&#225;s eficiente seg&#250;n la necesidad de cada servicio. (Aunque es mucho mas costoso a nivel general).</p></li></ul><p>Todos estos puntos suenan geniales, pero para poder lograrlos hay que evitar ciertas pr&#225;cticas que ya son conocidas en el mercado como los antipatrones de la arquitectura microservicios.</p><h2>Los asesinos de los microservicios</h2><p>A continuaci&#243;n se listan los 7 asesinos fundametales de la arquitectura microservicios.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!jDSg!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e2b2bbb-c536-4081-9e66-ba1c9c7ff3fc_1057x736.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!jDSg!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e2b2bbb-c536-4081-9e66-ba1c9c7ff3fc_1057x736.png 424w, /__u/substackcdn.com/image/fetch/$s_!jDSg!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e2b2bbb-c536-4081-9e66-ba1c9c7ff3fc_1057x736.png 848w, /__u/substackcdn.com/image/fetch/$s_!jDSg!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e2b2bbb-c536-4081-9e66-ba1c9c7ff3fc_1057x736.png 1272w, /__u/substackcdn.com/image/fetch/$s_!jDSg!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e2b2bbb-c536-4081-9e66-ba1c9c7ff3fc_1057x736.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!jDSg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e2b2bbb-c536-4081-9e66-ba1c9c7ff3fc_1057x736.png" width="1057" height="736" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7e2b2bbb-c536-4081-9e66-ba1c9c7ff3fc_1057x736.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:736,&quot;width&quot;:1057,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:255582,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sacavix.substack.com/i/192521632?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e2b2bbb-c536-4081-9e66-ba1c9c7ff3fc_1057x736.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!jDSg!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e2b2bbb-c536-4081-9e66-ba1c9c7ff3fc_1057x736.png 424w, /__u/substackcdn.com/image/fetch/$s_!jDSg!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e2b2bbb-c536-4081-9e66-ba1c9c7ff3fc_1057x736.png 848w, /__u/substackcdn.com/image/fetch/$s_!jDSg!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e2b2bbb-c536-4081-9e66-ba1c9c7ff3fc_1057x736.png 1272w, /__u/substackcdn.com/image/fetch/$s_!jDSg!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e2b2bbb-c536-4081-9e66-ba1c9c7ff3fc_1057x736.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p> </p><p>Si te sientes con suerte o quieres probar que tanto sabes de antipatrones de microservicios te invito a que visites <a href="https://learn.sacavix.com/">SACAViX Learn</a>, donde tenemos un reto especifico de 12 preguntas sobre antipatrones, es un sencillo quiz que te ayudar&#225; a verificar tus conocimientos en esta materia.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://app.learn.sacavix.com/quiz/907838&quot;,&quot;text&quot;:&quot;Hacer el quiz&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://app.learn.sacavix.com/quiz/907838"><span>Hacer el quiz</span></a></p><h4>Problema 1: La base de datos compartida (Shared database)</h4><p>Es quiz&#225;s el asesino m&#225;s com&#250;n. Ocurre cuando m&#250;ltiples servicios leen y escriben en el mismo esquema de base de datos.</p><ul><li><p>Elimina la autonom&#237;a: Si el Servicio A cambia el nombre de una columna, rompe al Servicio B. </p></li><li><p>Se pierda la libertad de elegir el motor de base de datos adecuado para cada dominio. </p></li><li><p>Se pierde el ownership sobre los datos; al final al tener m&#250;ltiples sistemas y probablemente varios equipos termina siendo poco claro quien es el due&#241;o de cada dato.</p></li><li><p>Limita la escalabilidad o al hacerla se corre el riesgo de un sistema pueda terminar afectando la base de datos y como consecuencia termine impactando la estabilidad de la plataforma.</p></li></ul><p>El acoplamiento se termina trasladando del c&#243;digo a la capa de datos.</p><h4>Problema 2: Monolito distribuido</h4><p>Este es un cl&#225;sico problema que tiene lugar en algunas implementaciones, ocurre cuando el acoplamiento es tan alto que es imposible aprovechar las ventajas claras de los microservicios relacionadas a la independencia.</p><ul><li><p><strong>Despliegues en cascada:</strong> Se pierde la capacidad de entrega continua real. Si el Servicio A requiere la versi&#243;n exacta del Servicio B para funcionar, el riesgo de error en producci&#243;n aumenta exponencialmente.</p></li><li><p><strong>Complejidad sin beneficio:</strong> Pagas el &#8220;impuesto de red&#8221; (latencia, serializaci&#243;n) sin obtener la agilidad de los microservicios.</p></li><li><p><strong>Fragilidad de runtime:</strong> Si un servicio cae, el sistema entero colapsa porque las dependencias son r&#237;gidas y s&#237;ncronas en lugar de estar desacopladas por eventos.</p></li><li><p><strong>Sobrecarga cognitiva:</strong> Los desarrolladores deben conocer el estado de m&#250;ltiples repositorios y servicios solo para realizar un cambio peque&#241;o en una l&#243;gica de negocio. </p></li></ul><p>Al final el problema de los monolitos termina distribuido entre varios equipos, sin ventaja alguna.</p><h4>Problema 3: Excesiva comunicaci&#243;n entre servicios (Chatty API)</h4><p>Este problema es m&#225;s frecuente en equipos con baja experiencia en la adopci&#243;n de microservicios, cuando un equipo si experiencia comienza a migrar a microservicios es muy com&#250;n convertir &#8220;cualquier cosa&#8220; en un peque&#241;o servicio, esto es extremadamente peligroso y conlleva a una excesiva comunicaci&#243;n entre componentes para resolver una solicitud.</p><ul><li><p><strong>Latencia acumulada:</strong> El tiempo de respuesta total para el usuario es la suma de todas las llamadas internas de red, lo que genera una experiencia de usuario pobre y un apdex general degradado.</p></li><li><p><strong>Consumo ineficiente de recursos:</strong> Cada llamada implica negociaci&#243;n TLS (si existe), headers HTTP, serializaci&#243;n JSON/gRPC, lo que consume CPU y ancho de banda de red innecesariamente. Por otra parte conlleva a tener m&#225;s servicios y por lo tanto una mayor infraestructura para mantener.</p></li><li><p><strong>Mayor superficie de fallo:</strong> A m&#225;s llamadas, m&#225;s probabilidades de que un timeout o un error 500 en un servicio intermedio rompa la transacci&#243;n completa. Aunque esto es mitigable con la aplicaci&#243;n de patrones de estabilidad como timeout-limiters, retries o circuit-breakers se aumenta de forma innecesaria la posibilidad de fallos, siempre hay que tener en cuenta las <a href="https://www.youtube.com/watch?v=SI8x6fQEsaY">falacias de la computaci&#243;n distribuida</a>. </p></li><li><p><strong>Acoplamiento de interfaz:</strong> El cliente (o el servicio llamante) debe conocer demasiados detalles internos de la estructura de otros servicios para armar una respuesta completa.</p></li></ul><p>La comunicaci&#243;n excesiva tiene lugar cuando hay demasiados servicios por una incorrecta delimitaci&#243;n del contexto al momento de dise&#241;ar la arquitectura.</p><h4>Problema 4: El servicio <em>dios</em> (God service)</h4><p>Este problema se evidencia usualmente cuando existe un servicio central, complejo y extremadamente fundamental en la ejecuci&#243;n del proceso de negocio. Es ese servicio que en algunas empresas llaman &#8220;el servicio&#8220; o &#8220;la API&#8220;, posee l&#243;gica compleja, es grande y pocas personas lo tocan o nadie lo quiere tocar, un error en este servicio tiene alto impacto operativo para el negocio. </p><p>Algunos problemas de tener un <em>god</em> service son: </p><ul><li><p><strong>Cuello de botella organizacional:</strong> Se crean colas de espera en los equipos porque todos necesitan modificar el mismo servicio para sus respectivas funcionalidades.</p></li><li><p><strong>Escalado ineficiente:</strong> Te ves obligado a escalar instancias gigantes de un servicio (con mucha RAM/CPU) solo porque una peque&#241;a funcionalidad dentro de &#233;l tiene mucha carga o es tan importante que es mejor, &#8220;por si acaso&#8220; tenerlo escalado.</p></li><li><p><strong>Tiempos de compilaci&#243;n y test largos:</strong> El servicio se vuelve tan pesado que las pruebas unitarias y de integraci&#243;n tardan demasiado, ralentizando el pipeline de CI/CD.</p></li><li><p><strong>Resistencia al cambio:</strong> El c&#243;digo se vuelve &#8220;espagueti&#8221; y dif&#237;cil de refactorizar debido a la enorme cantidad de responsabilidades mezcladas. En ocasiones es mejor &#8220;no tocarlo&#8220;, hacerlo supone un riesgo muy grande.</p></li></ul><p>Tener el servicio <em>dios</em> en ocasiones, durante una etapa de transici&#243;n de descomposici&#243;n del monolito a microservicios es una parte normal de la arquitectura, lo que NO puede ser normal es no tener la perspectiva de saber que es necesario trabajar en su mejora o descomposici&#243;n hacia componentes mas simples adapatados a las capacidades del negocio. </p><h4>Problema 5: Infraestructura compartida</h4><p>Este problema es un elementos m&#225;s general que el problema 1 (base de datos compartida), pero es usual que no solo se comparta la base de datos, en algunas implementaci&#243;n de microservicios tambi&#233;n se comparte otros componentes cuyo efecto es malo para la arquitectura, ejemplos son: Nodos de despliegue (VMs, VPS, etc), sistemas de cache distribuida compartidos, como Redis, etc.</p><p>Los problemas fundamentales de compartir infraestructura son:</p><ul><li><p>Un pico de tr&#225;fico en un microservicio de baja prioridad puede agotar los recursos (CPU, memoria, I/O) y afectar a un servicio cr&#237;tico alojado en el mismo nodo.</p></li><li><p><strong>Punto &#250;nico de fallo (SPOF):</strong> Si el Redis compartido o el cluster de Kubernetes no est&#225; correctamente particionados, una mala configuraci&#243;n en un lugar puede afectar todo el ecosistema.</p></li><li><p><strong>Dificultad en la gesti&#243;n de costos:</strong> Es casi imposible determinar cu&#225;nto dinero consume cada servicio individualmente si todos comparten los mismos recursos sin etiquetas o l&#237;mites claros.</p></li><li><p><strong>Conflictos de configuraci&#243;n:</strong> Limitaciones en variables de entorno o versiones de librer&#237;as a nivel de sistema operativo que afectan a todos los servicios por igual.</p></li></ul><h4>Problema 6: Nanoservicios</h4><p>Es lo opuesto al monolito, tiene lugar cuando en la arquitectura vemos aplicaciones extremadamente simples, por ejemplo: una servicio que realice un c&#225;lculo simple como tomar un monto y calcular el IVA solo sumandole el 21% de su valor, o que su l&#243;gica sea devolver la edad de una persona basado en su fecha de nacimiento, etc. </p><p>Si es demasiado simple o no cubre un contexto delimitado (DDD) es probablemente una mala idea convertirlo en un servicio. Me gusta en lo personal mirar cuantas tablas o entidades en la base de datos tiene que gestionar el servicio, si es una sola, por ejemplo <em>Address</em> de un <em>User</em>, es una red flag para tener en cuenta.</p><p>Nanoservicios implica algunos problemas importantes:</p><ul><li><p><strong>Alta complejidad operativa:</strong> Gestionar 50 servicios para una l&#243;gica simple implica configurar 50 pipelines de CI/CD, 50 monitoreos y 50 esquemas de seguridad.</p></li><li><p><strong>Alta latencia:</strong> Una operaci&#243;n simple de negocio que deber&#237;a tardar 20ms en memoria termina tardando 500ms por los saltos entre servicios (terminamos teniendo Chatty services).</p></li><li><p><strong>Inconsistencia de datos:</strong> Mantener la integridad de los datos entre tantos servicios peque&#241;os obliga a usar transacciones distribuidas (Sagas) para cosas que no deber&#237;an ser tan complejas.</p></li><li><p><strong>Fragmentaci&#243;n del conocimiento:</strong> Es dif&#237;cil entender el flujo completo de una aplicaci&#243;n cuando la l&#243;gica est&#225; repartida en piezas insignificantes de 10 l&#237;neas de c&#243;digo cada una.</p></li></ul><h4>Problema 7: Todo es un servicio</h4><p>Muy relacionado con el problema anterior, es un poco m&#225;s de lo mismo, cada nueva feature que se va a desarrollar se piensa como un microservicio y no como una feature en si o en &#250;ltima instancia, si ser&#225; usado por varios componentes como una posible biblioteca reusable.</p><p>El pensar todo como un servicio termina reuniendo todo lo malo de todos los anteriores antipatrones:</p><ul><li><p>Se generan nanoservicios.</p></li><li><p>Aumentan los costes de mantenimiento/infraestructura.</p></li><li><p>En ocasiones se incurre en duplicidad de c&#243;digo.</p></li><li><p>Se introducen puntos de falla adicionales.</p></li><li><p>Se genera comunicaci&#243;n excesiva (chatty services)</p></li></ul><p>No todo es un servicio, a veces solo hay que organizar un poco y ponerlo en el contexto delimitado correcto.</p><p>Espero que te haya gustado el art&#237;culo, en estos tiempos que solo se habla de AI (incluido este blog) est&#225; bueno revisar un poco sobre arquitectura.</p><p>Ahora que llegaste hasta ac&#225;, &#191;quieres comprobar cuanto aprendiste? Haz el <a href="https://app.learn.sacavix.com/quiz/907838">quiz</a> sobre antipatrones de la categor&#237;a de Arquitectura.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Suscribirse&quot;,&quot;language&quot;:&quot;es&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading SACAViX's Substack! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Escribe tu correo electr&#243;nico..." tabindex="-1"><input type="submit" class="button primary" value="Suscribirse"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[De REST a MCP. ¿Cómo exponer tu API Java para agentes?]]></title><description><![CDATA[Convierte tus endpoints en herramientas MCP para que sean ejecutadas por los LLM]]></description><link>https://sacavix.substack.com/p/de-rest-a-mcp-como-exponer-tu-api</link><guid isPermaLink="false">https://sacavix.substack.com/p/de-rest-a-mcp-como-exponer-tu-api</guid><dc:creator><![CDATA[Yoandy]]></dc:creator><pubDate>Mon, 23 Mar 2026 11:20:49 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!cAwN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facab08ed-d046-40c5-b979-23d7e9e1b964_595x498.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Durante la &#250;ltima d&#233;cada, se han construido millones de <strong>APIs REST</strong> para todo tipo de sectores. Sin embargo, nos encontramos ante un cambio de paradigma: el consumidor de nuestras APIs ya no es solo un frontend de React o una aplicaci&#243;n m&#243;vil, sino un Agente de Inteligencia Artificial.</p><p>Para un LLM, un endpoint REST tradicional es una caja negra que requiere documentaci&#243;n externa (OpenAPI) y una integraci&#243;n manual r&#237;gida. Aqu&#237; es donde surge el <strong>Model Context Protocol (MCP)</strong>. MCP es el est&#225;ndar que permite a los modelos de lenguaje &#8220;ver&#8221; y &#8220;tocar&#8221; la l&#243;gica de negocio de forma nativa, segura y, sobre todo, estandarizada. Es un est&#225;ndar abierto que facilita la conexi&#243;n de asistentes de IA con sistemas donde residen los datos, como repositorios de contenido y herramientas empresariales. Su objetivo es permitir que los modelos de lenguaje accedan a informaci&#243;n relevante y actualizada, mejorando la calidad de sus respuestas. &#8203;(Gracias Anthropic por esto &#129653;)</p><p>En este art&#237;culo, exploraremos c&#243;mo trascender el backend REST. Utilizando el poder de <strong>Spring AI</strong>, explicaremos el proceso para exponer una API REST como un servicio MCP. Veremos c&#243;mo pasar de la exposici&#243;n de datos a la provisi&#243;n de herramientas din&#225;micas que los modelos pueden razonar, invocar y orquestar en tiempo real.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!cAwN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facab08ed-d046-40c5-b979-23d7e9e1b964_595x498.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!cAwN!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facab08ed-d046-40c5-b979-23d7e9e1b964_595x498.png 424w, /__u/substackcdn.com/image/fetch/$s_!cAwN!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facab08ed-d046-40c5-b979-23d7e9e1b964_595x498.png 848w, /__u/substackcdn.com/image/fetch/$s_!cAwN!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facab08ed-d046-40c5-b979-23d7e9e1b964_595x498.png 1272w, /__u/substackcdn.com/image/fetch/$s_!cAwN!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facab08ed-d046-40c5-b979-23d7e9e1b964_595x498.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!cAwN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facab08ed-d046-40c5-b979-23d7e9e1b964_595x498.png" width="595" height="498" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/acab08ed-d046-40c5-b979-23d7e9e1b964_595x498.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:498,&quot;width&quot;:595,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:69134,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://sacavix.substack.com/i/191804394?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facab08ed-d046-40c5-b979-23d7e9e1b964_595x498.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!cAwN!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facab08ed-d046-40c5-b979-23d7e9e1b964_595x498.png 424w, /__u/substackcdn.com/image/fetch/$s_!cAwN!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facab08ed-d046-40c5-b979-23d7e9e1b964_595x498.png 848w, /__u/substackcdn.com/image/fetch/$s_!cAwN!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facab08ed-d046-40c5-b979-23d7e9e1b964_595x498.png 1272w, /__u/substackcdn.com/image/fetch/$s_!cAwN!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Facab08ed-d046-40c5-b979-23d7e9e1b964_595x498.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h2><strong>&#191;Qu&#233; es Spring AI?</strong></h2><p>En en art&#237;culo anterior de esta newsletter hicimos un resumen completo de los principales conceptos que debes conocer si estas empezando en este mundo como desarrollador. Te invito a que lo revises.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/p/de-rest-a-mcp-como-exponer-tu-api?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Compartir&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/sacavix.substack.com/p/de-rest-a-mcp-como-exponer-tu-api?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share"><span>Compartir</span></a></p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;b0b8b3b1-209e-4b2d-8899-2b28ad7919e9&quot;,&quot;caption&quot;:&quot;Desde hace unos 10 d&#237;as comenc&#233; a meterme mucho en esto de la IA, LLMs, RAG, MCP, Embbeding, bases de datos vectoriales y todo lo que rodea este mundo que est&#225; de moda actualmente, pero que luego de probar algunas cosas, estoy m&#225;s que convencido que lleg&#243; para quedarse.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;lg&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Spring AI, un ejemplo con RAG, MCP y OpenAI&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:113566277,&quot;name&quot;:&quot;Yoandy&quot;,&quot;bio&quot;:null,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/20f8b449-52a1-4aea-8881-b4b126da2dd3_1080x1080.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-04-09T12:03:52.618Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F698cded0-a993-48e9-af21-b674c64dc01a_872x346.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://sacavix.substack.com/p/spring-ai-un-ejemplo-con-rag-mcp&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:160884724,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:9,&quot;comment_count&quot;:3,&quot;publication_id&quot;:2325894,&quot;publication_name&quot;:&quot;SACAViX's Substack&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!Ftre!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8c3095c-b9ab-435c-877e-13d25f3e12ac_900x900.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>Spring AI es un <a href="https://spring.io/projects/spring-ai">proyecto</a> del ecosistema Spring dise&#241;ado para facilitar la integraci&#243;n de capacidades de inteligencia artificial (IA) en aplicaciones Java, especialmente aquellas basadas en Spring/Boot. Lo mejor es que proporciona una fachada &#250;nica que permite interactuar con diferentes modelos; usando Spring AI puedes sin cambiar el c&#243;digo de tu aplicaci&#243;n conectarte con Open AI, Amazon Bedrock, Vertex Gemini o un modelo local en Ollama, entre otros. Y no, NO es lo que piensas, NO ES solo un cliente para conecarse a los LLM, es mucho m&#225;s que eso.</p><h2>De un API REST a un MCP Server</h2><p>Toda aplicaci&#243;n creada con Spring que exponga un servicio HTTP Rest puede exponer sus endpoints por MCP, pero cambian algunos conceptos.</p><p>En el mundo REST las aplicaciones exponen endpoints, en la modernidad de MCP se llaman tools, fin de la historia, es todo.</p><p>Entonces, la forma de hacer es crear un &#8220;Controller&#8220; pero que exponga las &#8220;tools&#8220; en lugar de los endpoints.</p><p>Antes de mostrarte como hacerlo con un ejemplo pr&#225;ctico es importante conocer algunas cosas relacionadas con los tipos de transporte que soporta MCP. </p><p></p><ul><li><p><strong>stdio: </strong>Estandar input/output, es el mecanismo m&#225;s b&#225;sico y simple, el servidor MCP se ejecuta localmente y es instanciado por el cliente MCP cuando necesita acceder a sus herramientas.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!MgBW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d64c908-f0cb-452f-9f89-9d120d0ce373_503x481.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!MgBW!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d64c908-f0cb-452f-9f89-9d120d0ce373_503x481.png 424w, /__u/substackcdn.com/image/fetch/$s_!MgBW!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d64c908-f0cb-452f-9f89-9d120d0ce373_503x481.png 848w, /__u/substackcdn.com/image/fetch/$s_!MgBW!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d64c908-f0cb-452f-9f89-9d120d0ce373_503x481.png 1272w, /__u/substackcdn.com/image/fetch/$s_!MgBW!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d64c908-f0cb-452f-9f89-9d120d0ce373_503x481.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!MgBW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d64c908-f0cb-452f-9f89-9d120d0ce373_503x481.png" width="503" height="481" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2d64c908-f0cb-452f-9f89-9d120d0ce373_503x481.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:481,&quot;width&quot;:503,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:29748,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sacavix.substack.com/i/191804394?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d64c908-f0cb-452f-9f89-9d120d0ce373_503x481.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!MgBW!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d64c908-f0cb-452f-9f89-9d120d0ce373_503x481.png 424w, /__u/substackcdn.com/image/fetch/$s_!MgBW!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d64c908-f0cb-452f-9f89-9d120d0ce373_503x481.png 848w, /__u/substackcdn.com/image/fetch/$s_!MgBW!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d64c908-f0cb-452f-9f89-9d120d0ce373_503x481.png 1272w, /__u/substackcdn.com/image/fetch/$s_!MgBW!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2d64c908-f0cb-452f-9f89-9d120d0ce373_503x481.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><ul><li><p><strong>Streamable HTTP:</strong> En el transporte HTTP de flujo continuo (Streamable HTTP), el servidor opera como un proceso independiente que puede gestionar m&#250;ltiples conexiones de clientes. El servidor puede, opcionalmente, hacer uso de <strong>Eventos Enviados por el Servidor (SSE)</strong> para transmitir m&#250;ltiples mensajes del servidor en flujo. Esto permite tanto servidores MCP b&#225;sicos, como servidores con funciones m&#225;s ricas que admiten streaming y notificaciones o peticiones del servidor al cliente.</p></li></ul><p>Aprende m&#225;s sobre como implementar un MCP Server basado en <strong>stdio</strong> en este <a href="/__u/sacavix.substack.com/p/mcp-en-java-inspector-and-vscode">art&#237;culo</a> .</p><h3>Dependencias</h3><p>Una vez explicado los tipos de transporte, vamos a implementar nuestra API REST de ejemplo y exponer endpoints como tools. Usaremos el mecanismo de transporte streamable http.</p><p>Actualmente MCP en Spring esta en la version 2.x, pero aun esta en desarrollo (versiones M). Usaremos la version 1.x. Es importante tener en cuenta que tanto si nuestra API usa servlets o el stack reactivo podemos hacer esto, es independiente.</p><p><strong>Dependencias principales</strong></p><p>La principales dependencias son el starter web (para crear el API REST de toda la vida), y el mcp server webmvc, esto es importante, si estuvieramos usando webflux tendriamos que usar la dependencia correspondiente del servidor mcp.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;xml&quot;,&quot;nodeId&quot;:&quot;c0438217-2219-400e-8b7d-5706dc264d7f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-xml"> &lt;dependency&gt;
   &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
   &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
 &lt;/dependency&gt;

 &lt;dependency&gt;
    &lt;groupId&gt;org.springframework.ai&lt;/groupId&gt;
    &lt;artifactId&gt;spring-ai-starter-mcp-server-webmvc&lt;/artifactId&gt;
 &lt;/dependency&gt;
</code></pre></div><p></p><h3>Controlador REST + Streamable MCP</h3><p>La API REST es la misma de siempre, veamos un ejemplo en el c&#243;digo.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;ca985e4a-9e31-4098-8095-3444bbf784ee&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">@RestController
@RequestMapping("/rates")
public class ExchangeRateController {

    private final ExchangeRateService exchangeRateService;

    public ExchangeRateController(ExchangeRateService exchangeRateService) {
        this.exchangeRateService = exchangeRateService;
    }

    @GetMapping
    public ExchangeRate getExchangeRate(
            @RequestParam("from") String from,
            @RequestParam("to") String to) {
        return this.exchangeRateService.getExchangeRate(from, to);

    }
}</code></pre></div><p>Como bien se auto-explica el c&#243;digo es un servicio que permite haciendo <code>GET /rates</code> obtener la tasa de cambio de una moneda de origen a una de destino.</p><p>Ahora <strong>&#191;C&#243;mo convertirmos esto en una tools expuesta mediante MCP para los LLM puedan ejecutarla</strong>?</p><p>Para ello, en Spring AI es bien simple, esa dependencia que incluimos hace la &#8220;magia&#8220; por detr&#225;s.</p><p>Solo debe anotar apropiadamente los m&#233;todos y listo. Me gusta para mantener el proyecto limpio no &#8220;ensuciar&#8220; los controladores Spring con anotaciones de MCP, para ello prefiero organizar las tools en un paquete llamado &#8220;tools&#8220;, que seria el equivalente a &#8220;controller&#8220; en una arquitectura n-capas.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!KOS5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F904ea6b7-175d-4cee-901b-50f86f12b0f1_413x325.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!KOS5!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F904ea6b7-175d-4cee-901b-50f86f12b0f1_413x325.png 424w, /__u/substackcdn.com/image/fetch/$s_!KOS5!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F904ea6b7-175d-4cee-901b-50f86f12b0f1_413x325.png 848w, /__u/substackcdn.com/image/fetch/$s_!KOS5!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F904ea6b7-175d-4cee-901b-50f86f12b0f1_413x325.png 1272w, /__u/substackcdn.com/image/fetch/$s_!KOS5!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F904ea6b7-175d-4cee-901b-50f86f12b0f1_413x325.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!KOS5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F904ea6b7-175d-4cee-901b-50f86f12b0f1_413x325.png" width="413" height="325" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/904ea6b7-175d-4cee-901b-50f86f12b0f1_413x325.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:325,&quot;width&quot;:413,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:22961,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sacavix.substack.com/i/191804394?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F904ea6b7-175d-4cee-901b-50f86f12b0f1_413x325.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!KOS5!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F904ea6b7-175d-4cee-901b-50f86f12b0f1_413x325.png 424w, /__u/substackcdn.com/image/fetch/$s_!KOS5!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F904ea6b7-175d-4cee-901b-50f86f12b0f1_413x325.png 848w, /__u/substackcdn.com/image/fetch/$s_!KOS5!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F904ea6b7-175d-4cee-901b-50f86f12b0f1_413x325.png 1272w, /__u/substackcdn.com/image/fetch/$s_!KOS5!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F904ea6b7-175d-4cee-901b-50f86f12b0f1_413x325.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>En la carpeta tools, agregamos tantas como necesitemos, podemos seguir una estructura basada en recursos o como deseemos.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;f943ae1e-234e-42cb-9cec-fdeb567cb77f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">import com.yoandypv.exchangerate.apirest2mcp.service.ExchangeRate;
import com.yoandypv.exchangerate.apirest2mcp.service.ExchangeRateService;
import org.springaicommunity.mcp.annotation.McpTool;
import org.springaicommunity.mcp.annotation.McpToolParam;
import org.springframework.stereotype.Component;

@Component
public class ExchangeRateTool {

    private final ExchangeRateService exchangeRateService;

    public ExchangeRateTool(ExchangeRateService exchangeRateService) {
        this.exchangeRateService = exchangeRateService;
    }

    @McpTool(
            name = "getExchangeRate",
            description = "Get the exchange rate between two currencies using ISO codes (e.g., USD to EUR)"
    )
    public ExchangeRate getExchangeRate(
            @McpToolParam(description = "Source currency in ISO format (e.g., USD)")
            String from,

            @McpToolParam(description = "Target currency in ISO format (e.g., USD)")
            String to) {
        return exchangeRateService.getExchangeRate(from, to);
    }
}</code></pre></div><p>Como se observa es un simple componente de Spring que inyecta el bean con la l&#243;gica del servicio (la cual es compartida con el controlador REST).</p><p>Las anotaciones @McpTool y @McpToolParam.</p><ul><li><p>@McpTool: Permite definir la herramienta (&#8220;el endpoint&#8220;), es importante tenga un nombre para que los LLM sepan como invocarlas y una descripci&#243;n que sea clara y exprese exactamente lo que hace la herramienta, asi los LLM saben para que se usan.</p></li><li><p>@McpToolParam: Para anotar los p&#225;rametros, de igual forma debe ser clara para que el LLM sepa que valor debe poner cuando invoque la herramienta. Permite otras opciones como indicar si el p&#225;rametro es mandatorio o no.</p></li></ul><h3>Configuraci&#243;n del servidor MCP</h3><p>Es importante para que el servicio MCP funcione adecuadamente definir la configuraciones adecuadas.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;toml&quot;,&quot;nodeId&quot;:&quot;5c188cde-91ea-40d4-b631-28b1b30ec127&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-toml">spring.ai.mcp.server.name=exchange-rate-mcp
spring.ai.mcp.server.version=0.0.1
spring.ai.mcp.server.type=SYNC
spring.ai.mcp.server.protocol=streamable
spring.ai.mcp.server.stdio=false
</code></pre></div><p>Solo con eso ya es suficiente.</p><p>Un punto adicional es el tema de los CORS, al ser HTTP ten en cuenta que en dependencia del cliente es importante habilitar los CORS. El c&#243;digo del ejemplo brinda acceso a todo, pero sigue las buenas pr&#225;cticas del manejo de CORS si vas a exponer la API. </p><p>Para conectar el cliente MCP como Claude, VisualStudio Code, Cursor o cualquier otro el endpoint ser&#225;: http://localhost:8080/mcp.</p><p>El ejemplo completo esta disponible <a href="https://github.com/yoandypv/api-rest-to-mcp">ac&#225;</a>.</p><p></p>]]></content:encoded></item><item><title><![CDATA[Entendiendo la concurrencia y bloqueos en MySQL con Spring]]></title><description><![CDATA[Aprende como funcionan en MySQL con InnoDB los niveles de aislamiento en entornos de alta concurrencia, el bloqueo optimista y pesimista.]]></description><link>https://sacavix.substack.com/p/entendiendo-la-concurrencia-y-bloqueos</link><guid isPermaLink="false">https://sacavix.substack.com/p/entendiendo-la-concurrencia-y-bloqueos</guid><dc:creator><![CDATA[Yoandy]]></dc:creator><pubDate>Mon, 28 Jul 2025 11:03:30 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!bOY3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b287bc-2766-40a1-b6ea-d826c45b503b_973x667.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>La alta concurrencia de las aplicaciones modernas usualmente conlleva a que m&#250;ltiples usuarios o procesos acceden y modifiquen la misma informaci&#243;n en una base de datos de forma simult&#225;nea, lo que puede dar lugar a problemas cr&#237;ticos como la p&#233;rdida de actualizaciones, lecturas sucias (dirty reads) o inconsistencias de datos. Para garantizar la <strong>integridad</strong> y <strong>consistencia</strong> de la informaci&#243;n, los sistemas de bases de datos implementan mecanismos de <strong>bloqueo</strong>.</p><p>Un mecanismo de <strong>bloqueo</strong> funciona restringiendo el acceso a un recurso de la base de datos (como una fila o una tabla) para evitar que las transacciones simult&#225;neas causen conflictos, como la p&#233;rdida de actualizaciones o lecturas inconsistentes. Al aplicar bloqueos, el sistema de gesti&#243;n de bases de datos asegura que las operaciones se realicen de manera ordenada y que cada transacci&#243;n perciba los datos en un estado consistente y v&#225;lido, respetando las propiedades ACID de las transacciones.</p><p>En este art&#237;culo explicaremos los mecanismo de bloqueo m&#225;s populares usados:</p><ul><li><p>bloqueo optimista</p></li><li><p>bloqueo pesimista (compartido y exclusivo)</p></li></ul><p>La explicaci&#243;n estar&#225; marcada por ejemplos en MySQL/InnoDB y a nivel de aplicaci&#243;n veremos como implementar estos mecanismos de bloqueo usando Spring Boot con JPA.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Suscribirse&quot;,&quot;language&quot;:&quot;es&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading SACAViX's Substack! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Escribe tu correo electr&#243;nico..." tabindex="-1"><input type="submit" class="button primary" value="Suscribirse"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><h2>Conceptos iniciales</h2><ul><li><p>Transacci&#243;n: Es una secuencia de una o m&#225;s operaciones que se ejecutan como una unidad l&#243;gica de trabajo. O se completan todas las operaciones (<code>commit</code>), o se deshacen todas (<code>rollback</code>).</p></li><li><p>ACID: Son un grupo de propiedades fundamentales para entender como las bases de datos (fundamentalmente relacionales como MySQL) garantizan la fiabilidad de los datos; cada letra representa una propiedad:</p><ul><li><p>Atomicidad: Una transacci&#243;n es una unidad indivisible, se ejecuta todo o nada (concepto de transacci&#243;n).</p></li><li><p>Consistencia: Una transacci&#243;n lleva la base de datos de un estado v&#225;lido a otro estado v&#225;lido. Siempre se respetan las reglas y restricciones de la base de datos (por ejemplo, claves for&#225;neas, restricciones UNIQUE, etc.)</p></li><li><p>Isolation (Aislamiento): Las transacciones concurrentes se ejecutan de forma independiente y no interfieren entre s&#237;. El resultado de ejecutar transacciones concurrentemente debe ser el mismo que si se hubieran ejecutado una tras otra. Aqu&#237; es donde el <strong>bloqueo</strong> juega un papel fundamental.</p></li><li><p>Durabilidad:Una vez que una transacci&#243;n se ha confirmado (<code>commit</code>), sus cambios son permanentes y sobreviven a fallos del sistema (apagones, reinicios, etc.)</p></li></ul></li><li><p>Concurrencia: Es la capacidad de que m&#250;ltiples transacciones o usuarios accedan y modifiquen los mismos datos en la base de datos &#8220;al mismo tiempo&#8221;. La concurrencia puede traer problemas que buscar resolver los mecanismos de bloqueo. Los problemas se muestran a continuaci&#243;n:</p><ul><li><p>Lost updates: Cuando dos transacciones intentan actualizar el mismo dato y una de las actualizaciones se sobrescribe sin ser reconocida.</p></li><li><p>Dirty reads: Cuando una transacci&#243;n lee datos que han sido modificados por otra transacci&#243;n, pero que a&#250;n no han sido confirmados (y que podr&#237;an ser rollbackeados)</p></li><li><p>Non-repeatable reads: Cuando una transacci&#243;n lee el mismo dato dos veces y obtiene valores diferentes porque otra transacci&#243;n lo modific&#243; y lo confirm&#243; entre las dos lecturas.</p></li><li><p>Phantom reads: Cuando una transacci&#243;n ejecuta una consulta dos veces y obtiene un conjunto diferente de filas debido a que otra transacci&#243;n insert&#243; o elimin&#243; filas que cumplen los criterios de la consulta.</p></li><li><p>Deadlocks: Una situaci&#243;n en la que dos o m&#225;s transacciones est&#225;n esperando indefinidamente a que las otras liberen un recurso que necesitan para continuar, creando un ciclo de espera.</p></li></ul></li><li><p>Niveles de aislamiento: Definen c&#243;mo se comportan las transacciones concurrentes: qu&#233; pueden ver, qu&#233; pueden modificar y qu&#233; tipos de anomal&#237;as pueden ocurrir o evitarse. MySQL a trav&#233;s del motor InnoDB ofrece 4 niveles de aislamiento:</p><ul><li><p>READ UNCOMMITED: Es el m&#225;s permisivo ofreciendo un menor nivel de aislamento, permite lecturas sucias (dirty reads mencionado anteriormente).</p></li><li><p>READ COMMITED: Solo se puede leer datos confirmados evitando lecturas sucias pero si permite lecturas no repetibles.</p></li><li><p>REPEATABLE READ: Es el por defecto de MySQL, evita lecturas sucias y no repetibles pero pueden tener lugar phantom reads.</p></li><li><p>SERIALIZABLE: Es el m&#225;s restrictivo, funciona como si las transacciones se ejecutaran una tras otra, eliminando los problemas de concurrencia mencionados en el punto anterior, sin embargo este alto nivel aislamiento genera una mayor contenci&#243;n.</p></li></ul></li><li><p>Bloqueo a nivel de tabla: Bloquea toda la tabla de la base de datos, lo que reduce la concurrencia pero es simple de implementar.</p></li><li><p>Bloqueo a nivel de fila: Bloquea solo las filas afectadas, permitiendo una mayor concurrencia. </p></li></ul><h2>Conceptos espec&#237;ficos de bloqueos</h2><p>A continuaci&#243;n se muestran los conceptos espec&#237;ficos de bloqueo optimista y pesimista en base de datos. </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!bOY3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b287bc-2766-40a1-b6ea-d826c45b503b_973x667.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!bOY3!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b287bc-2766-40a1-b6ea-d826c45b503b_973x667.png 424w, /__u/substackcdn.com/image/fetch/$s_!bOY3!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b287bc-2766-40a1-b6ea-d826c45b503b_973x667.png 848w, /__u/substackcdn.com/image/fetch/$s_!bOY3!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b287bc-2766-40a1-b6ea-d826c45b503b_973x667.png 1272w, /__u/substackcdn.com/image/fetch/$s_!bOY3!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b287bc-2766-40a1-b6ea-d826c45b503b_973x667.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!bOY3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b287bc-2766-40a1-b6ea-d826c45b503b_973x667.png" width="540" height="370.174717368962" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/90b287bc-2766-40a1-b6ea-d826c45b503b_973x667.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:667,&quot;width&quot;:973,&quot;resizeWidth&quot;:540,&quot;bytes&quot;:65454,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sacavix.substack.com/i/169345318?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b287bc-2766-40a1-b6ea-d826c45b503b_973x667.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!bOY3!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b287bc-2766-40a1-b6ea-d826c45b503b_973x667.png 424w, /__u/substackcdn.com/image/fetch/$s_!bOY3!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b287bc-2766-40a1-b6ea-d826c45b503b_973x667.png 848w, /__u/substackcdn.com/image/fetch/$s_!bOY3!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b287bc-2766-40a1-b6ea-d826c45b503b_973x667.png 1272w, /__u/substackcdn.com/image/fetch/$s_!bOY3!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F90b287bc-2766-40a1-b6ea-d826c45b503b_973x667.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h3>Bloqueo optimista</h3><p>El bloqueo optimista opera bajo la filosof&#237;a de "esperar lo mejor". No utiliza bloqueos expl&#237;citos durante la lectura de los datos. En su lugar, permite que m&#250;ltiples transacciones accedan a los mismos datos libremente, y <strong>solo verifica si hubo un conflicto (es decir, si el dato fue modificado por otra transacci&#243;n)</strong> justo antes de que la transacci&#243;n actual intente confirmar sus propios cambios. Si se detecta un conflicto, la transacci&#243;n actual se revierte y en algunos casos, si es requerido se reintenta (en el ejemplo de m&#225;s adelante veremos como ejecutar estos reintentos).</p><h3>Bloqueo pesimista</h3><p>Este tipo de bloqueo se basa en la filosof&#237;a de "esperar lo peor". Asume que los conflictos entre transacciones concurrentes son probables y, por lo tanto, previene estos conflictos adquiriendo bloqueos expl&#237;citos sobre los datos antes de que una transacci&#243;n los lea o los modifique. Esto significa que si una transacci&#243;n necesita acceder a un dato, primero debe "reservarlo" (bloquearlo), impidiendo que otras transacciones lo manipulen hasta que el bloqueo sea liberado.</p><p>Para implementar bloque pesimista existe dos posibilidades:</p><ul><li><p>Bloqueo compartido (Shared lock): Permite a m&#250;ltiples transacciones leer el mismo recurso simult&#225;neamente. Pero mientras un recurso (por ejemplo una fila) tiene bloqueo compartido no se podr&#225; adquirir un bloqueo exclusivo.</p></li><li><p>Bloqueo exclusivo (Exclusive lock): Solo permite que una transacci&#243;n tenga acceso al recurso (para escritura) en un momento dado. Nadie m&#225;s puede leer ni escribir el recurso mientras est&#233; bloqueado exclusivamente.</p></li></ul><p>Comparativa &#250;til entre bloqueos:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!xzlv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1e98d57-1266-4a15-8633-4bdfd41b0547_1602x538.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!xzlv!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1e98d57-1266-4a15-8633-4bdfd41b0547_1602x538.png 424w, /__u/substackcdn.com/image/fetch/$s_!xzlv!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1e98d57-1266-4a15-8633-4bdfd41b0547_1602x538.png 848w, /__u/substackcdn.com/image/fetch/$s_!xzlv!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1e98d57-1266-4a15-8633-4bdfd41b0547_1602x538.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xzlv!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1e98d57-1266-4a15-8633-4bdfd41b0547_1602x538.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!xzlv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1e98d57-1266-4a15-8633-4bdfd41b0547_1602x538.png" width="1456" height="489" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a1e98d57-1266-4a15-8633-4bdfd41b0547_1602x538.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:489,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:208823,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sacavix.substack.com/i/169345318?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1e98d57-1266-4a15-8633-4bdfd41b0547_1602x538.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!xzlv!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1e98d57-1266-4a15-8633-4bdfd41b0547_1602x538.png 424w, /__u/substackcdn.com/image/fetch/$s_!xzlv!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1e98d57-1266-4a15-8633-4bdfd41b0547_1602x538.png 848w, /__u/substackcdn.com/image/fetch/$s_!xzlv!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1e98d57-1266-4a15-8633-4bdfd41b0547_1602x538.png 1272w, /__u/substackcdn.com/image/fetch/$s_!xzlv!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1e98d57-1266-4a15-8633-4bdfd41b0547_1602x538.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>La clave es que en el bloqueo optimista, no se imponen bloqueos sobre los recursos durante la mayor parte del tiempo de vida de la transacci&#243;n. Por eso, el bloqueo compartido y el exclusivo, al implicar una "reserva" y restricci&#243;n de acceso al recurso, son por definici&#243;n parte del modelo pesimista, el modelo de bloqueo pesimista al definir bloqueos a recursos por lo general genera m&#225;s contenci&#243;n en la base de datos, lo que en sistemas de alta concurrencia puede llegar a generar problemas como mayor latencia, disminuci&#243;n del rendimiento, mayor consumo de recursos e incluso deadlocks; siempre hay que usarlo con cuidado :-).</p><h2>Caso de estudio con MySQL/InnoDB y Spring Boot/JPA</h2><p>Para poder ver como implementar bloqueo optimista y pesimista en un ejemplo pr&#225;ctico vamos a implementar un sencillo ejemplo en el que tenemos una tabla <code>account</code> que muestra el balance de una cuenta bancaria.</p><p>Esta ser&#237;a la definici&#243;n de la tabla:</p><pre><code>CREATE TABLE `account` (
  `id` bigint NOT NULL,
  `balance` decimal(10,2) NOT NULL,
  `version` bigint DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
</code></pre><p>El campo <code>version</code> es fundamental en esta definici&#243;n. El bloque optimista no es manejado directamente por una sentencia de base de datos como si existe en el pesimista, por ello debemos &#8220;manualmente&#8220; implementar el bloqueo optimista. El campo <code>version</code> nos ayudar&#225; a poder saber cual fue la versi&#243;n de la fila le&#237;da, su valor ser&#225; incremental, lo veremos en la siguiente secci&#243;n.</p><p>Por lo dem&#225;s es una tabla normal en MySQL con InnoDB.</p><p>Para la parte de Spring Boot es un proyecto est&#225;ndar con las dependencias de Spring Web (opcional), el driver de MySQL y Spring Data JPA.</p><h2>Bloqueos en la pr&#225;ctica con Spring</h2><p>Una vez creada la tabla le hemos realizado algunos inserts iniciales de datos.</p><p>Este es el estado antes de comenzar a experimentar.</p><pre><code>&gt; select * from account;
+----+---------+---------+
| id | balance | version |
+----+---------+---------+
|  1 | 1000.00 |       0 |
|  2 | 1000.00 |       0 |
|  3 | 2000.00 |       0 |
+----+---------+---------+
3 rows in set (0.01 sec)</code></pre><p>A nivel de aplicaci&#243;n esta es la Entity Account:</p><pre><code>@Entity
public class Account {

    @Id
    private Long id;

    @Column(nullable = false)
    private BigDecimal balance;

    @Version
    private Long version;

   // getters and setters
}</code></pre><p>Especial importancia a la anotaci&#243;n @Version sobre el campo num&#233;rico <code>version</code>, esta anotaci&#243;n es clave en JPA para implementar bloqueos optimistas, pero ella de por si no implementa el bloqueo en si, solo permite saber al leer una fila cual es la &#8220;version&#8220; le&#237;da.</p><p>La anotaci&#243;n <code>@Version</code> marca un campo de una entidad JPA (una clase que se mapea a una tabla de base de datos) para que act&#250;e como un <strong>contador de versiones</strong> o <strong>marca de tiempo</strong> para esa entidad. Su objetivo es detectar si un registro ha sido modificado por otra transacci&#243;n desde que fue le&#237;do por la transacci&#243;n en curso. El flujo es &#8230; si se lee un registro &#8230;. se hacen cambios &#8230;. se intenta escribir y al intentar escribir difiere la versi&#243;n le&#237;da indica que &#8220;alguien&#8220; en el medio cambio el registro y es probable que el dato no sea valida, usualmente se rollbackean estos cambio y se reintenta leyendo el valor actualizado nuevamente.</p><p>Cuando ocurre la diferencia de versi&#243;n al escribir se lanza la excepci&#243;n <code>OptimisticLockingFailureException</code>, la aplicaci&#243;n debe estar preparada para manejar este excepci&#243;n y poder reintentar de ser necesario.</p><p>El campo anota con @Version puede ser de los siguientes tipos:</p><ul><li><p><code>int</code> o <code>Integer</code></p></li><li><p><code>long</code> o <code>Long</code></p></li><li><p><code>short</code> o <code>Short</code></p></li><li><p><code>java.sql.Timestamp</code></p></li><li><p><code>java.time.Instant</code></p></li></ul><p>En caso de que sea num&#233;rico se incrementa en uno el valor cada vez que se cambia la entidad, en caso de que ser una fecha se settea la fecha actual del sistema.</p><p><strong>Repository</strong></p><p>Nuestro repositorio del ejemplo es sencillo.</p><pre><code><code>public interface AccountRepository extends JpaRepository&lt;Account, Long&gt; {

    @Lock(LockModeType.PESSIMISTIC_WRITE)
    @Query("SELECT a FROM Account a WHERE a.id = :id")
    Optional&lt;Account&gt; findByIdForUpdate(@Param("id") Long id);
}</code></code></pre><p>Es el est&#225;ndar JpaRepository, sin embargo incluye un m&#233;todo para buscar custom usando JPQL y lo m&#225;s interesante esta anotado con <code>@Lock</code>.</p><p>La anotaci&#243;n <code>@Lock</code> permite explicitamente definir control m&#225;s estricto sobre la concurrencia a nivel de base de datos, garantizando que un recurso (una fila o un conjunto de filas) no sea modificado (o incluso le&#237;do, dependiendo del modo) por otras transacciones mientras tu transacci&#243;n lo est&#225; procesando. Puede usarse tanto para bloqueo optimista como pesimista.</p><p>En el caso de ejemplo de nuestro repository lo vamos a usar para un bloqueo pesimista con la variante de bloque exclusivo (<strong>exclusive lock</strong>). </p><p>Este bloqueo exclusivo termina generando un <code>SELECT &#8230; FOR UPDATE</code>.</p><p>Si habilitas en la config la property para visualizar las consultas:</p><pre><code>spring.jpa.show-sql=true</code></pre><p>Podr&#225;s ver al llamar este m&#233;todo los logs de la consulta:</p><pre><code>Hibernate: select a1_0.id,a1_0.balance,a1_0.version from account a1_0 where a1_0.id=? for update</code></pre><p>Esta anotaci&#243;n permite varias opciones, para no hacer m&#225;s largo el art&#237;culo puedes ver m&#225;s informaci&#243;n <a href="https://docs.spring.io/spring-data/jpa/docs/current/api/org/springframework/data/jpa/repository/Lock.html">ac&#225;</a>.</p><p><strong>Service</strong></p><p>En el service de nuestra aplicaci&#243;n que permite probar ambos tipos de bloqueos tendremos la siguiente implementaci&#243;n.</p><pre><code>@Service
public class AccountService {

    private final AccountRepository accountRepository;

    public AccountService(AccountRepository accountRepository) {
        this.accountRepository = accountRepository;
    }
    
    @Transactional
    public void transferWithOptimisticLock(Long accountId, BigDecimal amount) {
        Account account = accountRepository.findById(accountId)
                .orElseThrow(() -&gt; new RuntimeException("Account not found"));

        account.setBalance(account.getBalance().subtract(amount));
        // Al commit, si otro proceso cambi&#243; la versi&#243;n, lanza OptimisticLockingFailureException
    }
    
    @Transactional
    public void transferWithPessimisticLock(Long accountId, BigDecimal amount) {
        Account account = accountRepository.findByIdForUpdate(accountId)
                .orElseThrow(() -&gt; new RuntimeException("Account not found"));

        account.setBalance(account.getBalance().subtract(amount));
        // El lock se mantiene hasta que finaliza la transacci&#243;n
    }
}</code></pre><p><strong>Bloqueo optimista en la pr&#225;ctica</strong></p><p>Si ejecutamos el m&#233;todo <code>transferWithOptimisticLock</code> obtendremos una implementaci&#243;n de bloqueo optimista, si entre que leemos el datos y lo manipulamos otro proceso lo modifica se lanzar&#225; una excepci&#243;n al intentar escribir. Nuestra aplicaci&#243;n deber&#237;a estar preparada para manejar la excepci&#243;n, capturarla e implementar una estrategia de reintento que permita obtener la versi&#243;n m&#225;s fresca del dato de la base de datos y trabajar con ella. Un ejemplo de como manejar este escenario es con un c&#243;digo como el siguiente:</p><pre><code>public void transferWithRetry(Long accountId, BigDecimal amount) {
    int maxRetries = 3;
    for (int i = 0; i &lt; maxRetries; i++) {
        try {
            accountService.transferWithOptimisticLock(accountId, amount);
            return;
        } catch (OptimisticLockingFailureException e) {
            System.<em>out</em>.println("Retrying due to optimistic locking...");
        }
    }
    throw new IllegalStateException("Too many retries on optimistic lock");
}</code></pre><p>Es un ejemplo sencillo pero ilustra el como debe hacerse.</p><p><strong>Bloqueo pesimista en la pr&#225;ctica</strong></p><p>En nuestro service tenemos el m&#233;todo <code>transferWithPessimisticLock</code>, como su nombre indica es para ejemplificar este tipo de bloqueo. </p><p>Este m&#233;todo es <code>@Transactional</code> y dentro hace dos cosas dentro de la transacci&#243;n:</p><ol><li><p>Invoca al m&#233;todo <code>findByIdForUpdate</code> del Repository</p></li><li><p>Modifica la entidad Account</p></li></ol><p>Luego de que se ejecuta el m&#233;todo <code>findByIdForUpdate</code> se hace un <code>SELECT &#8230; FOR UPDATE </code>y las tuplas quedan bloqueadas de forma exclusiva, solo se desbloquean cuando termina de ejecutarse el m&#233;todo y se haga el <code>COMMIT</code> de la transacci&#243;n; durante ese per&#237;odo de tiempo nadie podr&#225; leer ni escribir en esas filas. </p><p>Si un proceso intenta acceder con otra instrucci&#243;n <code>SELECT &#8230; FOR UPDATE</code> no lo podr&#225; hacer y quedar&#225; en espera (causando contenci&#243;n); en cambio si se hace un <code>SELECT</code> com&#250;n se obtendr&#225;n datos en dependencia del nivel de aislamiento que tenga configurado el motor de base de datos.</p><p>Como nota aclaratoria el uso de <code>@Version</code> en este art&#237;culo solo es necesario para el bloqueo optimista.</p><p>Espero te haya sido el &#250;til el art&#237;culo,</p><p></p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Análisis de sentimientos con Spring AI, PrompTemplate y OpenAI]]></title><description><![CDATA[Implementamos una API para analizar sentimientos a partir de textos de entrada.]]></description><link>https://sacavix.substack.com/p/analisis-de-sentimientos-con-spring</link><guid isPermaLink="false">https://sacavix.substack.com/p/analisis-de-sentimientos-con-spring</guid><dc:creator><![CDATA[Yoandy]]></dc:creator><pubDate>Fri, 02 May 2025 11:31:24 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/93a0a621-9dcd-49e5-aabe-db9c0a762204_462x138.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>El an&#225;lsis de sentimientos es una poderosa t&#233;cnica que permite evaluar la percepci&#243;n que se tiene sobre un bien o servicio ofrecido por una persona o empresa. Utiliza el procesamiento del lenguaje natural (NLP) para analizar el tono emocional de un texto, determinando si expresa un sentimiento positivo, negativo o neutro. Esto se logra mediante la evaluaci&#243;n de opiniones, emociones y actitudes expresadas en el texto.</p><p>Los 3 posibles resultados b&#225;sicos de analizar un texto en busca del sentimiento expresado son: </p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Suscribirse&quot;,&quot;language&quot;:&quot;es&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">&#161;Gracias por leer SACAViX's Substack! Suscr&#237;bete gratis para recibir nuevos posts y apoyar mi trabajo.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Escribe tu correo electr&#243;nico..." tabindex="-1"><input type="submit" class="button primary" value="Suscribirse"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><ul><li><p><strong>Positivo:</strong></p><p>Indica una actitud favorable o positiva hacia un tema, producto o servicio.</p></li><li><p><strong>Negativo:</strong></p><p>Indica una actitud desfavorable o cr&#237;tica hacia un tema, producto o servicio.</p></li><li><p><strong>Neutro:</strong></p><p>No expresa una actitud clara o favorable, o es ambiguo.</p></li></ul><p>Las aplicaciones de analizar sentimientos son extensas, podemos usarlo por ejemplo:</p><ul><li><p>Evaluar atenci&#243;n al cliente (sin tener que usar la fastidiosa encuesta a la que te derivan la mayor&#237;a de los call centers cuando terminas una consulta).</p></li><li><p>An&#225;lisis de noticias.</p></li><li><p>An&#225;slisis de redes sociales.</p></li><li><p>Entre muchas otras, libera tu imaginaci&#243;n :-)</p></li></ul><h2>API con Spring AI y OpenAI</h2><p>De Spring AI ya hemos hablando en otros art&#237;culos de esta newsletter, te recomiendo especialmente el primer art&#237;culo de esta serie donde explicamos muchos conceptos para empezar en esto de la IA como RAG, LLMs, Embeddings, Vectores, etc. Puedes acceder ac&#225;:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;00f2cd9a-653f-41b7-8e0f-b20dc66e12c9&quot;,&quot;caption&quot;:&quot;Desde hace unos 10 d&#237;as comenc&#233; a meterme mucho en esto de la IA, LLMs, RAG, MCP, Embbeding, bases de datos vectoriales y todo lo que rodea este mundo que est&#225; de moda actualmente, pero que luego de probar algunas cosas, estoy m&#225;s que convencido que lleg&#243; para quedarse.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Spring AI, un ejemplo con RAG, MCP y OpenAI&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:113566277,&quot;name&quot;:&quot;Yoandy&quot;,&quot;bio&quot;:null,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/20f8b449-52a1-4aea-8881-b4b126da2dd3_1080x1080.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-04-09T12:03:52.618Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F698cded0-a993-48e9-af21-b674c64dc01a_872x346.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://sacavix.substack.com/p/spring-ai-un-ejemplo-con-rag-mcp&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:160884724,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:7,&quot;comment_count&quot;:3,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;SACAViX's Substack&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8c3095c-b9ab-435c-877e-13d25f3e12ac_900x900.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>En este ejemplo en caso de uso para el an&#225;lisis de sentimientos vamos a usar OpenAI, Spring AI 1.0.0-M8 (en el art&#237;culo referenciado usamos M6, hay cosas que vienen cambiando toda vez que no es una versi&#243;n final a&#250;n), haremos uso de Spring Web ya que va a ser una API y especificamente dentro de Spring AI, usaremos el concepto de PrompTemplate que es &#250;til en este tipo de casos de uso.</p><div class="pullquote"><p><em>Si te gusta y ves &#250;til el contenido considera apoyar mi trabajo con una contribuci&#243;n en <a href="https://www.patreon.com/sacavix_tech">Patreon</a>.</em></p></div><h3>&#191;Qu&#233; es prompTemplate?</h3><p>Un prompt es la entrada o instrucci&#243;n proporcionada por el usuario a un modelo de IA para obtener una respuesta o realizar una tarea espec&#237;fica. Refleja las necesidades inmediatas del usuario y gu&#237;a al modelo en la generaci&#243;n de una respuesta adecuada, en muchos casos &#8212; como el actual &#8212; necesitamos enriquecer el prompt entrado por el usuario para instruir al LLM sobre que queremos hacer con ese texto.</p><p>Definiendo un poco mejor el concepto: en Spring AI es una clase que te permite <strong>definir un prompt con variables din&#225;micas</strong>, de forma similar a una plantilla de texto con par&#225;metros. Su objetivo es ayudarte a construir f&#225;cilmente <em>prompts personalizados</em> para enviar a un modelo de lenguaje sin tener que concatenar strings manualmente.</p><p>Ejemplo:</p><pre><code><code>String template = "&#191;Cu&#225;l es la capital de {pais}?";
PromptTemplate promptTemplate = new PromptTemplate(template);

Prompt prompt = promptTemplate.create(Map.of("pais", "Francia"));</code></code></pre><p>No hay mucha ciencia.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!gO2z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a9426c5-442e-41a8-abd3-a7cab2bc73d0_462x138.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!gO2z!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a9426c5-442e-41a8-abd3-a7cab2bc73d0_462x138.png 424w, /__u/substackcdn.com/image/fetch/$s_!gO2z!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a9426c5-442e-41a8-abd3-a7cab2bc73d0_462x138.png 848w, /__u/substackcdn.com/image/fetch/$s_!gO2z!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a9426c5-442e-41a8-abd3-a7cab2bc73d0_462x138.png 1272w, /__u/substackcdn.com/image/fetch/$s_!gO2z!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a9426c5-442e-41a8-abd3-a7cab2bc73d0_462x138.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!gO2z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a9426c5-442e-41a8-abd3-a7cab2bc73d0_462x138.png" width="462" height="138" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3a9426c5-442e-41a8-abd3-a7cab2bc73d0_462x138.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:138,&quot;width&quot;:462,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:16640,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sacavix.substack.com/i/162632684?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a9426c5-442e-41a8-abd3-a7cab2bc73d0_462x138.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!gO2z!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a9426c5-442e-41a8-abd3-a7cab2bc73d0_462x138.png 424w, /__u/substackcdn.com/image/fetch/$s_!gO2z!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a9426c5-442e-41a8-abd3-a7cab2bc73d0_462x138.png 848w, /__u/substackcdn.com/image/fetch/$s_!gO2z!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a9426c5-442e-41a8-abd3-a7cab2bc73d0_462x138.png 1272w, /__u/substackcdn.com/image/fetch/$s_!gO2z!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a9426c5-442e-41a8-abd3-a7cab2bc73d0_462x138.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p></p><h5>Dependencias de la API</h5><p>El proyecto lo puedes generar usando <a href="https://start.spring.io/">Spring Initialzr</a>, b&#225;sicamente estas son las dos dependencias que necesitamos.</p><pre><code>&lt;dependency&gt;
 &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
 &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
 &lt;groupId&gt;org.springframework.ai&lt;/groupId&gt;
 &lt;artifactId&gt;spring-ai-starter-model-openai&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre><p>Por un lado Spring Web y por otro el starter de Spring AI para Open AI, como comentamos en el primer art&#237;culo, si queremos reemplazar por otro proveedor, solo tendr&#237;amos que cambiar la dependencia por ejemplo: spring-ai-vertex-ai-gemini-spring-boot-starter si deseamos usar el LLM de Google y obviamente ajustar las configuraciones.</p><h5>C&#243;digo de la API de sentimientos</h5><p>La implementaci&#243;n de la API consta de 3 archivos principales, m&#225;s la configuraci&#243;n del proyecto Spring Boot en s&#237;.</p><p>La creaci&#243;n del bean del chat con el cual le mandaremos los prompts al LLM.</p><pre><code>@Component
public class ChatConfig {
    @Bean
    public ChatClient chatClient(ChatClient.Builder clientBuilder) {
        return clientBuilder
                .build();
    }
}</code></pre><p>El servicio principal, que nos permitir&#225; recibir el texto entrado desde el controlador, armar el prompt con el template predefinido y mandar la consulta al model de Open AI.</p><pre><code>@Service
public class SentimentAnalysisService {
        private final ChatClient chatClient;
        public SentimentAnalysisService(ChatClient chatClient) {
            this.chatClient = chatClient;
        }
        public String analyzeSentiment(String text) {
            String promptTemplate = """
                Analiza el sentimiento del siguiente texto y responde solo con: Positivo, Negativo o Neutral.

                Texto: {input}
                Sentimiento:
                """;

            PromptTemplate template = new PromptTemplate(promptTemplate);
            Prompt prompt = template.create(Map.of("input", text));

            return chatClient.prompt(prompt).call().content();
        }
}
</code></pre><p>El controlador no lo agregar&#233; porque es trivial, al final del art&#237;culo dejo el enlace al c&#243;digo del proyecto.</p><p>En la configuraci&#243;n del proyecto solo necesitamos especificar la key de OpenAI bajo la property:</p><pre><code># OpenAI
spring.ai.openai.api-key=${OPEN_AI_KEY}</code></pre><p>Siempre este tipo de claves ponla en el ambiente, as&#237; las mantienes seguras y evitas por error pushearlas a los repositorios.</p><p>Por defecto Spring AI con Open AI usado el modelo gpt-4o-mini, pero usando la property spring.ai.openai.chat.options.model puedes especificar cualquier otro disponible, por ej: gpt-4o, gpt-4-turbo, gpt-3.5-turbo. Puedes ver m&#225;s opciones de configuraci&#243;n en la <a href="https://docs.spring.io/spring-ai/reference/api/chat/openai-chat.html">documentaci&#243;n oficia</a>l.</p><p>Algunos ejemplos de la respuesta de la API:</p><pre><code>http :8080/sentiment text=="Fue genial la compra, me encanto"
HTTP/1.1 200 
Connection: keep-alive
Content-Length: 8
Content-Type: text/plain;charset=UTF-8
Date: Thu, 01 May 2025 17:36:24 GMT
Keep-Alive: timeout=60

Positivo
</code></pre><pre><code>http :8080/sentiment text=="No estuvo a la altura"
HTTP/1.1 200 
Connection: keep-alive
Content-Length: 8
Content-Type: text/plain;charset=UTF-8
Date: Thu, 01 May 2025 17:36:43 GMT
Keep-Alive: timeout=60

Negativo
</code></pre><pre><code>http :8080/sentiment text=="Normal, no mucho que resaltar"
HTTP/1.1 200 
Connection: keep-alive
Content-Length: 7
Content-Type: text/plain;charset=UTF-8
Date: Thu, 01 May 2025 17:36:56 GMT
Keep-Alive: timeout=60

Neutral
</code></pre><p>Por si no lo haz usado esta forma de ejecutar llamadas a la API es usando <a href="https://httpie.io/">HTTPie</a>, es simple y ligera.</p><p>Espero te haya gustado el art&#237;culo, aunque es simple creo resulta &#250;til para continuar explorando la IA en Java.</p><p>Antes de concluir en este lugar no hablamos solo de IA, te recomiendo le des una lectura a nuesto art&#237;culo reciente sobre Java 24 y el tema de los hilos virtuales:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;88e33f10-e280-4178-96fc-07ac8c84b870&quot;,&quot;caption&quot;:&quot;En una entrada anterior hablamos de los hilos virtuales y del problema del pinning. Los hilos virtuales son parte del proyecto Loom, una iniciativa que busca revolucionar el modelo de concurrencia de Java con 3 elementos principales:&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;showDescription&quot;:true,&quot;showImage&quot;:true,&quot;size&quot;:&quot;sm&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Virtual Threads ahora m&#225;s escalables, el pinning ha sido resuelto&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:113566277,&quot;name&quot;:&quot;Yoandy&quot;,&quot;bio&quot;:null,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/20f8b449-52a1-4aea-8881-b4b126da2dd3_1080x1080.jpeg&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-03-21T12:03:23.036Z&quot;,&quot;cover_image&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5895839-b9f2-4f64-9e6b-2860779032f1_2224x1054.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://sacavix.substack.com/p/virtual-threads-ahora-mas-escalables&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:159525211,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:5,&quot;comment_count&quot;:2,&quot;publication_id&quot;:null,&quot;publication_name&quot;:&quot;SACAViX's Substack&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc8c3095c-b9ab-435c-877e-13d25f3e12ac_900x900.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><p>Nos vemos en el pr&#243;ximo art&#237;culo, hasta la pr&#243;xima.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Suscribirse&quot;,&quot;language&quot;:&quot;es&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">&#161;Gracias por leer SACAViX's Substack! Suscr&#237;bete gratis para recibir nuevos posts y apoyar mi trabajo.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Escribe tu correo electr&#243;nico..." tabindex="-1"><input type="submit" class="button primary" value="Suscribirse"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[MCP en Java, paso a paso (+Inspector & VsCode)]]></title><description><![CDATA[Creamos un servidor MCP con Spring AI para conectar la IA con tu negocio y lo probamos con MCP Inspector y VSCode]]></description><link>https://sacavix.substack.com/p/mcp-en-java-inspector-and-vscode</link><guid isPermaLink="false">https://sacavix.substack.com/p/mcp-en-java-inspector-and-vscode</guid><dc:creator><![CDATA[Yoandy]]></dc:creator><pubDate>Mon, 14 Apr 2025 12:02:57 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!eMTN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe461a130-f36d-4f42-b710-0b23269323f4_772x317.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>MCP es la joya de la corona en la expansi&#243;n de la IA, te&#243;ricamente permite conectar la IA con cualquier sistema, la idea resumida es bastante simple, ejecutas un prompt en una IA y en dependencia de las herramientas que exponga to servicio MCP y la descripci&#243;n que tengan el LLM ejecutar&#225; una funci&#243;n en la herramienta. Se pasar&#225;n los par&#225;metros que exija la funci&#243;n llamada y el resultado de la funci&#243;n nuevamente es &#8220;moderado&#8220; por el modelo, creando una mejora en la respuesta; es un enfoque para mejorar la calidad de las respuestas con valores actualizados (un tipo de RAG y forma de &#8220;disminuir&#8220; la posibilidad de alucionaciones).</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Suscribirse&quot;,&quot;language&quot;:&quot;es&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">&#161;Gracias por leer SACAViX's Substack! Suscr&#237;bete gratis para recibir nuevos posts y apoyar mi trabajo.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Escribe tu correo electr&#243;nico..." tabindex="-1"><input type="submit" class="button primary" value="Suscribirse"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Vamos por el principio.</p><p>Conceptualmente <strong>Model Context Protocol (MCP)</strong> es un est&#225;ndar abierto desarrollado por Anthropic que busca estandarizar la forma en que las aplicaciones de inteligencia artificial, especialmente aquellas basadas en modelos de lenguaje grandes (LLMs), interact&#250;an con fuentes de datos y herramientas externas.</p><div class="pullquote"><p>Si te gusta y ves &#250;til el contenido considera apoyar mi trabajo con una contribuci&#243;n en <a href="https://www.patreon.com/sacavix_tech">Patreon</a>.</p></div><p>MCP funciona bajo un esquema de arquitectura Cliente/Servidor.</p><ul><li><p><strong>Cliente MCP</strong>: Es el componente dentro del host que se encarga de la comunicaci&#243;n con los servidores. Cada cliente mantiene una conexi&#243;n 1:1 con un servidor MCP espec&#237;fico.</p></li><li><p><strong>Servidor MCP</strong>: Es un programa que expone una fuente de datos o servicio externo a trav&#233;s del protocolo MCP. Act&#250;a como adaptador, conectando con una fuente (como un sistema de archivos local, una API web, una base de datos, etc.) y present&#225;ndola de forma estandarizada al cliente MCP.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!eMTN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe461a130-f36d-4f42-b710-0b23269323f4_772x317.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!eMTN!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe461a130-f36d-4f42-b710-0b23269323f4_772x317.png 424w, /__u/substackcdn.com/image/fetch/$s_!eMTN!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe461a130-f36d-4f42-b710-0b23269323f4_772x317.png 848w, /__u/substackcdn.com/image/fetch/$s_!eMTN!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe461a130-f36d-4f42-b710-0b23269323f4_772x317.png 1272w, /__u/substackcdn.com/image/fetch/$s_!eMTN!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe461a130-f36d-4f42-b710-0b23269323f4_772x317.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!eMTN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe461a130-f36d-4f42-b710-0b23269323f4_772x317.png" width="772" height="317" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e461a130-f36d-4f42-b710-0b23269323f4_772x317.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:317,&quot;width&quot;:772,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:50913,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://sacavix.substack.com/i/161212203?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe461a130-f36d-4f42-b710-0b23269323f4_772x317.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!eMTN!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe461a130-f36d-4f42-b710-0b23269323f4_772x317.png 424w, /__u/substackcdn.com/image/fetch/$s_!eMTN!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe461a130-f36d-4f42-b710-0b23269323f4_772x317.png 848w, /__u/substackcdn.com/image/fetch/$s_!eMTN!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe461a130-f36d-4f42-b710-0b23269323f4_772x317.png 1272w, /__u/substackcdn.com/image/fetch/$s_!eMTN!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe461a130-f36d-4f42-b710-0b23269323f4_772x317.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>A nivel de transporte existen dos mecanismos principales para la comunicaci&#243;n entre el cliente y el servidor:</p><ul><li><p>stdio (entrada/salida est&#225;ndar): Este m&#233;todo utiliza los flujos est&#225;ndar de entrada y salida del sistema operativo para la comunicaci&#243;n. Es especialmente &#250;til en entornos donde el cliente y el servidor se ejecutan en el mismo proceso o en procesos estrechamente acoplados. &#8203;</p></li><li><p>HTTP con Server-Sent Events (SSE): Este enfoque emplea HTTP junto con SSE para permitir una comunicaci&#243;n unidireccional desde el servidor al cliente. En este enfoque el cliente inicia la solicitud y luego el servidor puede enviar multiples solicitudes.</p></li></ul><h2>&#191;C&#243;mo crear un servidor MCP?</h2><p>Aunque en el art&#237;culo anterior hablamos un poco del tema, en este vamos a ir mas en profundidad en el tema.</p><p>Como es costumbre, usaremos Spring AI; a diferencia del art&#237;culo anterior en este caso usaremos como sistema de transporte <code>stdio</code>, despu&#233;s de crear el servidor vamos a conectarlo con MCP Inspector y con VisualStudio Code (luego hablamos m&#225;s de esto, pero es super importante).</p><p>Crear un servidor MCP es b&#225;sicamente exponer Tools que &#8220;hacen cosas&#8220;, lo que van a hacer estas herramientas es cualquier cosa que se te puedo ocurrir &#8230; llamar a una API externa, leer un fichero, enviar un email &#8230; cualquier cosa.</p><p><strong>Paso 1: Crear un proyecto Spring y agregar dependencias.</strong></p><p>La forma r&#225;pida es entrar a <a href="https://start.spring.io/">Spring Starter</a> y agregar la dependencia de Model Context Protocol Server: </p><pre><code><code>&lt;dependency&gt;
&lt;groupId&gt;org.springframework.ai&lt;/groupId&gt;
&lt;artifactId&gt;spring-ai-starter-mcp-server-webmvc&lt;/artifactId&gt;
&lt;/dependency&gt;</code></code></pre><p><strong>Paso 2: Crear las herramientas que queremos exponer en un servicio com&#250;n de Spring.</strong></p><p>En este ejemplo creamos 2 herramientas, una que devuelve la cantidad de libros para ni&#241;os y otra que dado un t&#237;tulo devuelve la descripci&#243;n del libro correspondiente.</p><pre><code><code>@Tool(name = "get_books", description = "Get the total of books for children")
public int countBooks() {
  return books.size();
}

@Tool(name = "get_books_by_title", description = "Get the total of books for children")
public String getDescription(@ToolParam(description = "the book title") String title) {
 return books.getOrDefault(title, "The book was not found");
}</code></code></pre><p><strong>Paso 3: Exponer las herramientas</strong></p><p>Para esto solo creamos un Bean de Spring con la lista de herramientas que queremos exponer:</p><pre><code>@Bean
public List&lt;ToolCallback&gt; toolCallbackProvider(BookService bookService){
  return List.of(ToolCallbacks.from(bookService));
}</code></pre><p><strong>Paso 4: Definir la configuraci&#243;n correcta</strong></p><pre><code>spring.ai.mcp.server.name=mcp-server-example
spring.ai.mcp.server.version=0.0.1
spring.ai.mcp.server.stdio=true
spring.main.web-application-type=none
spring.main.banner-mode=off

logging.pattern.console=</code></pre><p>Es fundamental habilitar el modo stdio en el servidor, deshabilitar los logs y el banner de Spring; sin esto no va a funcionar el servidor en el modo stdio, para usar SSE no importa el tema de los logs.</p><p>Una vez terminado el desarrollo del servidor MCP pasamos a construir el jar.</p><pre><code>mvn package</code></pre><p>El jar quedar&#225; en la carpeta target/. Desde ah&#237; lo referenciaremos para ejecutarlo con java -jar path. Tambi&#233;n puedes mover el jar a otro lugar.</p><h2>&#191;C&#243;mo probar un servidor MCP?</h2><p>Para probar un servidor MCP podemos hacerlo de varias formas, IDEs como VisualStudio Code con Github Copilot en modo agente incorporan un mecanismo para comunicarse con los servidores MCP. Tambi&#233;n existe MCP Inspector, una UI para probar.</p><h4>MCP con VisualStudio Code</h4><p>La combinaci&#243;n de MCP con VisualStudio code adem&#225;s de servir para poder probar los servidores es poderosa, pues permite agregar herramientas para elevar nuestra productividad como desarrolladores.</p><p>Para configurar el servidor MCP es importante tener VisualStudio code con Copilot (en la capa gratuita funciona esto) en modo agente:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!f4Er!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff64a318f-e5cf-486c-8262-842cae542441_589x122.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!f4Er!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff64a318f-e5cf-486c-8262-842cae542441_589x122.png 424w, /__u/substackcdn.com/image/fetch/$s_!f4Er!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff64a318f-e5cf-486c-8262-842cae542441_589x122.png 848w, /__u/substackcdn.com/image/fetch/$s_!f4Er!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff64a318f-e5cf-486c-8262-842cae542441_589x122.png 1272w, /__u/substackcdn.com/image/fetch/$s_!f4Er!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff64a318f-e5cf-486c-8262-842cae542441_589x122.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!f4Er!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff64a318f-e5cf-486c-8262-842cae542441_589x122.png" width="589" height="122" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f64a318f-e5cf-486c-8262-842cae542441_589x122.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:122,&quot;width&quot;:589,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:12552,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sacavix.substack.com/i/161212203?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff64a318f-e5cf-486c-8262-842cae542441_589x122.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!f4Er!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff64a318f-e5cf-486c-8262-842cae542441_589x122.png 424w, /__u/substackcdn.com/image/fetch/$s_!f4Er!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff64a318f-e5cf-486c-8262-842cae542441_589x122.png 848w, /__u/substackcdn.com/image/fetch/$s_!f4Er!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff64a318f-e5cf-486c-8262-842cae542441_589x122.png 1272w, /__u/substackcdn.com/image/fetch/$s_!f4Er!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff64a318f-e5cf-486c-8262-842cae542441_589x122.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Luego pulsar en el icono de Select Tools en la imagen (Ctrl + Shift + /, en Linux).</p><p>Saltar&#225; un di&#225;logo que nos guiar&#225; para asociar el servidor MCP a VisualStudio, al final de proceso se generar&#225; un JSON de config, similar al siguiente:</p><pre><code><code>{
    "mcp": {
        "servers": {
            "example-mcp-server": {
                "type": "stdio",
                "command": "java",
                "args": [
                    "-jar",
                    "/home/yoandypv/mcp-server-example/target/mcp-server-example-0.0.1-SNAPSHOT.jar"
                ]
            }
        }
    }
}</code></code></pre><p>Saldr&#225; adem&#225;s un peque&#241;o men&#250; contextual para iniciar el servidor MCP configurado.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!Soix!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b163b08-18cd-4419-b127-0aadc55d4cc6_310x107.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!Soix!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b163b08-18cd-4419-b127-0aadc55d4cc6_310x107.png 424w, /__u/substackcdn.com/image/fetch/$s_!Soix!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b163b08-18cd-4419-b127-0aadc55d4cc6_310x107.png 848w, /__u/substackcdn.com/image/fetch/$s_!Soix!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b163b08-18cd-4419-b127-0aadc55d4cc6_310x107.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Soix!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b163b08-18cd-4419-b127-0aadc55d4cc6_310x107.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!Soix!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b163b08-18cd-4419-b127-0aadc55d4cc6_310x107.png" width="310" height="107" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2b163b08-18cd-4419-b127-0aadc55d4cc6_310x107.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:107,&quot;width&quot;:310,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:10733,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sacavix.substack.com/i/161212203?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b163b08-18cd-4419-b127-0aadc55d4cc6_310x107.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!Soix!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b163b08-18cd-4419-b127-0aadc55d4cc6_310x107.png 424w, /__u/substackcdn.com/image/fetch/$s_!Soix!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b163b08-18cd-4419-b127-0aadc55d4cc6_310x107.png 848w, /__u/substackcdn.com/image/fetch/$s_!Soix!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b163b08-18cd-4419-b127-0aadc55d4cc6_310x107.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Soix!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b163b08-18cd-4419-b127-0aadc55d4cc6_310x107.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Una vez se esta ejecutando el servidor MCP, ya podemos interactuar con el chat de Copilot y el modelo seleccionado (en esta oportunidad estoy usando Claude 3.5 Sonnet) decidir&#225; como usar nuestras herramientas, por ejemplo:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!Mpac!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0894e47e-3d57-4769-9569-75974445590e_607x283.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!Mpac!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0894e47e-3d57-4769-9569-75974445590e_607x283.png 424w, /__u/substackcdn.com/image/fetch/$s_!Mpac!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0894e47e-3d57-4769-9569-75974445590e_607x283.png 848w, /__u/substackcdn.com/image/fetch/$s_!Mpac!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0894e47e-3d57-4769-9569-75974445590e_607x283.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Mpac!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0894e47e-3d57-4769-9569-75974445590e_607x283.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!Mpac!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0894e47e-3d57-4769-9569-75974445590e_607x283.png" width="607" height="283" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0894e47e-3d57-4769-9569-75974445590e_607x283.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:283,&quot;width&quot;:607,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:23722,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sacavix.substack.com/i/161212203?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0894e47e-3d57-4769-9569-75974445590e_607x283.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!Mpac!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0894e47e-3d57-4769-9569-75974445590e_607x283.png 424w, /__u/substackcdn.com/image/fetch/$s_!Mpac!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0894e47e-3d57-4769-9569-75974445590e_607x283.png 848w, /__u/substackcdn.com/image/fetch/$s_!Mpac!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0894e47e-3d57-4769-9569-75974445590e_607x283.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Mpac!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0894e47e-3d57-4769-9569-75974445590e_607x283.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>La magia de la IA, ahora se puede poner uno creativo con las herramientas y hacerlas productivas, no como esta que es solo un ejemplo b&#225;sico. Si te fijas bien ejecut&#243; <code>&#8220;get_books&#8220;</code>, que es el @Tool name que especificamos en el m&#233;todo.</p><h4>MCP Inspector</h4><p>Esta es una utilidad para probar servidores MCP, que funciona igual tanto con stdio como con SSE. Esta soluci&#243;n permite explorar las herramientas disponibles, revisar variables de entorno, invocar las herramientas, etc.</p><p>Es una utilidad desarrollada en node. <a href="https://modelcontextprotocol.io/docs/tools/inspector">Ac&#225;</a> puedes consultar m&#225;s informaci&#243;n de la misma.</p><p>Una vez instalada (en el <a href="https://github.com/modelcontextprotocol/inspector">link</a> est&#225; como hacerlo) debemos ejecutarla apuntando al servidor creado, por ejemplo:</p><pre><code>npx @modelcontextprotocol/inspector java -jar /home/yoandypv/mcp-server-example/target/mcp-server-example-0.0.1-SNAPSHOT.jar
</code></pre><p>Esto disponibilizar&#225; una interfaz web en el puerto 6274.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!1X6H!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc449ff-b85d-447a-92a3-9b6786db0da0_544x89.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!1X6H!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc449ff-b85d-447a-92a3-9b6786db0da0_544x89.png 424w, /__u/substackcdn.com/image/fetch/$s_!1X6H!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc449ff-b85d-447a-92a3-9b6786db0da0_544x89.png 848w, /__u/substackcdn.com/image/fetch/$s_!1X6H!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc449ff-b85d-447a-92a3-9b6786db0da0_544x89.png 1272w, /__u/substackcdn.com/image/fetch/$s_!1X6H!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc449ff-b85d-447a-92a3-9b6786db0da0_544x89.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!1X6H!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc449ff-b85d-447a-92a3-9b6786db0da0_544x89.png" width="544" height="89" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4fc449ff-b85d-447a-92a3-9b6786db0da0_544x89.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:89,&quot;width&quot;:544,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:14578,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sacavix.substack.com/i/161212203?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc449ff-b85d-447a-92a3-9b6786db0da0_544x89.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!1X6H!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc449ff-b85d-447a-92a3-9b6786db0da0_544x89.png 424w, /__u/substackcdn.com/image/fetch/$s_!1X6H!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc449ff-b85d-447a-92a3-9b6786db0da0_544x89.png 848w, /__u/substackcdn.com/image/fetch/$s_!1X6H!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc449ff-b85d-447a-92a3-9b6786db0da0_544x89.png 1272w, /__u/substackcdn.com/image/fetch/$s_!1X6H!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fc449ff-b85d-447a-92a3-9b6786db0da0_544x89.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Solo queda abrir el enlace y tendremos acceso a todo para probar nuestro servidor MCP.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!eVp0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02a17e5f-eeea-4c56-9fc1-78cb3c67935d_1388x656.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!eVp0!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02a17e5f-eeea-4c56-9fc1-78cb3c67935d_1388x656.png 424w, /__u/substackcdn.com/image/fetch/$s_!eVp0!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02a17e5f-eeea-4c56-9fc1-78cb3c67935d_1388x656.png 848w, /__u/substackcdn.com/image/fetch/$s_!eVp0!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02a17e5f-eeea-4c56-9fc1-78cb3c67935d_1388x656.png 1272w, /__u/substackcdn.com/image/fetch/$s_!eVp0!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02a17e5f-eeea-4c56-9fc1-78cb3c67935d_1388x656.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!eVp0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02a17e5f-eeea-4c56-9fc1-78cb3c67935d_1388x656.png" width="1388" height="656" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/02a17e5f-eeea-4c56-9fc1-78cb3c67935d_1388x656.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:656,&quot;width&quot;:1388,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:78810,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sacavix.substack.com/i/161212203?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02a17e5f-eeea-4c56-9fc1-78cb3c67935d_1388x656.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!eVp0!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02a17e5f-eeea-4c56-9fc1-78cb3c67935d_1388x656.png 424w, /__u/substackcdn.com/image/fetch/$s_!eVp0!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02a17e5f-eeea-4c56-9fc1-78cb3c67935d_1388x656.png 848w, /__u/substackcdn.com/image/fetch/$s_!eVp0!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02a17e5f-eeea-4c56-9fc1-78cb3c67935d_1388x656.png 1272w, /__u/substackcdn.com/image/fetch/$s_!eVp0!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02a17e5f-eeea-4c56-9fc1-78cb3c67935d_1388x656.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Bueno, hasta ac&#225; el art&#237;culo de hoy, espero te sea &#250;til, el c&#243;digo como siempre est&#225; disponible en <a href="https://github.com/yoandypv/spring-ai-mcp-server-example">Github</a>.</p><blockquote><p>Si te gusta y ves &#250;til el contenido considera apoyar mi trabajo con una contribuci&#243;n en <a href="https://www.patreon.com/sacavix_tech">Patreon</a>.</p></blockquote><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Suscribirse&quot;,&quot;language&quot;:&quot;es&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">&#161;Gracias por leer SACAViX's Substack! Suscr&#237;bete gratis para recibir nuevos posts y apoyar mi trabajo.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Escribe tu correo electr&#243;nico..." tabindex="-1"><input type="submit" class="button primary" value="Suscribirse"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Spring AI, un ejemplo con RAG, MCP y OpenAI]]></title><description><![CDATA[Construimos una tienda con venta por chat usando Spring AI, VectorDB, RAG, MCP y muchos otros conceptos.]]></description><link>https://sacavix.substack.com/p/spring-ai-un-ejemplo-con-rag-mcp</link><guid isPermaLink="false">https://sacavix.substack.com/p/spring-ai-un-ejemplo-con-rag-mcp</guid><dc:creator><![CDATA[Yoandy]]></dc:creator><pubDate>Wed, 09 Apr 2025 12:03:52 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F698cded0-a993-48e9-af21-b674c64dc01a_872x346.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Desde hace unos 10 d&#237;as comenc&#233; a meterme mucho en esto de la IA, LLMs, RAG, MCP, Embbeding, bases de datos vectoriales y todo lo que rodea este mundo que est&#225; de moda actualmente, pero que luego de probar algunas cosas, estoy m&#225;s que convencido que lleg&#243; para quedarse.</p><p>Un elemento clave que me motiv&#243; a adentrarme en este mundo fue la posibilidad de llevar a las aplicaciones Java los diferentes casos de uso que nos ofrece la IA, en ese sentido hoy te quiero presentar Spring AI &#127793;&#128640;.</p><p>En este art&#237;culo haremos una presentaci&#243;n de algunos conceptos relacionados con la IA, como estos enfoques est&#225;n soportados por Spring y haremos un ejemplo completo implementando una tienda de venta de dispositivos m&#243;viles cuyo canal de ventas es un simple chat, pero existir&#225;n microservicios que se comunicar&#225;n entre ellos usando MCP. M&#225;s adelante detallaremos el problema y su soluci&#243;n.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Suscribirse&quot;,&quot;language&quot;:&quot;es&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">&#161;Gracias por leer SACAViX's Substack! Suscr&#237;bete gratis para recibir nuevos posts y apoyar mi trabajo.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Escribe tu correo electr&#243;nico..." tabindex="-1"><input type="submit" class="button primary" value="Suscribirse"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2>Conceptos principales de la IA para desarrolladores</h2><p>Si ya est&#225;s en el tema de la IA puedes saltarte esta secci&#243;n.</p><ul><li><p>LLMs (Modelos de lenguaje de gran tama&#241;o): Son modelos de aprendizaje profundo entrenados con enormes cantidades de datos para comprender y generar lenguaje humano de manera coherente. Utilizan arquitecturas como los transformadores para procesar y generar texto, siendo fundamentales en aplicaciones como chatbots y asistentes virtuales.</p></li><li><p>RAG (Generaci&#243;n aumentada por recuperaci&#243;n ): Es una t&#233;cnica que combina modelos generativos con sistemas de recuperaci&#243;n de informaci&#243;n. Antes de generar una respuesta, el modelo busca en bases de datos externas informaci&#243;n relevante, mejorando la precisi&#243;n y actualidad de sus respuestas. Esto es &#250;til para proporcionar informaci&#243;n actualizada y espec&#237;fica en tiempo real. &#8203;</p></li><li><p> MCP (Protocolo de contexto del modelo): Es un est&#225;ndar abierto que facilita la conexi&#243;n de asistentes de IA con sistemas donde residen los datos, como repositorios de contenido y herramientas empresariales. Su objetivo es permitir que los modelos de lenguaje accedan a informaci&#243;n relevante y actualizada, mejorando la calidad de sus respuestas. &#8203;(Gracias Anthropic por esto &#129653;)</p></li><li><p>Embeddings: Son representaciones num&#233;ricas de objetos del mundo real, como palabras, im&#225;genes o videos, en forma de vectores en un espacio de alta dimensi&#243;n. Estas representaciones permiten a los modelos de aprendizaje autom&#225;tico procesar y analizar datos complejos de manera eficiente, facilitando tareas como la b&#250;squeda sem&#225;ntica y el an&#225;lisis de similitud.</p></li><li><p>Bases de datos vectoriales: Son sistemas dise&#241;ados para almacenar y gestionar datos en forma de vectores de alta dimensi&#243;n. Permiten realizar b&#250;squedas r&#225;pidas y precisas basadas en similitud, siendo esenciales en aplicaciones que requieren recuperaci&#243;n eficiente de informaci&#243;n, como sistemas de recomendaci&#243;n y b&#250;squeda de im&#225;genes. &#8203;</p></li><li><p>User prompt: Es la entrada o instrucci&#243;n proporcionada por el usuario a un modelo de IA para obtener una respuesta o realizar una tarea espec&#237;fica. Refleja las necesidades inmediatas del usuario y gu&#237;a al modelo en la generaci&#243;n de una respuesta adecuada. &#8203;</p></li><li><p>System prompt: Es una instrucci&#243;n predefinida que establece el comportamiento general y el rol del modelo de IA durante una interacci&#243;n. Define el contexto y las reglas bajo las cuales el modelo debe operar, influyendo en c&#243;mo interpreta y responde a las solicitudes del usuario. &#8203;(Esto es clave cuando se desarrollan agentes para delimitar su &#225;mbito de operaci&#243;n)</p></li><li><p>Alucinaciones: Se refiere a respuestas generadas por modelos de IA que son incorrectas o no tienen fundamento en los datos de entrenamiento. Estas "alucinaciones" pueden ser problem&#225;ticas en aplicaciones cr&#237;ticas, y se est&#225;n desarrollando t&#233;cnicas como RAG para mitigarlas.</p></li></ul><p>Si entiendes estos conceptos ya estas list@ para entender el resto del art&#237;culo.</p><h2>&#191;Qu&#233; es Spring AI?</h2><p>Sin muchas vueltas, es un <a href="https://spring.io/projects/spring-ai">proyecto</a> m&#225;s del ecosistema Spring dise&#241;ado para facilitar la integraci&#243;n de capacidades de inteligencia artificial (IA) en aplicaciones Java, especialmente aquellas basadas en Spring/Boot. Lo mejor es que proporciona una fachada &#250;nica que permite interactuar con diferentes modelos; usando Spring AI puedes sin cambiar el c&#243;digo de tu aplicaci&#243;n conectarte con Open AI, Amazon Bedrock, Vertex Gemini o un modelo local en Ollama, entre otros. Y no, NO es lo que piensas, NO ES solo un cliente para conecarse a los LLM, es mucho m&#225;s que eso.</p><blockquote><p>Si te est&#225; gustando el contenido considera apoyar mi trabajo con una contribuci&#243;n en <a href="https://www.patreon.com/sacavix_tech">Patreon</a>.</p></blockquote><p><strong>Principales caracter&#237;sticas</strong></p><ul><li><p>Compatibilidad con m&#250;ltiples proveedores de modelos de IA y soporte para sus caraceter&#237;sticas principales: </p><ul><li><p>Chat (lo usaremos en el ejemplo)&#8203;</p></li><li><p>Embeddings (lo usaremos en el ejemplo)&#8203;</p></li><li><p>Conversi&#243;n de texto a imagen&#8203;</p></li><li><p>Transcripci&#243;n de audio&#8203;</p></li><li><p>Conversi&#243;n de texto a voz&#8203;</p></li><li><p>Moderaci&#243;n de contenido&#8203;</p></li></ul></li><li><p>API port&#225;til y unificada: Ofrece una interfaz coherente para interactuar con diferentes proveedores de IA, admitiendo tanto operaciones s&#237;ncronas como en streaming, y brindando acceso a caracter&#237;sticas espec&#237;ficas de cada modelo.&#8203;</p></li><li><p>Soporte para bases de datos vectoriales: Se integra con proveedores como Apache Cassandra, Azure Vector Search, Chroma, Milvus, MongoDB Atlas, Neo4j, Oracle, PostgreSQL/PGVector, PineCone, Qdrant, Redis y Weaviate, facilitando el almacenamiento y recuperaci&#243;n eficiente de datos embebidos.&#8203; (en el ejemplo usaremos PgVector)</p></li><li><p>Llamadas a herramientas y funciones<strong>:</strong> Permite que los modelos soliciten la ejecuci&#243;n de herramientas y funciones del lado del cliente, accediendo a informaci&#243;n en tiempo real seg&#250;n sea necesario (puede ser con advisors llamando a herramientas locales a la app o remotas usando MCP).</p></li><li><p>Observabilidad<strong>:</strong> Proporciona informaci&#243;n sobre las operaciones relacionadas con la IA, facilitando el monitoreo y la depuraci&#243;n.&#8203;</p></li><li><p>Evaluaci&#243;n de modelos de IA<strong>:</strong> Incluye utilidades para evaluar el contenido generado y proteger contra respuestas err&#243;neas o no deseadas. (lo usaremos en el ejemplo)</p></li><li><p>APIs especializadas<strong>:</strong> Ofrece una API fluida para comunicarse con modelos de chat de IA, similar a WebClient y RestClient, y una API de asesores que encapsula patrones recurrentes de IA generativa, transformando datos enviados y recibidos de modelos de lenguaje.&#8203; (la usaremos en ejemplo)</p></li><li><p>Soporte para memoria de conversaci&#243;n y generaci&#243;n aumentada por recuperaci&#243;n (RAG)<strong>:</strong> Facilita la gesti&#243;n del contexto en interacciones de chat y mejora la generaci&#243;n de respuestas mediante la recuperaci&#243;n de informaci&#243;n relevante.&#8203; (la usaremos en el ejemplo)</p></li><li><p>Configuraci&#243;n autom&#225;tica con Spring Boot: Proporciona configuraciones predeterminadas y starters para todos los modelos de IA y almacenes vectoriales, simplificando la integraci&#243;n en aplicaciones Spring Boot.&#8203;</p></li><li><p>Soporte para MCP: Tiene soporte completo para montar un cliente y servidor usando Model Context Protocol, permitiendo crear &#8220;servicios&#8220; con l&#243;gica de negocio reusables (microservicios), pero cuya comunicaci&#243;n usa MCP. (la usaremos en el ejemplo)</p></li></ul><p>Adem&#225;s de esto ofrece otro grupo de funcionalidades que puedes inspeccionar m&#225;s a fondo en la p&#225;gina del proyecto.</p><h3>Ejemplo con Spring AI: Tienda de aplicaciones m&#243;viles</h3><p><strong>Descripci&#243;n del problema:</strong></p><p>Desarrollar una tienda online cuyo canal de ventas sea por chat mediante un agente de ventas, el usuario deber&#225; interactuar con el agente solicitando informaci&#243;n sobre los dispositivos m&#243;viles que se venden, sus caracter&#237;sticas, precio y cualquier otro detalle relevante. El agente deber&#225; tener memoria para poder dar continuidad a la comunicaci&#243;n con el cliente. El cliente podr&#225; consultar los diferentes m&#233;todos de pago disponibles para su compra y podr&#225; realizar la compra del dispositivo de su &#237;nteres. Para ejecutar los pagos debe crearse un servicio de pagos independiente y generico cuya comunicaci&#243;n con la tienda sea usando Model Context Protocol.</p><p><strong>Puntos claves para resolver el problema:</strong></p><ul><li><p>El canal de ventas ser&#225; por chat, por lo que se require la incorporaci&#243;n de un LLM que tenga esta capability. Casi todos los LLM hoy permiten esto, usaremos OpenAI (Este ejemplo igual lo hice con Vertex Gemini, en el c&#243;digo a&#250;n est&#225;n comentadas las dependencias).</p></li><li><p>Los productos de la tienda son propios y para mejorar la calidad de las respuestas aplicaremos RAG, esto brindar&#225; informaci&#243;n a los clientes sobre los productos a la venta. Los datos reales de los productos que venderemos ser&#225;n nuestros.</p></li><li><p>Para implementar RAG precisamos una base de datos vectorial y de la capability de <a href="https://platform.openai.com/docs/guides/embeddings">embedding</a> para texto de OpenAI (o cualquier otro modelo que lo soporte). </p></li><li><p>Usando el modelo de OpenAI con <a href="https://docs.spring.io/spring-ai/reference/api/embeddings/openai-embeddings.html">Spring AI</a> + PostgreSQL/PgVector vamos a convertir nuestra base de datos de productos y llevarla a un esquema vectorial.</p></li><li><p>Para darle acceso al LLM a nuestra base de datos vectorial con la informaci&#243;n (que seria el RAG) debemos crear un Advisor en Spring IA, el tipo de advisor para implementar RAG es QuestionAnswerAdvisor.</p></li><li><p>Debemos restringir el contexto de nuestro chat, as&#237; no da respuestas fuera de su funci&#243;n que es vender m&#243;viles. Para esto usaremos un prompt de sistema al inicializar la aplicaci&#243;n.</p></li><li><p>Debemos implementar un mecanismo de memoria por usuario, as&#237; nuestro chat podr&#225; mantener una comunicaci&#243;n fluida con el cliente. Para esto usaremos un mapa concurrente en Java, como key del mapa el username como valor un PromptChatMemoryAdvisor. Para este ejemplo la memoria de nuestro chat la montamos en la ram, pero pudieramos usar alguna otra implementaci&#243;n como implementar la memoria en la base de datos vectorial.</p></li><li><p>Como los pagos los tiene que procesar un servicio dedicado crearemos una api de payments que expondr&#225; dos herramientas (en MCP / Spring AI las funcionalidades expuestas por la API se llaman Tools); una funcionalidad para listar los m&#233;todos de pagos disponibles y otra para ejecutar el pago. (En este ejemplo las funcionalidades est&#225;n mockeadas, pero pueden ser perfectamente reales).</p></li><li><p>Tenemos que implementar el cliente MCP en la tienda para que se conecte a nuestro servidor MCP de payments.</p></li></ul><h4>Arquitectura de la aplicaci&#243;n</h4><p>A alto nivel son dos servicios (la tienda y el servicio de pagos), el usuario se conecta a la tienda v&#237;a http/rest (en chat real seguro mejor usar algo como websockets), la tienda interactuar&#225; con el LLM, la base de datos vectorial y si el cliente necesita comprar con la herramienta de pagos v&#237;a MCP.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!ZCu5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F519c568e-ef23-462c-a04d-6272febe8469_788x306.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!ZCu5!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F519c568e-ef23-462c-a04d-6272febe8469_788x306.png 424w, /__u/substackcdn.com/image/fetch/$s_!ZCu5!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F519c568e-ef23-462c-a04d-6272febe8469_788x306.png 848w, /__u/substackcdn.com/image/fetch/$s_!ZCu5!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F519c568e-ef23-462c-a04d-6272febe8469_788x306.png 1272w, /__u/substackcdn.com/image/fetch/$s_!ZCu5!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F519c568e-ef23-462c-a04d-6272febe8469_788x306.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!ZCu5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F519c568e-ef23-462c-a04d-6272febe8469_788x306.png" width="788" height="306" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/519c568e-ef23-462c-a04d-6272febe8469_788x306.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:306,&quot;width&quot;:788,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:40755,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sacavix.substack.com/i/160884724?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F519c568e-ef23-462c-a04d-6272febe8469_788x306.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!ZCu5!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F519c568e-ef23-462c-a04d-6272febe8469_788x306.png 424w, /__u/substackcdn.com/image/fetch/$s_!ZCu5!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F519c568e-ef23-462c-a04d-6272febe8469_788x306.png 848w, /__u/substackcdn.com/image/fetch/$s_!ZCu5!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F519c568e-ef23-462c-a04d-6272febe8469_788x306.png 1272w, /__u/substackcdn.com/image/fetch/$s_!ZCu5!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F519c568e-ef23-462c-a04d-6272febe8469_788x306.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Cuando el usuario hace una pregunta (a partir de ahora un prompt de usuario) entra en juego la magia del RAG, el flujo es como se describe en el siguiente diagrama:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!53Vs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F698cded0-a993-48e9-af21-b674c64dc01a_872x346.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!53Vs!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F698cded0-a993-48e9-af21-b674c64dc01a_872x346.png 424w, /__u/substackcdn.com/image/fetch/$s_!53Vs!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F698cded0-a993-48e9-af21-b674c64dc01a_872x346.png 848w, /__u/substackcdn.com/image/fetch/$s_!53Vs!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F698cded0-a993-48e9-af21-b674c64dc01a_872x346.png 1272w, /__u/substackcdn.com/image/fetch/$s_!53Vs!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F698cded0-a993-48e9-af21-b674c64dc01a_872x346.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!53Vs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F698cded0-a993-48e9-af21-b674c64dc01a_872x346.png" width="872" height="346" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/698cded0-a993-48e9-af21-b674c64dc01a_872x346.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:346,&quot;width&quot;:872,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:49809,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sacavix.substack.com/i/160884724?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F698cded0-a993-48e9-af21-b674c64dc01a_872x346.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!53Vs!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F698cded0-a993-48e9-af21-b674c64dc01a_872x346.png 424w, /__u/substackcdn.com/image/fetch/$s_!53Vs!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F698cded0-a993-48e9-af21-b674c64dc01a_872x346.png 848w, /__u/substackcdn.com/image/fetch/$s_!53Vs!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F698cded0-a993-48e9-af21-b674c64dc01a_872x346.png 1272w, /__u/substackcdn.com/image/fetch/$s_!53Vs!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F698cded0-a993-48e9-af21-b674c64dc01a_872x346.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Pero para poder aumentar y dar respuestas de calidad primero tuvimos que llenar nuestra base de datos vectorial con la informaci&#243;n de nuestros productos a la venta. Para ello seguimos el siguiente flujo:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!sVU_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa14cbae3-9994-4cd8-9a0b-26072e6daac9_643x310.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!sVU_!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa14cbae3-9994-4cd8-9a0b-26072e6daac9_643x310.png 424w, /__u/substackcdn.com/image/fetch/$s_!sVU_!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa14cbae3-9994-4cd8-9a0b-26072e6daac9_643x310.png 848w, /__u/substackcdn.com/image/fetch/$s_!sVU_!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa14cbae3-9994-4cd8-9a0b-26072e6daac9_643x310.png 1272w, /__u/substackcdn.com/image/fetch/$s_!sVU_!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa14cbae3-9994-4cd8-9a0b-26072e6daac9_643x310.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!sVU_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa14cbae3-9994-4cd8-9a0b-26072e6daac9_643x310.png" width="643" height="310" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a14cbae3-9994-4cd8-9a0b-26072e6daac9_643x310.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:310,&quot;width&quot;:643,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:43709,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sacavix.substack.com/i/160884724?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa14cbae3-9994-4cd8-9a0b-26072e6daac9_643x310.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!sVU_!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa14cbae3-9994-4cd8-9a0b-26072e6daac9_643x310.png 424w, /__u/substackcdn.com/image/fetch/$s_!sVU_!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa14cbae3-9994-4cd8-9a0b-26072e6daac9_643x310.png 848w, /__u/substackcdn.com/image/fetch/$s_!sVU_!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa14cbae3-9994-4cd8-9a0b-26072e6daac9_643x310.png 1272w, /__u/substackcdn.com/image/fetch/$s_!sVU_!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa14cbae3-9994-4cd8-9a0b-26072e6daac9_643x310.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>El proceso de llenar la base de datos vectorial se realiza una sola vez, en un caso de uso real tendr&#237;amos que buscar la forma de sincronizarlas. Tambi&#233;n podemos exponer la informaci&#243;n de la base de datos SQL como una tool y consultarla directamente, pero los resultados no ser&#225;n tan buenos (en mi opini&#243;n).</p><p>Bueno vamos a ver algunos snippets de c&#243;digo ahora. Todo esta disponible para probar en GitHub.</p><h4>C&#243;digo del servicio principal: mobile store</h4><p>Empecemos por las dependencias necesarias. Ser&#225; ante todo una aplicaci&#243;n web por lo que el starter web estar&#225; presente, voy a colocar solo las principales:</p><pre><code>&lt;dependency&gt;
&lt;groupId&gt;org.springframework.ai&lt;/groupId&gt;
&lt;artifactId&gt;spring-ai-openai-spring-boot-starter&lt;/artifactId&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
&lt;groupId&gt;org.springframework.ai&lt;/groupId&gt;
&lt;artifactId&gt;spring-ai-pgvector-store-spring-boot-starter&lt;/artifactId&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
&lt;groupId&gt;org.springframework.ai&lt;/groupId&gt;
&lt;artifactId&gt;spring-ai-mcp-client-spring-boot-starter&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre><p>Creo las dependencias son bastante claras de arriba a abajo: integraci&#243;n con OpenAI (por defecto con el modelo gpt-4o-mini, pero <a href="https://docs.spring.io/spring-ai/reference/api/chat/openai-chat.html">puede cambiarse</a>), le sigue la integraci&#243;n con la base vectorial (extensi&#243;n de postgresql), y el cliente MCP para poder conectarnos al servicio de payments.</p><p>Importante es crear nuestro cliente de chat (cliente para OpenAI), inicializarlo y disponibilizarlo como un bean de Spring.</p><pre><code>@Bean
public ChatClient chatClient(ChatClient.Builder clientBuilder, McpSyncClient paymentMcpSyncClient) {
        return clientBuilder
                .defaultSystem(systemPrompt)
                .defaultTools(new SyncMcpToolCallbackProvider(paymentMcpSyncClient))
                .build();
    }</code></pre><p>Este cliente en realidad tiene cositas:</p><p>Con esta configuraci&#243;n le setteamos el prompt de sistema justo cuando lo creamos, as&#237; restringimos el scope.</p><pre><code><code>.defaultSystem(systemPrompt) </code></code></pre><p>En nuestro caso le pusimos:</p><pre><code><code>private String systemPrompt = """
You are an AI assistant helping people find the phone of their dreams 
in our store in Uruguay. If no information is available, simply inform 
them that there are no devices available at the moment.
""";</code></code></pre><p>Con esta otra parte le hacemos la conexi&#243;n al servidor MCP de payments que veremos m&#225;s adelante:</p><pre><code><code>.defaultTools(new SyncMcpToolCallbackProvider(paymentMcpSyncClient))</code></code></pre><p>Ese paymentMcpSyncClient es este Bean.</p><pre><code><code>@Bean
public McpSyncClient paymentMcpSyncClient() {

   var mcpClient = McpClient
            .sync(new HttpClientSseClientTransport("http://localhost:8081"))
            .build();
   logger.info("McpSyncClient created");
   mcpClient.initialize();
   return mcpClient;

}</code></code></pre><p>Una vez creado con el codigo anterior, le dimos memoria a nuestro chat con este c&#243;digo:</p><pre><code>private Map&lt;String, PromptChatMemoryAdvisor&gt; memoryAdvisors;  
....
this.memoryAdvisors = new ConcurrentHashMap&lt;&gt;();  
.....

public PromptChatMemoryAdvisor getOrCreateChatMemoryAdvisor(String user) {

        if (this.memoryAdvisors.containsKey(user)) {
            logger.info("Retrieving chat memory advisor for user {}", user);
            return this.memoryAdvisors.get(user);
        }

        this.memoryAdvisors.put(user, PromptChatMemoryAdvisor.builder(new InMemoryChatMemory()).build());
        logger.info("Creating chat memory advisor for user {}", user);
        return this.memoryAdvisors.get(user);
    }</code></pre><p>Cada vez que un usuario hace una pregunta o inicia una conversaci&#243;n le creamos o reusamos su PromptChatMemoryAdvisor, de esta forma nuestra aplicaci&#243;n siempre tendr&#225; contexto.</p><p>La parte principal de la aplicaci&#243;n es esta secci&#243;n de c&#243;digo:</p><pre><code>public String chat(String user, String userQuery) {

        var memory = this.getOrCreateChatMemoryAdvisor(user);

        return this.chatClient
                .prompt()
                .user(userQuery)
                .advisors(Arrays.asList(memory, this.questionAnswerAdvisor))
                .call()
                .content();
    }</code></pre><p>Este m&#233;todo ser&#225; invocado desde el controlador y pasar&#225; el usuario que est&#225; en la conversaci&#243;n y el su consulta o prompt de usuario.</p><p>La parte que falta por explicar es: <code>this.questionAnswerAdvisor</code></p><p>En Spring AI existe un Advisor llamado: QuestionAnswerAdvisor, este es el encargado de conectar con nuestra base vectorial para poder hacer el RAG correctamente. Para ello al incluir la dependencia de pgVector, y proveer configuraciones de conexi&#243;n a postgress automaticamente se crea en el contexto un bean llamado VectorStore (una fachada para la VectorDB incluida en la app). </p><p>Entonces el c&#243;digo anterior se complementa con: </p><pre><code>private QuestionAnswerAdvisor questionAnswerAdvisor;
private final VectorStore vectorStore;
...
this.questionAnswerAdvisor = new QuestionAnswerAdvisor(vectorStore);</code></pre><p>El controlador de la aplicaci&#243;n para este ejemplo se sencilla y sin relevancia para este art&#237;culo:</p><pre><code>@RestController
@RequestMapping("/store")
public class StoreAssistantController {

    private final StoreAssistantService storeAssistantService;

    public StoreAssistantController(StoreAssistantService storeAssistantService) {
        this.storeAssistantService = storeAssistantService;
    }
    
    @GetMapping("/{user}/question")
    public String chat(@PathVariable("user") String user, @RequestParam String question) {
        return this.storeAssistantService.chat(user, question);
    }
}
</code></pre><p>Algunas properties importantes:</p><pre><code><code># La conexi&#243;n a postgre/pgvector
spring.application.name=mobile-store
spring.datasource.url=jdbc:postgresql://localhost/store
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.hikari.maximum-pool-size=10

# Cuando se vaya a popular la base vectorial
spring.ai.vectorstore.pgvector.initialize-schema=true

# La clave de la API de Open AI (esta no es gratis)
spring.ai.openai.api-key=${OPEN_AI_KEY}</code></code></pre><h4>C&#243;digo del servicio de pagos: Payment Tool (mcp server)</h4><p>Como vimos, tenemos que crear un servidor MCP, en la tienda ya vimos como crear el cliente, ahora veremos como creamos un servidor MCP.</p><p>Antes de ver algo de c&#243;digo Spring AI tiene 3 formas principales de comunicaci&#243;n para MCP a nivel de transporte: </p><ul><li><p>stdio (s&#225;lida est&#225;ndar, esta es &#250;til para herramientas como claude desktop).</p></li><li><p>SSE basado en servlets</p></li><li><p>SSE con Webflux para variante reactiva.</p></li></ul><p>En este ejemplo usamos SSE basado en servlets.</p><p>La implementaci&#243;n de MCP de Spring puedes consultarla <a href="https://docs.spring.io/spring-ai/reference/api/mcp/mcp-overview.html">ac&#225;</a>, es interesante.</p><p>Bueno, veamos el c&#243;digo, empecemos por las dependencias:</p><pre><code>&lt;dependency&gt;
&lt;groupId&gt;org.springframework.ai&lt;/groupId&gt;
&lt;artifactId&gt;spring-ai-mcp-server-webmvc-spring-boot-starter&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre><p>Como comentamos anteriormente, en un servidor MCP se exponen herramientas, las cuales son aprovechadas por el LLM para &#8220;hacer cosas&#8220;. Cuando un cliente MCP inicia se conecta al servidor y carga las herramientas disponibles para usar. En nuestro ejemplo creamos dos herramientas en un servicio de Spring.</p><pre><code>@Service
public class PaymentService {

    private final Logger log = LoggerFactory.getLogger(PaymentService.class);

    @Tool(name = "execute_payment", description = "Execute a payment for buy a mobile")
    private String executePayment(@ToolParam(description = "the id of the mobile") String productId,
                                  @ToolParam(description = "the user") String user,
                                  @ToolParam(description = "the price of the mobile") double amount) throws JsonProcessingException {
        log.info("Executing payment id:{}, user: {}, amount:{}", productId, user, amount);

        return "Payment executed successfully";
    }


    @Tool(name = "get_card_brands", description = "Get the card brands or payment methods available")
    private String getCardBrandsAvailable() {
        return "Visa, Mastercard";
    }
}</code></pre><p>Las tools se crean anotando m&#233;todos con @Tool y es importante proveer una descripci&#243;n precisa de lo que hace la herramienta, de esta forma el LLM sabe cuando invocar la herramienta. Los par&#225;metros deben ser de igual forma documentados.</p><p>La creaci&#243;n del servidor concluye creando un bean con las herramientas que se quieren exponer:</p><pre><code><code>@Bean
public ToolCallbackProvider toolCallbackProvider(PaymentService paymentService) {
        return MethodToolCallbackProvider.builder()
                .toolObjects(paymentService)
                .build();
    }</code></code></pre><p>Con esto ya nuestro servidor MCP est&#225; disponible.</p><p>Este proyecto est&#225; 100% disponible en Github, espero te sirva este art&#237;culo, aprend&#237; muchas cosas nuevas programando esta soluci&#243;n, espero te sea &#250;til.</p><p>Si te gusta el contenido considera apoyar mi trabajo con una contribuci&#243;n en <a href="https://www.patreon.com/sacavix_tech">Patreon</a>.</p><ul><li><p>Repositorio de la tienda: https://github.com/yoandypv/spring-ai-mobile-store</p></li><li><p>Repositorio del servidor MCP de pagos: https://github.com/yoandypv/spring-ai-mcp-payments-server</p></li></ul><p>Disfruta &#128640;&#128640;</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Suscribirse&quot;,&quot;language&quot;:&quot;es&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">&#161;Gracias por leer SACAViX's Substack! Suscr&#237;bete gratis para recibir nuevos posts y apoyar mi trabajo.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Escribe tu correo electr&#243;nico..." tabindex="-1"><input type="submit" class="button primary" value="Suscribirse"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Virtual Threads ahora más escalables, el pinning ha sido resuelto]]></title><description><![CDATA[El problema del los hilos virtuales anclados ha sido resuelto, ahora Java escalar&#225; a&#250;n m&#225;s.]]></description><link>https://sacavix.substack.com/p/virtual-threads-ahora-mas-escalables</link><guid isPermaLink="false">https://sacavix.substack.com/p/virtual-threads-ahora-mas-escalables</guid><dc:creator><![CDATA[Yoandy]]></dc:creator><pubDate>Fri, 21 Mar 2025 12:03:23 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5895839-b9f2-4f64-9e6b-2860779032f1_2224x1054.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>En una entrada anterior hablamos de los hilos virtuales y del problema del pinning. Los hilos virtuales son parte del proyecto Loom, una iniciativa que busca revolucionar el modelo de concurrencia de Java con 3 elementos principales:</p><ul><li><p>La introducci&#243;n de hilos ligeros y baratos controlados por la JVM para optimizar los cl&#225;sicos hilos de plataforma; conocidos inicialmente como fibras, ahora hilos virtuales.</p></li><li><p>Proveer un mecanismo de concurrencia estructurada a la plataforma Java.</p></li><li><p>Adaptar ThreadLocal para los hilos virtuales a trav&#233;s de valores de &#225;mbito, en ingl&#233;s Scoped Values.</p></li></ul><p>Como dato interesante los hilos virtuales son daemon threads.</p><p>En Java 21 se liber&#243; en versi&#243;n final la <a href="https://openjdk.org/jeps/444">JEP 444</a> que dot&#243; a la JDK de hilos virtuales &#8220;estables&#8220;, pero &#8230; a&#250;n quedaba el problema del pinning por resolver, sino est&#225;s muy al tanto te recomiendo leas este <a href="https://sacavix.com/2024/11/los-hilos-virtuales-y-el-problema-del-pinning/">art&#237;culo</a> en el blog.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Suscribirse&quot;,&quot;language&quot;:&quot;es&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">&#161;Gracias por leer SACAViX's Substack! Suscr&#237;bete gratis para recibir nuevos posts y apoyar mi trabajo.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Escribe tu correo electr&#243;nico..." tabindex="-1"><input type="submit" class="button primary" value="Suscribirse"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Para entender los hilos virtuales debes conocer 4 conceptos b&#225;sicos:</p><ul><li><p>Carrier threads: Son los hilos de plataforma sobre los que se ejecutan los hilos virtuales, por defecto se crean tantos hilos de plataforma como indique Runtime.getRuntime().availableProcessors(), obviamente esto es &#8220;tunneable&#8220;.</p></li><li><p>Mount: Es el proceso mediante el cual el Virtual Thread Scheduler asigna la ejecuci&#243;n de un hilo virtual a uno de plataforma (lo coloca sobre un carrier thread para que se ejecute).</p></li><li><p>Unmount: Lo contrario a lo anterior, si un proceso virtual entra en un estado de espera entonces el planificador desmonta el hilo virtual para liberar el hilo de plataforma, evitando que permanezca bloqueado y pueda ser usado en otra tarea.</p></li><li><p>Pinning: Es el problema que ocurr&#237;a antes de Java 24 en el que un VirtualThread en ciertas condiciones se quedaba montado en el Carrier thread, evitando la liberaci&#243;n del recurso.</p></li></ul><p>Con estos conceptos y esta imagen podr&#225;s ver como funcionan los hilos virtuales sobre los de plataforma (carrier threads).</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!DrC5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4b3268ec-440d-4573-a756-2a259fc095f1_755x899.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!DrC5!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4b3268ec-440d-4573-a756-2a259fc095f1_755x899.webp 424w, /__u/substackcdn.com/image/fetch/$s_!DrC5!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4b3268ec-440d-4573-a756-2a259fc095f1_755x899.webp 848w, /__u/substackcdn.com/image/fetch/$s_!DrC5!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4b3268ec-440d-4573-a756-2a259fc095f1_755x899.webp 1272w, /__u/substackcdn.com/image/fetch/$s_!DrC5!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4b3268ec-440d-4573-a756-2a259fc095f1_755x899.webp 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!DrC5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4b3268ec-440d-4573-a756-2a259fc095f1_755x899.webp" width="755" height="899" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4b3268ec-440d-4573-a756-2a259fc095f1_755x899.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:899,&quot;width&quot;:755,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:54586,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/webp&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://sacavix.substack.com/i/159525211?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4b3268ec-440d-4573-a756-2a259fc095f1_755x899.webp&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!DrC5!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4b3268ec-440d-4573-a756-2a259fc095f1_755x899.webp 424w, /__u/substackcdn.com/image/fetch/$s_!DrC5!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4b3268ec-440d-4573-a756-2a259fc095f1_755x899.webp 848w, /__u/substackcdn.com/image/fetch/$s_!DrC5!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4b3268ec-440d-4573-a756-2a259fc095f1_755x899.webp 1272w, /__u/substackcdn.com/image/fetch/$s_!DrC5!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4b3268ec-440d-4573-a756-2a259fc095f1_755x899.webp 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h1>Java 21 vs 24: Anclaje resuelto</h1><p>Para ver si se resolvi&#243; el problema del pinning hemos realizado un experimento con un bloque de c&#243;digo que produce pinning en Java 21 usando la primitiva synchronized de Java.</p><p>El ejemplo:</p><pre><code><code>private static final Object lock = new Object();

.....

try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
    for (int i = 0; i &lt; 100; i++) {
        executor.submit(() -&gt; {
            synchronized (lock) {
                System.out.println(Thread.currentThread());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        });
    }
}</code></code></pre><p>En resumen creamos un executor que ejecutar&#225; cada tarea enviada en un hilo virtual. Le mandamos un grupo de tareas con un bloque sincronizado por una variable global.</p><p>Para ver si la ejecuci&#243;n de un c&#243;digo produce pinning tenemos la opci&#243;n:  <code>-Djdk.tracePinnedThreads=full.</code></p><p>Para el experimento hemos ejecutado el c&#243;digo anterior, con la flag activa en la JDK 21 y 24, adem&#225;s visualizamos la ejecuci&#243;n con la herramienta de profiling <a href="https://visualvm.github.io/">Visual VM</a>.</p><h2>Java 21: Con pinning</h2><p>Para la prueba con Java 21 hemos usado la siguiente versi&#243;n.</p><pre><code>openjdk version "21.0.6" 2025-01-21 LTS
OpenJDK Runtime Environment Corretto-21.0.6.7.1 (build 21.0.6+7-LTS)
OpenJDK 64-Bit Server VM Corretto-21.0.6.7.1 (build 21.0.6+7-LTS, mixed mode, sharing)</code></pre><p>Al ejecutar el bloque de c&#243;digo en la salida est&#225;ndar tenemos:</p><pre><code><code>VirtualThread[#27]/runnable@ForkJoinPool-1-worker-2
VirtualThread[#27]/runnable@ForkJoinPool-1-worker-2 reason:MONITOR
    java.base/java.lang.VirtualThread$VThreadContinuation.onPinned(VirtualThread.java:199)
    java.base/jdk.internal.vm.Continuation.onPinned0(Continuation.java:393)
    java.base/java.lang.VirtualThread.parkNanos(VirtualThread.java:635)
    java.base/java.lang.VirtualThread.sleepNanos(VirtualThread.java:807)
    java.base/java.lang.Thread.sleep(Thread.java:507)
    com.yoandypv.vt.PinnedThreadsExample.lambda$sample2$1(PinnedThreadsExample.java:50) &lt;== monitors:1
    java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
    java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
    java.base/java.lang.VirtualThread.run(VirtualThread.java:329)
VirtualThread[#33]/runnable@ForkJoinPool-1-worker-8
VirtualThread[#35]/runnable@ForkJoinPool-1-worker-8
VirtualThread[#32]/runnable@ForkJoinPool-1-worker-7
VirtualThread[#37]/runnable@ForkJoinPool-1-worker-7
......</code></code></pre><p>Esta parte es importante:</p><pre><code><code>VirtualThread[#27]/runnable@ForkJoinPool-1-worker-2 reason:MONITOR
    java.base/java.lang.VirtualThread$VThreadContinuation.onPinned(VirtualThread.java:199)</code></code></pre><p>Nos indica que en la ejecuci&#243;n quedaron hilos anclados, espec&#237;ficamente que el virtual thread #27 qued&#243; anclado al carrier thread worker-2. Esto es un problema porque el hilo permanece ocupado y no se libera para tomar otro trabajo, el problema con esto es que afecta la escalabilidad prometida con hilos virtuales.</p><p>En el c&#243;digo podr&#225;s ver <code>Continuation.onPinned </code>esto es fundamental, los hilos virtuales se ejecutan gracias a Continuation, que es en verdad la gran revoluci&#243;n a mi entender de Loom, los virtual threads son solo una aplicaci&#243;n de la Continuations. En otro art&#237;culo hablaremos de esto.</p><p>Si vemos esta ejecuci&#243;n en VisualVM se ve as&#237;:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!m5oN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ce42b15-b6df-4345-a3d7-bf7d5f77f909_2244x1146.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!m5oN!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ce42b15-b6df-4345-a3d7-bf7d5f77f909_2244x1146.png 424w, /__u/substackcdn.com/image/fetch/$s_!m5oN!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ce42b15-b6df-4345-a3d7-bf7d5f77f909_2244x1146.png 848w, /__u/substackcdn.com/image/fetch/$s_!m5oN!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ce42b15-b6df-4345-a3d7-bf7d5f77f909_2244x1146.png 1272w, /__u/substackcdn.com/image/fetch/$s_!m5oN!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ce42b15-b6df-4345-a3d7-bf7d5f77f909_2244x1146.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!m5oN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ce42b15-b6df-4345-a3d7-bf7d5f77f909_2244x1146.png" width="1456" height="744" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9ce42b15-b6df-4345-a3d7-bf7d5f77f909_2244x1146.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:744,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:301747,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sacavix.substack.com/i/159525211?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ce42b15-b6df-4345-a3d7-bf7d5f77f909_2244x1146.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!m5oN!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ce42b15-b6df-4345-a3d7-bf7d5f77f909_2244x1146.png 424w, /__u/substackcdn.com/image/fetch/$s_!m5oN!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ce42b15-b6df-4345-a3d7-bf7d5f77f909_2244x1146.png 848w, /__u/substackcdn.com/image/fetch/$s_!m5oN!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ce42b15-b6df-4345-a3d7-bf7d5f77f909_2244x1146.png 1272w, /__u/substackcdn.com/image/fetch/$s_!m5oN!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ce42b15-b6df-4345-a3d7-bf7d5f77f909_2244x1146.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Lo m&#225;s importante es que los carrier thread (workers del 1 a 8) quedan durante la ejecuci&#243;n en estado WAIT, lo que efectivamente representa un bloqueo. El problema del pinning esta presente.</p><h2>Java 24: Bye bye pinning</h2><p>Con la JDK 24 ejecutamos el experimento con el mismo ejemplo, los datos espec&#237;ficos de la JDK son: </p><pre><code><code>openjdk version "24" 2025-03-18
OpenJDK Runtime Environment Corretto-24.0.0.36.2 (build 24+36-FR)
OpenJDK 64-Bit Server VM Corretto-24.0.0.36.2 (build 24+36-FR, mixed mode, sharing)</code></code></pre><p>Al ejecutar el bloque de c&#243;digo en la salida est&#225;ndar tenemos:</p><pre><code><code>VirtualThread[#26]/runnable@ForkJoinPool-1-worker-1
VirtualThread[#103]/runnable@ForkJoinPool-1-worker-1
VirtualThread[#132]/runnable@ForkJoinPool-1-worker-2
VirtualThread[#131]/runnable@ForkJoinPool-1-worker-2
VirtualThread[#134]/runnable@ForkJoinPool-1-worker-2
VirtualThread[#133]/runnable@ForkJoinPool-1-worker-2
VirtualThread[#130]/runnable@ForkJoinPool-1-worker-1
VirtualThread[#129]/runnable@ForkJoinPool-1-worker-2
VirtualThread[#128]/runnable@ForkJoinPool-1-worker-2
VirtualThread[#127]/runnable@ForkJoinPool-1-worker-1
....</code></code></pre><p>El primer punto a se&#241;alar es que no salt&#243; el log que indica pinning al usar la flag. Si vamos al VisualVM se visualiza de esta manera:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!8fyf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5895839-b9f2-4f64-9e6b-2860779032f1_2224x1054.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!8fyf!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5895839-b9f2-4f64-9e6b-2860779032f1_2224x1054.png 424w, /__u/substackcdn.com/image/fetch/$s_!8fyf!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5895839-b9f2-4f64-9e6b-2860779032f1_2224x1054.png 848w, /__u/substackcdn.com/image/fetch/$s_!8fyf!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5895839-b9f2-4f64-9e6b-2860779032f1_2224x1054.png 1272w, /__u/substackcdn.com/image/fetch/$s_!8fyf!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5895839-b9f2-4f64-9e6b-2860779032f1_2224x1054.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!8fyf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5895839-b9f2-4f64-9e6b-2860779032f1_2224x1054.png" width="1456" height="690" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b5895839-b9f2-4f64-9e6b-2860779032f1_2224x1054.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:690,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:288036,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sacavix.substack.com/i/159525211?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5895839-b9f2-4f64-9e6b-2860779032f1_2224x1054.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!8fyf!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5895839-b9f2-4f64-9e6b-2860779032f1_2224x1054.png 424w, /__u/substackcdn.com/image/fetch/$s_!8fyf!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5895839-b9f2-4f64-9e6b-2860779032f1_2224x1054.png 848w, /__u/substackcdn.com/image/fetch/$s_!8fyf!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5895839-b9f2-4f64-9e6b-2860779032f1_2224x1054.png 1272w, /__u/substackcdn.com/image/fetch/$s_!8fyf!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb5895839-b9f2-4f64-9e6b-2860779032f1_2224x1054.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Los carrier threads se muestran en estado Parked. El estado parked nos indica en este contexto que el scheduler desmont&#243; el hilo virtual evitando el bloqueo del hilo de plataforma, carrier thread fue &#8220;aparcado&#8220; y podr&#237;a recibir m&#225;s trabajo. </p><p>Como dijo Neil Armstrong: </p><blockquote><p>Ese es un peque&#241;o paso para el hombre; un gran salto para la humanidad.</p></blockquote><h3>Consideraciones</h3><p>A&#250;n no tengo del todo comprobado que el problema del pinning este resuelto en otros escenarios como los relacionados con JNI, tendremos que esperar a que madure un poco esta nueva versi&#243;n y sea sometida a m&#225;s pruebas. Pero ya es un salto fundamental que lo hayan resuelto para <code>synchronized</code>.</p><p>Nos vemos en el pr&#243;ximo, comparte si crees puede ayudar a otros a aprender.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Suscribirse&quot;,&quot;language&quot;:&quot;es&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">&#161;Gracias por leer SACAViX's Substack! Suscr&#237;bete gratis para recibir nuevos posts y apoyar mi trabajo.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Escribe tu correo electr&#243;nico..." tabindex="-1"><input type="submit" class="button primary" value="Suscribirse"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[MySQL por dentro: cómo se almacenan y buscan los datos.]]></title><description><![CDATA[Un an&#225;lisis profundo sobre como funciona MySQL internamente. Mejorando el performance.]]></description><link>https://sacavix.substack.com/p/mysql-por-dentro-como-se-almacenan</link><guid isPermaLink="false">https://sacavix.substack.com/p/mysql-por-dentro-como-se-almacenan</guid><dc:creator><![CDATA[Yoandy]]></dc:creator><pubDate>Sun, 16 Feb 2025 21:28:11 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!fxvd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24545823-be2e-4c98-8e84-3026d74a08ff_610x578.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>La persistencia de datos es clave en la &#233;poca moderna, existen dos enfoques principales cuando se habla de bases de datos: las SQL y NoSQL (por cierto NoSQL no significa No SQL, sino Not Only SQL, ah&#237; te dejo el dato, volvamos al art&#237;culo).</p><p>Las bases de datos SQL surgieron como una manera para estructurar adecuada y normalizada-mente la informaci&#243;n en una &#233;poca donde el almacenamiento era muy costoso y la normalizaci&#243;n era una soluci&#243;n para el ahorro de espacio. A&#250;n con el creciente abaratamiento del almacenamiento son mayor&#237;a probablemente los casos de uso donde un motor SQL tradicional como MySQL o Postgres se ajuste mejor.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Suscribirse&quot;,&quot;language&quot;:&quot;es&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">&#161;Gracias por leer SACAViX's Substack! Suscr&#237;bete gratis para recibir nuevos posts y apoyar mi trabajo.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Escribe tu correo electr&#243;nico..." tabindex="-1"><input type="submit" class="button primary" value="Suscribirse"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Sin embargo muchos desarrolladores solo usan el motor de base de datos y algunos no tienen claro como funciona por dentro. En esta entrada te intentare explicar como funciona y se almacena la informaci&#243;n en SQL, en este caso tomaremos como punto de referencia MySQL con InnoDB.</p><p>Aprender&#225;s:</p><ul><li><p>&#191;C&#243;mo se guarda la informaci&#243;n en MySQL/InnoDB?</p></li><li><p>&#191;Qu&#233; es una p&#225;gina en el contexto de MySQL?</p></li><li><p>&#191;C&#243;mo se organizan internamente los datos?</p></li><li><p>&#191;Qu&#233; es un &#237;ndice y cual es su importancia?</p></li><li><p>&#191;Cu&#225;les son los tipos de &#237;ndices que existen?</p></li><li><p>&#191;C&#243;mo funcionan los &#237;ndices internamente?</p></li><li><p>&#191;C&#243;mo podemos analizar las consultas y optimizarlas?</p></li></ul><p>Te invito a continuar leyendo este interesante art&#237;culo en mi blog personal. Es gratis.</p><p>Me tom&#243; bastante tiempo hacerlo, pero aprend&#237; y me divert&#237; mucho, espero lo disfrutes.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://sacavix.com/2025/02/analizando-el-rendimiento-de-las-consultas-en-mysql-una-mirada-profunda/" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!fxvd!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24545823-be2e-4c98-8e84-3026d74a08ff_610x578.png 424w, /__u/substackcdn.com/image/fetch/$s_!fxvd!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24545823-be2e-4c98-8e84-3026d74a08ff_610x578.png 848w, /__u/substackcdn.com/image/fetch/$s_!fxvd!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24545823-be2e-4c98-8e84-3026d74a08ff_610x578.png 1272w, /__u/substackcdn.com/image/fetch/$s_!fxvd!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24545823-be2e-4c98-8e84-3026d74a08ff_610x578.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!fxvd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24545823-be2e-4c98-8e84-3026d74a08ff_610x578.png" width="610" height="578" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/24545823-be2e-4c98-8e84-3026d74a08ff_610x578.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:578,&quot;width&quot;:610,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:82153,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:&quot;https://sacavix.com/2025/02/analizando-el-rendimiento-de-las-consultas-en-mysql-una-mirada-profunda/&quot;,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!fxvd!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24545823-be2e-4c98-8e84-3026d74a08ff_610x578.png 424w, /__u/substackcdn.com/image/fetch/$s_!fxvd!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24545823-be2e-4c98-8e84-3026d74a08ff_610x578.png 848w, /__u/substackcdn.com/image/fetch/$s_!fxvd!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24545823-be2e-4c98-8e84-3026d74a08ff_610x578.png 1272w, /__u/substackcdn.com/image/fetch/$s_!fxvd!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24545823-be2e-4c98-8e84-3026d74a08ff_610x578.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Lee el art&#237;culo completo <a href="https://sacavix.com/2025/02/analizando-el-rendimiento-de-las-consultas-en-mysql-una-mirada-profunda/">ac&#225;</a>.</p><p>Si quieres colaborar para ayudarme a crear m&#225;s contenido como este considera unirte como colaborador en <a href="https://www.patreon.com/c/sacavix_tech">Patreon</a>.</p><p>Tambi&#233;n me puedes seguir en <a href="https://www.youtube.com/@SACAViXTech/featured">Youtube</a>.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Suscribirse&quot;,&quot;language&quot;:&quot;es&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">&#161;Gracias por leer SACAViX's Substack! Suscr&#237;bete gratis para recibir nuevos posts y apoyar mi trabajo.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Escribe tu correo electr&#243;nico..." tabindex="-1"><input type="submit" class="button primary" value="Suscribirse"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[El problema de los hilos virtuales]]></title><description><![CDATA[En java 21 se liberaron los hilos virtuales bajo la sombrilla de Loom, pero los pinned threads no dejan alcanzar todas sus ventajas.]]></description><link>https://sacavix.substack.com/p/el-problema-de-los-hilos-virtuales</link><guid isPermaLink="false">https://sacavix.substack.com/p/el-problema-de-los-hilos-virtuales</guid><dc:creator><![CDATA[Yoandy]]></dc:creator><pubDate>Sun, 24 Nov 2024 20:32:07 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!-mEd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a2023cc-e992-48c0-99aa-c989d0b72479_755x899.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Los hilos virtuales (JEP 444) llegaron a Java en forma estable con la JDK 21, pero en realidad a&#250;n no son del todo confiables, al menos no son compatibles con todas las librer&#237;as existentes, entonces para usarlo en ambientes productivos y que el resultado sea el esperado debe tenerse en cuenta el posible problema que puede traer uso, nos referimos al tema del anclaje o pinning. </p><p><strong>Nota</strong>: El tema del pinning no es un invalidante para usar hilos virtuales, pero si atentan contra su buen desempe&#241;o.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Suscribirse&quot;,&quot;language&quot;:&quot;es&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading SACAViX's Substack! Subscribe for free to receive new posts.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Escribe tu correo electr&#243;nico..." tabindex="-1"><input type="submit" class="button primary" value="Suscribirse"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Los hilos virtuales son un modelo de concurrencia novedoso para Java en el cu&#225;l el manejo de los hilos deja de ser controlado por el sistema operativo y el control es tomado por la JVM, el menos el control de los hilos virtuales en s&#237;.</p><p>Un hilo virtual se mapea 1 a muchos con respecto a los hilos de plataforma, permitiendo una mayor escalabilidad, cuando el hilo virtual no est&#225; en uso, el mismo se &#8220;desmonta&#8220; del hilo de plataforma y lo cede para que otro hilo virtual pueda ser &#8220;montado&#8220; y ejecutado. </p><p>El problema de los hilos pinned se da cuando bajo ciertas condiciones el hilo virtual queda montado en su carried thread, cuesti&#243;n que puede terminar afectando la escalabilidad.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!-mEd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a2023cc-e992-48c0-99aa-c989d0b72479_755x899.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!-mEd!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a2023cc-e992-48c0-99aa-c989d0b72479_755x899.png 424w, /__u/substackcdn.com/image/fetch/$s_!-mEd!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a2023cc-e992-48c0-99aa-c989d0b72479_755x899.png 848w, /__u/substackcdn.com/image/fetch/$s_!-mEd!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a2023cc-e992-48c0-99aa-c989d0b72479_755x899.png 1272w, /__u/substackcdn.com/image/fetch/$s_!-mEd!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a2023cc-e992-48c0-99aa-c989d0b72479_755x899.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!-mEd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a2023cc-e992-48c0-99aa-c989d0b72479_755x899.png" width="755" height="899" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1a2023cc-e992-48c0-99aa-c989d0b72479_755x899.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:899,&quot;width&quot;:755,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:115913,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!-mEd!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a2023cc-e992-48c0-99aa-c989d0b72479_755x899.png 424w, /__u/substackcdn.com/image/fetch/$s_!-mEd!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a2023cc-e992-48c0-99aa-c989d0b72479_755x899.png 848w, /__u/substackcdn.com/image/fetch/$s_!-mEd!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a2023cc-e992-48c0-99aa-c989d0b72479_755x899.png 1272w, /__u/substackcdn.com/image/fetch/$s_!-mEd!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1a2023cc-e992-48c0-99aa-c989d0b72479_755x899.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>En la siguiente entrada de nuestro blog explicamos en profundidad este tema con un ejemplo, aprender&#225;s como detectar esta situaci&#243;n y como podemos evitarla.</p><p>Te invito a que le des una lectura completa a este art&#237;culo: <a href="https://sacavix.com/2024/11/los-hilos-virtuales-y-el-problema-del-pinning/">Los hilos virtuales y el problema del &#8220;pinning&#8221;</a>.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/p/el-problema-de-los-hilos-virtuales?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Compartir&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/sacavix.substack.com/p/el-problema-de-los-hilos-virtuales?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share"><span>Compartir</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[Java 23 ha sido liberado]]></title><description><![CDATA[Java 23 fue liberado, pong&#225;monos al d&#237;a con lo que trae esta nueva versi&#243;n.]]></description><link>https://sacavix.substack.com/p/java-23-ha-sido-liberado</link><guid isPermaLink="false">https://sacavix.substack.com/p/java-23-ha-sido-liberado</guid><dc:creator><![CDATA[Yoandy]]></dc:creator><pubDate>Sun, 22 Sep 2024 19:41:21 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!sYBS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928e6cd8-4fab-4a89-86a1-ba997ee5a82f_1133x473.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!sYBS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928e6cd8-4fab-4a89-86a1-ba997ee5a82f_1133x473.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!sYBS!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928e6cd8-4fab-4a89-86a1-ba997ee5a82f_1133x473.png 424w, /__u/substackcdn.com/image/fetch/$s_!sYBS!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928e6cd8-4fab-4a89-86a1-ba997ee5a82f_1133x473.png 848w, /__u/substackcdn.com/image/fetch/$s_!sYBS!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928e6cd8-4fab-4a89-86a1-ba997ee5a82f_1133x473.png 1272w, /__u/substackcdn.com/image/fetch/$s_!sYBS!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928e6cd8-4fab-4a89-86a1-ba997ee5a82f_1133x473.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!sYBS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928e6cd8-4fab-4a89-86a1-ba997ee5a82f_1133x473.png" width="1133" height="473" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/928e6cd8-4fab-4a89-86a1-ba997ee5a82f_1133x473.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:473,&quot;width&quot;:1133,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:165418,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!sYBS!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928e6cd8-4fab-4a89-86a1-ba997ee5a82f_1133x473.png 424w, /__u/substackcdn.com/image/fetch/$s_!sYBS!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928e6cd8-4fab-4a89-86a1-ba997ee5a82f_1133x473.png 848w, /__u/substackcdn.com/image/fetch/$s_!sYBS!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928e6cd8-4fab-4a89-86a1-ba997ee5a82f_1133x473.png 1272w, /__u/substackcdn.com/image/fetch/$s_!sYBS!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F928e6cd8-4fab-4a89-86a1-ba997ee5a82f_1133x473.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Esta semana que reci&#233;n finaliza Oracle lanz&#243; Java 23. La versi&#243;n m&#225;s reciente de la JDK llega con un importante n&#250;mero de novedades que como desarrollador deber&#237;as conocer, eso si, la mayor&#237;a de las cosas llegan en &#8220;Preview&#8221;. </p><p>Fueron presentadas al menos 7 interesantes JDK Enhancement Proposal (JEP) en esta nueva versi&#243;n que no es LTS por lo que no est&#225; recomendada para entornos productivos. Por otro lado al estar muchas de las novedades en fase de preview tampoco se recomiendan para comenzar a usar.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Suscribirse&quot;,&quot;language&quot;:&quot;es&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">&#161;Gracias por leer SACAViX's Substack! Suscr&#237;bete gratis para recibir nuevos posts y apoyar mi trabajo.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Escribe tu correo electr&#243;nico..." tabindex="-1"><input type="submit" class="button primary" value="Suscribirse"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Sin embargo es importante estar al d&#237;a, porque lo que hoy sale como preview en una versi&#243;n no LTS es muy probable en la pr&#243;xima LTS (que ser&#225; Java 25, en el 2025) sea ya una feature definitiva de Java.</p><p>A continuaci&#243;n te muestro las novedades mas relevantes, pero en el <a href="https://sacavix.com/2024/09/java-23-esta-ahora-disponible/">blog </a>he elaborado un art&#237;culo con ejemplos en c&#243;digo donde vemos todo esto en profundidad. Lee el articulo completo <a href="https://sacavix.com/2024/09/java-23-esta-ahora-disponible/">ac&#225;</a>.</p><p><strong>Se liberan las siguientes JEP:</strong></p><ul><li><p>JEP 467 - Java doc ahora soporta markdown.</p></li><li><p>JEP 474 - ZGenerational model ser&#225; usado por defecto cuando se use ZGC . <br></p></li></ul><p><strong>Contin&#250;an en fase preview:</strong></p><ul><li><p>JEP 455 - Tipos primitos en patrones, switch e instanceof.</p></li><li><p>JEP 466 - Cass File API.</p></li><li><p>JEP 473 - Stream Gatherers, para crear operaciones intermedias custom cuando manejemos streams de datos.</p></li><li><p>JEP 476 - Import modules. Tiene como objetivo poder importar de forma simple todos los m&#243;dulos exportados en un paquete (disponibles seg&#250;n su nivel de visibilidad.)</p></li><li><p>JEP 480 - Concurrencia estructurada (forma parte del <a href="https://www.youtube.com/playlist?list=PLi_K_8CU7k82cXcVgNlAOhS1fegx_YfG5">proyecto Loom</a>)</p></li><li><p>JEP 481 - Scoped Values  (forma parte del<a href="https://www.youtube.com/playlist?list=PLi_K_8CU7k82cXcVgNlAOhS1fegx_YfG5"> proyecto Loom</a>)</p></li><li><p>JEP 482 - Constructores flexibles (Antes llamado instrucciones antes de super)<br></p></li></ul><p><strong>Se deprecan:</strong></p><ul><li><p>JEP 471 - (memory access methods) sun.misc.Unsafe se marco como deprecado y sera movido en el futuro. El reemplazo para sun.micUnsafe fue introducido en Java 9 con VarHandle API y en 22 se complemento con Foreign Function y Memory API.</p></li><li><p>Retiradas las plantillas de cadena introducidas en Java 21 para un redise&#241;o.</p><p></p></li></ul><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/p/java-23-ha-sido-liberado?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Compartir&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/sacavix.substack.com/p/java-23-ha-sido-liberado?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share"><span>Compartir</span></a></p><p></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Suscribirse&quot;,&quot;language&quot;:&quot;es&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">&#161;Gracias por leer SACAViX's Substack! Suscr&#237;bete gratis para recibir nuevos posts y apoyar mi trabajo.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Escribe tu correo electr&#243;nico..." tabindex="-1"><input type="submit" class="button primary" value="Suscribirse"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[EP02: PACELC en los sistemas de bases de datos (+ Cache en Spring con Caffeine)]]></title><description><![CDATA[En este episodio hablamos sobre como PACELC extiende el teorema CAP para ayudarnos a entender el funcionamiento de los sistemas de persistencia distribuidos que agregan particiones.]]></description><link>https://sacavix.substack.com/p/ep02-pacelc-en-los-sistemas-de-bases</link><guid isPermaLink="false">https://sacavix.substack.com/p/ep02-pacelc-en-los-sistemas-de-bases</guid><dc:creator><![CDATA[Yoandy]]></dc:creator><pubDate>Sat, 01 Jun 2024 23:14:38 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!puFC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa778ba44-4ae0-439b-bea9-3ed4d8826141_636x371.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>El objetivo principal de implementar un sistema con un enfoque de arquitectura basado en microservicios es agregar la posibilidad de escalar cuando las capacidades del negocio lo requieran. Uno de los problemas radica en que para escalar desde el punto de vista del software no solo basta con escalar las aplicaciones en s&#237;, se requiere adem&#225;s escalar los sistemas de persistencia (bases de datos).</p><p>Para lograr mecanismos de persistencia escalables se requiere de la adopci&#243;n de tecnolog&#237;as de almacenamiento de datos que as&#237; lo permitan. La elecci&#243;n incorrecta de un sistema de base de datos en etapas tempranas del desarrollo puede traer consigo verdaderos dolores de cabeza en el futuro.</p><p>Para elegir la base de datos deben tenerse en cuenta 3 factores principales: disponibilidad, consistencia y tolerancia a particiones que pueden tener lugar en la red de datos. Un sistema de consistencia escalable necesita ser distribuido con todo el reto que esto lleva consigo. La conjetura de Brewer convertida en teorema CAP fue extendida con el teorema PACELC por Daniel J. Abadi al incluir consideraciones de latencia y consistencia durante condiciones normales de operaci&#243;n.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!puFC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa778ba44-4ae0-439b-bea9-3ed4d8826141_636x371.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!puFC!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa778ba44-4ae0-439b-bea9-3ed4d8826141_636x371.png 424w, /__u/substackcdn.com/image/fetch/$s_!puFC!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa778ba44-4ae0-439b-bea9-3ed4d8826141_636x371.png 848w, /__u/substackcdn.com/image/fetch/$s_!puFC!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa778ba44-4ae0-439b-bea9-3ed4d8826141_636x371.png 1272w, /__u/substackcdn.com/image/fetch/$s_!puFC!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa778ba44-4ae0-439b-bea9-3ed4d8826141_636x371.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!puFC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa778ba44-4ae0-439b-bea9-3ed4d8826141_636x371.png" width="636" height="371" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a778ba44-4ae0-439b-bea9-3ed4d8826141_636x371.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:371,&quot;width&quot;:636,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:40919,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!puFC!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa778ba44-4ae0-439b-bea9-3ed4d8826141_636x371.png 424w, /__u/substackcdn.com/image/fetch/$s_!puFC!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa778ba44-4ae0-439b-bea9-3ed4d8826141_636x371.png 848w, /__u/substackcdn.com/image/fetch/$s_!puFC!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa778ba44-4ae0-439b-bea9-3ed4d8826141_636x371.png 1272w, /__u/substackcdn.com/image/fetch/$s_!puFC!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa778ba44-4ae0-439b-bea9-3ed4d8826141_636x371.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>En este art&#237;culo en nuestro <a href="https://sacavix.com/2024/06/que-es-pacelc/">blog SACAViX</a> te damos toda la explicaci&#243;n.</p><p><strong>&#191; Te gusta el contenido que publicamos ?</strong></p><p>Si es as&#237; considera ayudarme compartiendo este contenido en tus redes sociales y considera unirte como miembro en <a href="https://www.youtube.com/channel/UCRfR3e3wnN4qzesjajbgu1Q/join">Youtube</a> o <a href="https://www.patreon.com/sacavix_tech/membership">Patreon</a>.</p><h1>Cache en Spring Boot con Caffeine</h1><p>Caffeine es una de las implementaciones de cache m&#225;s populares en el mundo Java, e es una biblioteca de cach&#233; de alto rendimiento, popular por su baja latencia y alta capacidad de manejo. Ofrece configuraci&#243;n flexible, incluyendo tama&#241;o m&#225;ximo, pol&#237;ticas de expiraci&#243;n y estrategias de manejo de memoria. </p><p>Soporta carga as&#237;ncrona de datos, expiraci&#243;n basada en tiempo desde el &#250;ltimo acceso o escritura, y proporciona m&#233;tricas detalladas para monitoreo del rendimiento. Caffeine es ideal para aplicaciones que requieren acceso r&#225;pido y eficiente a datos en memoria.</p><p>En nuestro canal de Youtube publicamos un interesante v&#237;deo esta semana con un ejemplo pr&#225;ctico de como integrar esta librer&#237;a con Spring Boot, te invito a que le des una mirada y sumes esta tecnolog&#237;a a tu caja de herramientas como desarrollador.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://youtu.be/HOQs2BYiPlQ" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!cUqP!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe920a40a-e813-475d-a14b-8f77ac2f4c4d_1280x720.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!cUqP!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe920a40a-e813-475d-a14b-8f77ac2f4c4d_1280x720.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!cUqP!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe920a40a-e813-475d-a14b-8f77ac2f4c4d_1280x720.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!cUqP!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe920a40a-e813-475d-a14b-8f77ac2f4c4d_1280x720.jpeg 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!cUqP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe920a40a-e813-475d-a14b-8f77ac2f4c4d_1280x720.jpeg" width="1280" height="720" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e920a40a-e813-475d-a14b-8f77ac2f4c4d_1280x720.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:720,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:144901,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:&quot;https://youtu.be/HOQs2BYiPlQ&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!cUqP!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe920a40a-e813-475d-a14b-8f77ac2f4c4d_1280x720.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!cUqP!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe920a40a-e813-475d-a14b-8f77ac2f4c4d_1280x720.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!cUqP!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe920a40a-e813-475d-a14b-8f77ac2f4c4d_1280x720.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!cUqP!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe920a40a-e813-475d-a14b-8f77ac2f4c4d_1280x720.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Espero que te diviertas aprendiendo con este episodio de la newsletter e invita a tus amigos para que sumen y aprendan tambi&#233;n.</p><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/p/ep02-pacelc-en-los-sistemas-de-bases?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Compartir&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Gracias por leer SACAViX's Substack. Este post es p&#250;blico, as&#237; que si&#233;ntete libre de compartirlo.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/p/ep02-pacelc-en-los-sistemas-de-bases?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Compartir&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/sacavix.substack.com/p/ep02-pacelc-en-los-sistemas-de-bases?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share"><span>Compartir</span></a></p></div><p></p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[EP01: Modelos anémicos y enriquecidos]]></title><description><![CDATA[En este episodio hablamos de modelos an&#233;micos y enriquecidos en la programaci&#243;n orientada a objetos, adem&#225;s incluimos video sobre el manejo de la escalabilidad en los microservicios.]]></description><link>https://sacavix.substack.com/p/ep01-modelos-anemicos-y-enriquecidos</link><guid isPermaLink="false">https://sacavix.substack.com/p/ep01-modelos-anemicos-y-enriquecidos</guid><dc:creator><![CDATA[Yoandy]]></dc:creator><pubDate>Fri, 24 May 2024 19:30:05 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!uGPc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b43ccf0-d23f-4522-824d-d33928f0c6c4_518x617.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>En la programaci&#243;n orientada a objetos, la manera en que dise&#241;amos nuestras clases puede tener un impacto significativo en la calidad y mantenibilidad de nuestro c&#243;digo. Dos enfoques comunes para dise&#241;ar clases son las clases an&#233;micas y las clases enriquecidas. </p><p>En Java en particular es com&#250;n el uso de modelos (entitys), objetos de transferencia de datos (dto) y objetos planos de Java (pojos), al crear este tipo de elementos podemos hacerlo usando simples clases con atributos, getters y setters (clases con anemia) o incluso podemos agregar algo de l&#243;gica (enriquecidas). En este art&#237;culo hablaremos sobre esto.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Suscribirse&quot;,&quot;language&quot;:&quot;es&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">&#161;Gracias por leer SACAViX's Substack! Suscr&#237;bete gratis para recibir nuevas publicaciones y apoyar mi trabajo.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Escribe tu correo electr&#243;nico..." tabindex="-1"><input type="submit" class="button primary" value="Suscribirse"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p><a href="https://sacavix.com/2024/05/modelos-anemicos-vs-enriquecidos/">Seguir leyendo en nuestro blog</a></p><div><hr></div><h1>Escalabilidad en los microservicios</h1><p>Un sistema escalable es aquel que cuenta con una capacidad de adaptaci&#243;n y respuesta con respecto al rendimiento del mismo a medida que aumenta forma significativa el tr&#225;fico del mismo. </p><p>Un sistema es escalable si puede soportar los niveles de calidad de servicio a medida que crece su nivel transaccional. Aunque parezca un concepto claro, la escalabilidad de un sistema es un aspecto complejo e importante del dise&#241;o.</p><p>Existen dos enfoques principales, le escalabilidad vertical y la horizontal.</p><p></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!uGPc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b43ccf0-d23f-4522-824d-d33928f0c6c4_518x617.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!uGPc!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b43ccf0-d23f-4522-824d-d33928f0c6c4_518x617.gif 424w, /__u/substackcdn.com/image/fetch/$s_!uGPc!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b43ccf0-d23f-4522-824d-d33928f0c6c4_518x617.gif 848w, /__u/substackcdn.com/image/fetch/$s_!uGPc!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b43ccf0-d23f-4522-824d-d33928f0c6c4_518x617.gif 1272w, /__u/substackcdn.com/image/fetch/$s_!uGPc!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b43ccf0-d23f-4522-824d-d33928f0c6c4_518x617.gif 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!uGPc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b43ccf0-d23f-4522-824d-d33928f0c6c4_518x617.gif" width="518" height="617" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7b43ccf0-d23f-4522-824d-d33928f0c6c4_518x617.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:617,&quot;width&quot;:518,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:4493274,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/gif&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!uGPc!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b43ccf0-d23f-4522-824d-d33928f0c6c4_518x617.gif 424w, /__u/substackcdn.com/image/fetch/$s_!uGPc!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b43ccf0-d23f-4522-824d-d33928f0c6c4_518x617.gif 848w, /__u/substackcdn.com/image/fetch/$s_!uGPc!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b43ccf0-d23f-4522-824d-d33928f0c6c4_518x617.gif 1272w, /__u/substackcdn.com/image/fetch/$s_!uGPc!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7b43ccf0-d23f-4522-824d-d33928f0c6c4_518x617.gif 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><br></p><p>En el siguiente video te explicamos como funciona cada esquema de escalabilidad, los factores que influyen en su implementaci&#243;n y sus desaf&#237;os.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://www.youtube.com/watch?v=ECCgGFru6I0" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!Qtjo!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99d7762e-9754-484a-b83b-973bf3aa5a86_1280x720.png 424w, /__u/substackcdn.com/image/fetch/$s_!Qtjo!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99d7762e-9754-484a-b83b-973bf3aa5a86_1280x720.png 848w, /__u/substackcdn.com/image/fetch/$s_!Qtjo!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99d7762e-9754-484a-b83b-973bf3aa5a86_1280x720.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Qtjo!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99d7762e-9754-484a-b83b-973bf3aa5a86_1280x720.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!Qtjo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99d7762e-9754-484a-b83b-973bf3aa5a86_1280x720.png" width="1280" height="720" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/99d7762e-9754-484a-b83b-973bf3aa5a86_1280x720.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:720,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:616161,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:&quot;https://www.youtube.com/watch?v=ECCgGFru6I0&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!Qtjo!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99d7762e-9754-484a-b83b-973bf3aa5a86_1280x720.png 424w, /__u/substackcdn.com/image/fetch/$s_!Qtjo!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99d7762e-9754-484a-b83b-973bf3aa5a86_1280x720.png 848w, /__u/substackcdn.com/image/fetch/$s_!Qtjo!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99d7762e-9754-484a-b83b-973bf3aa5a86_1280x720.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Qtjo!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F99d7762e-9754-484a-b83b-973bf3aa5a86_1280x720.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/p/ep01-modelos-anemicos-y-enriquecidos?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Compartir&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Gracias por leer SACAViX's Substack. Este post es p&#250;blico, as&#237; que si&#233;ntete libre de compartirlo.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/p/ep01-modelos-anemicos-y-enriquecidos?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Compartir&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/sacavix.substack.com/p/ep01-modelos-anemicos-y-enriquecidos?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share"><span>Compartir</span></a></p></div>]]></content:encoded></item><item><title><![CDATA[Top 7 estilos para comunicar microservicios ]]></title><description><![CDATA[Conoce los 7 estilos de comunicaci&#243;n mas comunes para conectar e intercambiar datos entre aplicaciones.]]></description><link>https://sacavix.substack.com/p/top-7-estilos-para-comunicar-microservicios</link><guid isPermaLink="false">https://sacavix.substack.com/p/top-7-estilos-para-comunicar-microservicios</guid><dc:creator><![CDATA[Yoandy]]></dc:creator><pubDate>Wed, 14 Feb 2024 12:01:31 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!leMf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4613b69-8202-49db-9823-206dc76f0baa_3776x1654.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!leMf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4613b69-8202-49db-9823-206dc76f0baa_3776x1654.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!leMf!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4613b69-8202-49db-9823-206dc76f0baa_3776x1654.png 424w, /__u/substackcdn.com/image/fetch/$s_!leMf!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4613b69-8202-49db-9823-206dc76f0baa_3776x1654.png 848w, /__u/substackcdn.com/image/fetch/$s_!leMf!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4613b69-8202-49db-9823-206dc76f0baa_3776x1654.png 1272w, /__u/substackcdn.com/image/fetch/$s_!leMf!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_webp, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4613b69-8202-49db-9823-206dc76f0baa_3776x1654.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!leMf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4613b69-8202-49db-9823-206dc76f0baa_3776x1654.png" width="1456" height="638" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d4613b69-8202-49db-9823-206dc76f0baa_3776x1654.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:638,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:470049,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!leMf!, /__u/sacavix.substack.com/w_424, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4613b69-8202-49db-9823-206dc76f0baa_3776x1654.png 424w, /__u/substackcdn.com/image/fetch/$s_!leMf!, /__u/sacavix.substack.com/w_848, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4613b69-8202-49db-9823-206dc76f0baa_3776x1654.png 848w, /__u/substackcdn.com/image/fetch/$s_!leMf!, /__u/sacavix.substack.com/w_1272, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4613b69-8202-49db-9823-206dc76f0baa_3776x1654.png 1272w, /__u/substackcdn.com/image/fetch/$s_!leMf!, /__u/sacavix.substack.com/w_1456, /__u/sacavix.substack.com/c_limit, /__u/sacavix.substack.com/f_auto, /__u/sacavix.substack.com/q_auto:good, /__u/sacavix.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd4613b69-8202-49db-9823-206dc76f0baa_3776x1654.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Los 7 mecanismos mas comunes para comunicar aplicaciones son:</p><ul><li><p>SOAP</p></li><li><p>REST</p></li><li><p>GraphQL</p></li><li><p>gRPC</p></li><li><p>Websocket</p></li><li><p>Server Sent Events</p></li><li><p>Publicador / Suscriptor</p></li></ul><p>En <a href="https://sacavix.com/2024/02/top-7-estilos-para-comunicar-microservicios/">esta entrada</a> te explicamos cada uno, sus ventajas, desventajas y cuando usarlos.</p><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/p/top-7-estilos-para-comunicar-microservicios?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Compartir&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Gracias por leer SACAViX's Substack. Este post es p&#250;blico, as&#237; que si&#233;ntete libre de compartirlo.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/p/top-7-estilos-para-comunicar-microservicios?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Compartir&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/sacavix.substack.com/p/top-7-estilos-para-comunicar-microservicios?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share"><span>Compartir</span></a></p></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Suscribirse&quot;,&quot;language&quot;:&quot;es&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">&#161;Gracias por leer SACAViX's Substack! Suscr&#237;bete gratis para recibir nuevas publicaciones y apoyar mi trabajo.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Escribe tu correo electr&#243;nico..." tabindex="-1"><input type="submit" class="button primary" value="Suscribirse"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Bienvenidos a la newsletter de SACAViX]]></title><description><![CDATA[Una newsletter sobre microservicios, dise&#241;o de software, java, spring y programaci&#243;n del lado del backend.]]></description><link>https://sacavix.substack.com/p/bienvenidos-a-la-newsletter-de-sacavix</link><guid isPermaLink="false">https://sacavix.substack.com/p/bienvenidos-a-la-newsletter-de-sacavix</guid><dc:creator><![CDATA[Yoandy]]></dc:creator><pubDate>Mon, 05 Feb 2024 18:31:28 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/add3a1cc-710d-4c7e-84f8-fc7ddb00a013_1920x1080.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/sacavix.substack.com/subscribe"><span>Subscribe now</span></a></p><h2>&#191;Qu&#233; es SACAViX?</h2><p>En SACAViX llevamos varios a&#241;os ayudando a los desarrolladores a crear mejor software, tenemos un blog de tecnolog&#237;a y un canal de Youtube en el que hablamos con cierta regularidad sobre microservicios, dise&#241;o de software y programaci&#243;n del lado del backend. Usamos Java &amp; Spring. </p><h3>1.  &#191;Qu&#233; gano suscribi&#233;ndome a este bolet&#237;n? </h3><p>Es una excelente pregunta &#128587; </p><ul><li><p>Tendr&#225;s acceso directo a informaci&#243;n de desarrollo de software en espa&#241;ol.</p></li><li><p>Hablamos de temas avanzados del desarrollo de software de forma simple, sin complicaciones.</p></li><li><p>Te envi&#225;remos un email cada semana (solo uno) con temas de microservicios, casos de estudio, dise&#241;o de software, Java, Spring o alguna otra novedad que nos resulte interesante.</p></li><li><p>Te vas a mantener informado de los cursos y libros que vamos creando. Ya tenemos un libro que pronto daremos gratuitamente a nuestros suscriptores. Mantente alerta.</p></li><li><p>Estamos escribiendo nuestro segundo libro sobre dise&#241;o e implementaci&#243;n de microservicios tolerantes a fallos, estar ac&#225; de dar&#225; un descuento especial de lanzamiento.</p></li></ul><h3>2. &#191; Qu&#233; puedo hacer para aprender mas ?</h3><p> Lo primero, es suscribirte a este bolet&#237;n , recomendarlo a amigos que le interesen estos temas y seguirnos en nuestro blog <a href="https://sacavix.com">sacavix.com</a> y en nuestro <a href="https://www.youtube.com/channel/UCRfR3e3wnN4qzesjajbgu1Q">canal de Youtube</a>.</p><h3>3. Algunos materiales para empezar </h3><p>Ac&#225; te dejo una lista de recursos que te recomiendo leer o ver que seguro te gustar&#225;n y de paso aprender&#225;s algo nuevo &#128064; .</p><ol><li><p><a href="https://sacavix.com/2023/03/arquitectura-limpia-y-hexagonal-los-principios-y-la-esencia/">Arquitectura limpia y hexagonal: Los principios y la esencia.</a></p></li><li><p>Video explicativo, en detalles sobre como implementar una arquitectura limpia con Spring framework.</p><div id="youtube2-GDXrdZlzdHg" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;GDXrdZlzdHg&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/GDXrdZlzdHg?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div></li><li><p>Explicaci&#243;n con un ejemplo pr&#225;ctico del patron CQRS, uno de los patrones mas ampliamente usados para lograr la escalabilidad.</p></li></ol><div id="youtube2-Tx-x_xackwg" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;Tx-x_xackwg&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/Tx-x_xackwg?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><h3></h3><p>Bueno, te quiero dar la bienvenida a este substack, espero podamos aprender mucho juntos.</p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://sacavix.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Yoandy&#8217;s Substack! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item></channel></rss>