<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Carlos Morejon</title>
    <description>The latest articles on DEV Community by Carlos Morejon (@mutaito).</description>
    <link>https://dev.to/mutaito</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F417056%2F6bd283a8-d5a2-452b-8e42-db6000562b73.jpeg</url>
      <title>DEV Community: Carlos Morejon</title>
      <link>https://dev.to/mutaito</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mutaito"/>
    <language>en</language>
    <item>
      <title>Cómo dividir grandes lotes de búsqueda en Go sin sobrecargar tus servicios</title>
      <dc:creator>Carlos Morejon</dc:creator>
      <pubDate>Tue, 17 Jun 2025 01:18:22 +0000</pubDate>
      <link>https://dev.to/mutaito/como-dividir-grandes-lotes-de-busqueda-en-go-sin-sobrecargar-tus-servicios-2mpe</link>
      <guid>https://dev.to/mutaito/como-dividir-grandes-lotes-de-busqueda-en-go-sin-sobrecargar-tus-servicios-2mpe</guid>
      <description>&lt;p&gt;En la integración de sistemas, es común tener que consultar grandes volúmenes de información a servicios externos que tienen límites estrictos o, simplemente, servicios de terceros donde no tenemos la posibilidad de solicitar un escalamiento.&lt;/p&gt;

&lt;p&gt;En los últimos años, he tenido la "oportunidad" de desarrollar soluciones para migraciones de datos con volúmenes entre 1.000 y 300.000 registros, con datos que dependen de otros sistemas, en su mayoría &lt;em&gt;legacy&lt;/em&gt;. Si a eso le sumamos que muchos de estos no podían ser escalados para soportar consultas muy grandes &lt;em&gt;porque colapsaban&lt;/em&gt;, la situación se volvía un dolor de cabeza.&lt;/p&gt;

&lt;p&gt;Y dado que no era viable una solución trivial como hacer llamadas individuales para cada elemento &lt;em&gt;porque eso no es eficiente, es costoso y poco sostenible&lt;/em&gt; , necesitaba una forma simple y reutilizable de dividir la carga de trabajo o, dicho de otra manera, dividir el problema. A continuación, expongo un caso real:&lt;/p&gt;

&lt;h3&gt;
  
  
  El caso
&lt;/h3&gt;

&lt;p&gt;Tenemos que consultar 500.000 ítems para enriquecer la data que migraremos a los nuevos sistemas. A esta data la llamaremos "Producto". En este caso, solo necesitamos obtener información desde un servicio para guardar un nuevo campo llamado &lt;strong&gt;codigo_viejo&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problemas
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;No podemos ir a buscar estos ítems de uno en uno.
&lt;/li&gt;
&lt;li&gt;El servicio permite búsquedas de hasta un máximo de 100 códigos por &lt;em&gt;request&lt;/em&gt;.
&lt;/li&gt;
&lt;li&gt;El servicio lanza un &lt;em&gt;RateLimit&lt;/em&gt; cuando se hacen muchas peticiones en paralelo.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Diseño de la solución
&lt;/h3&gt;

&lt;p&gt;Creación de una utilidad genérica, ya que este problema se había repetido en varios proyectos.&lt;/p&gt;

&lt;p&gt;Decidí crear una función genérica en Go que permitiera dividir un &lt;em&gt;slice&lt;/em&gt; de cualquier tipo &lt;code&gt;T&lt;/code&gt; en sub-&lt;em&gt;slices&lt;/em&gt; de un tamaño fijo. La idea es que esta utilidad fuera reutilizable en distintos contextos, sin importar el tipo de dato con el que se trabaje ni el uso que se le quiera dar después.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;ChunkSlice&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunkSize&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;[][]&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;chunkSize&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([][]&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;chunkSize&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;chunkSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;chunkSize&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;chunkSize&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;chunks&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Ejemplo de uso&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Supongamos que tienes 500.000 códigos y necesitas procesarlos en bloques de 100, porque es lo máximo que tu servicio externo soporta.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;//Devuelve una []string con los ID que necesitamos consultar.&lt;/span&gt;
&lt;span class="n"&gt;itemCodes&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;getItemsFilesFromCsv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;utils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChunkSlice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itemCodes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;oldToNewCodes&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetCodes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;//Aquí podríamos agregar lógica de reintento o almacenar los errores para manejarlos más tarde.&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error trying to get codes: %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OldCode&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;oldToNewCodes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OldCode&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewCode&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;SaveItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;itemCodes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;oldToNewCodes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En este punto podrías pensar que &lt;code&gt;SaveItems&lt;/code&gt; recibirá demasiada data y que es posible que falle al intentar insertar todo en nuestra base de datos. Dentro de esta función podrías nuevamente dividir la carga usando &lt;code&gt;utils.ChunkSlice(itemCodes, "tamaño ajustado a la capacidad de tu servicio o base de datos")&lt;/code&gt;. En otra ocasión mostraré un ejemplo de esto, enfocado en PostgreSQL y manejo eficiente de conexiones y consultas.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Hablemos un poco de &lt;code&gt;ChunkSlice&lt;/code&gt;:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Genérica:&lt;/strong&gt; Gracias a &lt;code&gt;T any&lt;/code&gt;, sirve para &lt;code&gt;[]string&lt;/code&gt;, &lt;code&gt;[]int&lt;/code&gt;, &lt;code&gt;[]PurchaseOrders&lt;/code&gt;, etc.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Segura:&lt;/strong&gt; Maneja el caso en que &lt;code&gt;chunkSize &amp;lt;= 0&lt;/code&gt;, devolviendo &lt;code&gt;nil&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Eficiente:&lt;/strong&gt; Preasigna capacidad para evitar reasignaciones innecesarias.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([][]&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;chunkSize&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;chunkSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cuando usas &lt;code&gt;make([][]T, 0, capacidad)&lt;/code&gt;, estás diciendo:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Define un slice con largo 0 pero con espacio reservado para X elementos”.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Esto evita que Go tenga que crecer el slice dinámicamente con cada &lt;code&gt;append&lt;/code&gt;, lo que implicaría:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Copiar datos a una nueva ubicación en memoria.
&lt;/li&gt;
&lt;li&gt;Reasignar espacio interno varias veces.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Conclusión&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Pequeñas utilidades como esta pueden parecer triviales, pero en entornos reales marcan la diferencia entre un sistema confiable y uno que colapsa bajo carga, dejándote muchos dolores de cabeza. Además, escribir herramientas genéricas mejora la mantenibilidad del código y fomenta la reutilización. En equipos grandes esto puede ser beneficioso no solo por ahorrar tiempo, sino porque &lt;em&gt;a mi parecer&lt;/em&gt; es más fácil que todos conozcan los &lt;em&gt;utils&lt;/em&gt; más usados en el lugar de trabajo.&lt;/p&gt;

&lt;p&gt;Puedes descargar el repositorio aquí: &lt;a href="https://github.com/mutaito/go-chunk-slice" rel="noopener noreferrer"&gt;Git&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Estoy preparando una versión de este artículo donde mostraré casos reales en los que he utilizado este utilitario con &lt;em&gt;goroutines&lt;/em&gt; para hacer peticiones a servicios, así como algunos ejemplos mapeando datos para insertar en una base de datos. Donde aparecen nuevos desafíos al utilizar concurrencia en Go.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Golang - Hello World on Raspberry Pi using GPIO</title>
      <dc:creator>Carlos Morejon</dc:creator>
      <pubDate>Tue, 16 Aug 2022 01:19:44 +0000</pubDate>
      <link>https://dev.to/mutaito/golang-hello-world-on-raspberry-pi-using-gpio-4b65</link>
      <guid>https://dev.to/mutaito/golang-hello-world-on-raspberry-pi-using-gpio-4b65</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvo8qyysorqjftybszudd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvo8qyysorqjftybszudd.png" alt="Image description" width="748" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
