<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>bits on data</title>
    <link>https://bitsondata.dev/</link>
    <description>\&gt;_ the imposter&#39;s guide to software, data, and life</description>
    <pubDate>Thu, 09 Apr 2026 11:52:40 +0000</pubDate>
    <image>
      <url>https://i.snap.as/vWVqkBBl.png</url>
      <title>bits on data</title>
      <link>https://bitsondata.dev/</link>
    </image>
    <item>
      <title>Humans aren&#39;t going anywhere</title>
      <link>https://bitsondata.dev/humans-arent-going-anywhere?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[The Human in the loop series&#xA;&#xA;This is the first post in a larger human in the loop series that takes a social libertarian stance on AI and argues why humans are relevant if not required for the wave of AI technology we experience today. Although this view contradicts many proponents of the current AI wave, I also want to lend credibility to the utility of generative AI, more specifically the transformer architecture).&#xA;&#xA;In my view, the value of transformers lies more in their extension of information retrieval (i.e. search) technologies as opposed the marketed goal of building a centralized superintelligence that requires large amounts of our data and unsustainable energy demands. I believe if done correctly, this technology could set the stage to bring humans back into the loop for understanding, sharing with consent, and indexing our own precious information unlike the post-Google decades that brought us to surveillance capitalism.  !--more-- &#xA;&#xA;!--emailsub--&#xA;&#xA;The GenAI-to-AGI dichotomy&#xA;&#xA;Generative artificial intelligence (GenAI) is a new wave of artificial intelligence that gained the public&#39;s attention in late 2022 when OpenAI released their ChatGPT application. GenAI uses the transformer architecture to train on large amounts of human language scraped from the internet to develop systems that mimic expected language. Despite their convincing prose, these systems lack any human-like intuition or model of the world, yet are convincing enough to fool most people that there is real understanding and critical thinking based on what is asked of them. This has split public opinion to either see humans as essential for any AI application to work properly, or sees human intelligence as soon-to-be replaced entirely by GenAI. &#xA;&#xA;Artificial General Intelligence (AGI) is a theory suggesting once a computer system is given a sufficient amount of training data, it will be able to outperform human&#39;s in nearly every task and update its own understanding, or world model, without the need for human intervention. Unfortunately the GenAI-to-AGI debate has followed suit to our tribal political environment, causing many to support or oppose the use and development of GenAI altogether.&#xA;&#xA;Gary Marcus, a psychologist at NYU and early skeptic of the utility of today&#39;s LLM-based AI systems, and OpenAI CEO Sam Altman in 2023. Photo by: Andrew Caballero-Reynolds/AFP—Getty Images&#xA;&#xA;AGI skeptics against GenAI investment correctly see current implementations like ChatGPT as exploitative of the humans who created both public and private work without consent, energy hungry and unsustainable, failing to create real value, creating real job loss, and causing mental health problems for its users. &#xA;&#xA;Those who believe GenAI will achieve AGI, tend to focus on theoretical benefits, often see these downsides as inevitable and temporary precursors to AIs pending value towards global human progress. This group is made up of technooptimists who generally lack of accessible education on how GenAI works, are CEOs investing in AI due to groupthink consensus, or are otherwise often profiting off of the AGI narrative in some way or another. &#xA;&#xA;There is of course, a group of technophobic folks that also believes GenAI will become AGI but fall into the anti-GenAI category from fear the technology will evolve itself to wipe out humanity. I won&#39;t lie, there was a week where I felt this way before I educated myself on how the parlor trick worked. We can add this diminishing group to the anti-GenAI group. Let&#39;s then focus on the group who allegedly believes GenAI still could become AGI and view it as a positive for humanity.&#xA;&#xA;In an IBM study that questioned 2000 CEOs, two-thirds of the participants acknowledge they are taking a risk on AI and don&#39;t have a clear understanding of how it would value their company. A little over third of the CEOs believe, &#34;it’s better to be &#39;fast and wrong&#39; than &#39;right and slow&#39; when it comes to technology adoption.&#34; An even smaller group among the GenAI-to-AGI crowd are the techno-elitists who have a vested stake in selling the notion of AGI as this incentivizes large and centralized AI systems become ubiquitous to build monopoly in a domain with few regulations or public understanding. Further, the AGI narrative has spurred on nationalist technology race between the United States, China, and other countries adding a sense of urgency and impetus to ignore the impact of the scaling war on the climate and energy needs of local populations.&#xA;&#xA;Like many in the AI community, I believe generative language models provide value and should be researched — but building AGI superintelligence as the north star with unrestricted resource consumption is where I believe we must draw the line. This is actually where a lot of nuance between the two extreme viewpoints lives, and it&#39;s exactly where the conversation needs to be today. What is important to understand that GenAI is not synonymous with developing superintelligence, displacing humans from jobs, hoarding energy resources from communities, and enabling capitalist to profiteer off our personal information. Despite what either side believes, businesses and investors are running low on patience and there would need to be a drastic boost in performance of the next large model for Silicon Valley to continue this experiment.&#xA;&#xA;The business use case for artificial intelligence&#xA;&#xA;If you look at the economy and the language used by company leadership when describing their investment in AI, the sentiment becomes clear. &#34;65% (of CEOs) say their organization will use automation to address future skill gaps&#34;, which signals their intent to use GenAI to cut their staff budget. Yet, the MIT NANDA project just released a report that found that, &#34;despite $30–40 billion in enterprise investment into GenAI...95% of organizations are getting zero return.&#34;&#xA;&#xA;The report continues to point out;&#xA; &#xA;  the primary factor keeping organizations on the wrong side of the GenAI Divide is the learning gap, tools that don&#39;t learn, integrate poorly, or match workflows. Users prefer ChatGPT for simple tasks, but abandon it for mission-critical work due to its lack of memory. What&#39;s missing is systems that adapt, remember, and evolve, capabilities that define the difference between the two sides of the divide.&#xA;&#xA;What I found most striking about this report is its focus on value to mission-critical workflows that rely on carbon-based employees with domain experience to keep it running. The receipts that AI will actually replace us are not showing up due to the lack of leadership to guide current staff to retrofit AI into production workflows to replace their human intuition. Ironically, this report mentions that most employees showed higher value from simpler interface like ChatGPT over inflexible domain-specific tooling that wasn&#39;t able to adapt to a company&#39;s specific needs. That said, simpler interfaces were only useful for the most basic tasks as opposed to complex or domain-specific work. &#xA;&#xA;The 5% of pilots that were successful in domain-specific workloads, &#34;focus on narrow but high-value use cases, integrate deeply into workflows, and scale through continuous learning rather than broad feature sets. Domain fluency and workflow integration matter more than flashy UX.&#34; This both indicates that there is clearly value to this technology, but the ROI is evident only in narrow AI use cases as opposed to general use cases. Without true ROI to businesses, the centralized super-intelligent system will no longer justify the cost required to fund the costly and energy-inefficient scale of larger and larger models.&#xA;&#xA;!--emailsub--&#xA;&#xA;Where the buck stops&#xA;&#xA;I would like to challenge three sentiments characterized by the current AI companies that are essential to the hype and high valuation:&#xA;&#xA;Language models must be &#34;large&#34; in their training data in order to achieve the value companies see with GenAI today.&#xA;Language models must be proprietary and centralized to perform the best in comparison to open models.&#xA;Language models do not need humans to evolve their understanding of the world.&#xA;&#xA;Does infinite scale mean infinitely better performance?&#xA;&#xA;Cal Newport recounted in his recent New Yorker article, that the original premise set by an OpenAI paper from 2020 argued that language models would likely perform much better at virtually any task if you trained language models on larger data sets. The release of GPT-3 provided compelling evidence as it used ten times more data and saw drastically improved performance over the prior GPT-2 model. This evidence was sufficient enough for venture capital investors to further testing and had a rather convenient narrative to centralize and profit off the technology. &#xA;&#xA;If OpenAI&#39;s theory was correct, they could build a system with the potential to outperform humans on any task, and was only attainable with more money, more computing power, and more data. The continued success of OpenAI&#39;s larger GPT-4 model boosted the momentum of this belief, and had investors throwing money at multiple companies in hopes they would invest in the one that made it to AGI first. &#xA;&#xA;This was only to be followed by years of incremental releases from OpenAI&#39;s competitors on bigger models that barely performed better and OpenAI&#39;s continuous delays of the GPT-5 release. The AI industry began expressing doubt if scaling would consistently provide exponentially growing results. After two and a half years since the GPT-4 release, the unimpressive release of GPT-5 left the question on everyone&#39;s mind, &#34;Was GPT-4 the peak size and performance for transformer-based models?&#34;&#xA;&#xA;This question causes a great deal of tension today as this larger-means-better approach starts to unfold. The entire viability of Nvidia&#39;s surge in valuation lies at the heart of the truth of this scaling power law. If models don&#39;t require increasingly larger datasets to perform better, then Nvidia&#39;s sales will stagnate once companies have a sufficient amount of GPUs to train and fine-tune existing open models. It&#39;s not that Nvidia won&#39;t sell a lot of GPUs in the near future, it&#39;s that the number has a clear finite bound. This can be further constrained as open models provide a clear path to build custom models without the entry fee of starting from scratch. &#xA;&#xA;Open models prove size isn&#39;t always what matters&#xA;&#xA;Open source has become part of the strategic landscape and how companies like Meta diminish OpenAI&#39;s advantage by growing a community around an open alternative as they did with the LLaMa model. Open language models come in all domains and sizes and are widely available on platforms like Kaggle and HuggingFace. Some models obfuscate model training and release model weights for use and limited reuse, while other models open their entire training sets and publish their methods to communities like OpenML or MLCommons. A study from late 2024 compared the performance of open models like LLama 2, Phi 2, and Mistral OpenOrca against OpenAI&#39;s GPT-3.5 and GPT-4, which concluded:&#xA;&#xA;  Open-source models, while showing slightly lower scores in terms of precision and speed, offer an interesting alternative due to their deployment flexibility. For example, Mistral-7b-OpenOrca achieved an 83% exact match and a ROUGE-2 score of 80%, while LLaMA-2 showed a 76% exact match, proving their competitiveness in controlled and secure environments. These open-source models, with their optimized attention mechanisms and adjusted quantization configurations, show that they can compete with proprietary models while allowing companies to customize the models according to their specific needs. These models represent viable and cost-effective solutions for sectors where data privacy and the ability to deploy on private infrastructures are essential.&#xA;&#xA;Open models make it possible for more companies to outperform proprietary models with the use of retrieval augmentation or fine-tuning methods. This begged the question for companies who value cost-efficiency, How well do transformer models work with fine-tuned smaller models and other attention tuning like retrieval augmentation? &#xA;&#xA;Fine-tuned small language models (SLMs) have also begun to outperform large language models from traditional tasks like text classification, to domain-specific edge models and coding tasks with lower operating costs and higher ROI. This fine-tuning and RAG optimization doesn&#39;t come for free, but it has more compounding of benefits for users and avoids lock-in to proprietary models. Of the 5% of domain-specific workloads that succeeded in the MIT study, the collaborations between domain-specialists and AI engineers provided substantial benefit through making the mechanisms of generative AI more transparent to knowledge workers, increasing their trust in these tools and providing more confidence to use them to automate other workflows. Domain specialists can then tweak updates of shifting institutional knowledge to a company&#39;s custom model while bringing more efficient workloads into the scaffold of mission-critical tasks.&#xA;&#xA;It&#39;s hard to say that there will never be a case for large models, perhaps everyone will still want ChatGPT but cheaper and less AGI focused. If more research is placed into improving the performance of small language models towards the optimal performance of GPT-4. Then perhaps this is still a valid, albeit way smaller, use case for these general models that could be offered as a service to more and more companies. This does put a nail in the coffin of mindless scaling as a means to generate higher-value for consumers.&#xA;&#xA;Will SLMs and open models kill centralized or proprietary models?&#xA;&#xA;!--emailsub--&#xA;&#xA;The growing research using open models substantially reduced the overall  necessity and therefore value of a centralized platform. If training does not require substantial investment in a large data center, there would be more long-term value for companies with enough use cases to invest in developing internal or use existing open tools to create and run domain-specific models. As I pointed out in Your Own Private AI, any consumer these days can run open models locally and use RAG to train on their own information. This makes it nearly impossible for a single AI company like OpenAI to become the next Google monopoly in information networks.&#xA;&#xA;In contrast, this opens up room for a new market of smaller proprietary and domain-specific AI in tools. This may look similar to the current proliferation of LLM wrapper companies, with the difference of them taking the time to think through and develop their own foundational models to address specific needs. The models won&#39;t necessarily need to be large, but rather, work incredibly well within their domain. The only centralization of knowledge would exist for that domain, but it is no longer trying to get data from every corner of the planet to solve all problems. This ends up looking like a slightly more generalized version of narrow or traditional AI tooling. This tooling may be so insignificant to any domain, that the application may not even be branded as an AI tool, but rather serving some features of an application much like Markov Chains give us autocorrect on our phones.&#xA;&#xA;What about the job market? &#xA;&#xA;As the narrative that all human value can be produced better and faster by a centralized superintelligence fades into the next AI winter, we are then left with an insecure feeling that AGI didn&#39;t happen this time, but what about the next? Because GenAI was such a convincing parlor trick, it spurred on a lot of conversations and new research across experts in anthropology, neuroscience, computer science, and artificial intelligence. It became very important for us to understand how we humans think, learn, and most importantly, what we take for granted about our organic and specifically human cognition.&#xA;&#xA;I personally took solace in learning that large language models emulate language, but they don&#39;t model a world view, nor are they guided by biological stimulus like emotions that factor into their learning. I&#39;m not saying that computers couldn&#39;t outpace us in some ways, but drove me to question the vast complexity of our own experience that we rarely practice metacognition as we are always thinking much like we always breathe and have a heartbeat. What also becomes clear when looking at this is how early research is on understanding the connection between our thoughts and the biological matter we&#39;ve evolved over millennia. I&#39;d like to share one aspect of human cognition we do understand that already falls outside of what is modeled by a language model, which is, the human nervous system&#39;s role in conscious thought.&#xA;&#xA;Language models can&#39;t emulate human cognition&#xA;&#xA;I recently learned about the Enteric Nervous System (ENS), our &#34;second brain&#34;, which is a mesh-like system of neurons that controls our gastrointestinal track. It can control digestive function entirely on its own without signals from our brain or the central nervous system. It is responsible for 90% of the serotonin and 50% of the dopamine generated in our body which has a large effect on your emotional state. It is why we use the language of &#34;gut&#34; feelings that describe our intuition that guides our decision making. It also sends signals that make us cranky and contributes to worse decision-making when we are hungry.  Because humans aren&#39;t just brains with fingers and we have an entire body that dictate how we think and learn, we must consider the many complex systems of human anatomy factor into our experience when making a choice. This is just one of many things that make human thinking distinct from AI, and why we need to avoid comparing language emulation models to human thought.&#xA;&#xA;If that weren&#39;t enough, there is a much more complex way our behaviors are affected by our environment, culture, social interactions, and the information we consume. When we see AI generate false information with no model to verify it against (i.e. &#34;hallucinates&#34;), it is clear that AI is only working in limited language or sensory dimensions such as image and video to generate something that is plausible or possible, but not likely correct. There&#39;s a lot of great reading in AI papers from the 2000s that aimed to create models like Distributed Cognition not even to build an AI model around it, but simply to create a vocabulary framework around complex cognition seen in animals to clarify different taxonomies of the type of cognition being emulated and which weren&#39;t. It&#39;s a starch reminder about how anthropomorphism and confirmation bias can cause us to be reductive of the complex mechanisms of our cognition. Although it&#39;s still possible that one day we will build AGI that may or may not think like a human, it most definitely won&#39;t be agents or language models. I believe most, if not, all jobs are safe from being replaced from language models.&#xA;&#xA;!--emailsub--&#xA;&#xA;In the post LLM hype, my anticipation will be a rise in the job market. Transformer-based model research will live on in AI academia and in business, and we&#39;ll see an explosion in developments around open training and open data sets. There will be a larger focus on developing domain-specific smaller models with fine-tuning. There will also be a large interest in embedded AI for IOT devices and lower energy consumption. This type of AI economy makes knowledge of every human valuable. It will take time and will require consent if we do this right. &#xA;&#xA;Companies should still invest in AI, but gradually and following the techniques by the few successful companies that have had success. This will involve bringing back the domain experts and human resources AI suggested we could replace, and instead train them on how to effectively use AI in their workflow. As employee knowledge becomes important, companies must prioritize proper documentation and knowledge work. If leaders focus on proper incentives to capture workflows across teams through internal wikis, software logic mapping and validation, ops reports, meeting summaries, etc... GenAI has a lot of potential to lower the burden of time-consuming communication gaps and can provide a lot of information for experts to share knowledge with newcomers and remain productive within their role. This all comes down to telling the right stories around how we enable individuals to do their best work and their unique experiences and talents to shape the larger company organism into a more efficient being.&#xA;&#xA;GenAI and Search&#xA;&#xA;The single greatest pervasive and most influential information technology we know today is search, specifically Google&#39;s search initially powered by PageRank. Though general open source search engines like Solr, Lucene, Elasticsearch, meta search engines like SearXNG, and more modern vector and search hybrids like meilisearch, these only provide the mechanisms of how search works, but are missing the gargantuan amount of data that Google possesses through its search monopoly, data-collecting products, adware metrics, and its highly adopted web browser that feed into both providing context for search. The slow coercive shift of society granting Google its incredible influence over how humans around the globe mentally model and obtain knowledge that form our world views also shaped the way in which netizen&#39;s structure their information to be found.&#xA;&#xA;Much of the GenAI data was procured without consent from the large troves of publicly available data. This ranged from troves of information scraped off of forum sites like, Reddit and StackOverlow, to small sites from individual blogs. All of these were used to feed the large data needs of the LLMs. As we sit here in the aftermath of this technology, I think it&#39;s incredibly important that we reflect on the importance of how we structure our information as a internet society. There is clearly power in the conversations we have and information we produce. It&#39;s important that the exploitative practices of companies like OpenAI and Google drive us towards safeguarding personal information, while ensuring our intellectual assets remain available to the public. We should continue building the consensual information sharing of information to benefit everyone, while safeguarding personal information from private benefactors that can leave the public vulnerable in the current political climate.&#xA;&#xA;Many in open source have started developing open alternatives that enable us to create and use our information on our own terms. Social media alternatives have grown through Fediverse technologies, such as Mastadon) and Pixelfed. There are also user-driven search systems such as USENET that enable users to curate our own search indexes and share it with others. This could both build information netowrks that would create an information economy that expands our existing peer-to-peer blog funding economies. Rather, you would be funded by curating valuable information in your expertise. However, today&#39;s average netizen would consider a system like USENET complex and unusable as it was created before social media and personal phones shapes how users find information. &#xA;&#xA;Much of the current challenge poised to open designers and developers is to match current search mental models with those of a new system, that would also link across sites through semantic web standards. For those who understand how to build these economies can make some early examples themselves and train future generations of individuals and corporations on how to manage and profit off their own open digital gardens. I believe democratizing the ownership of information flow occurs on the internet will break up information bubbles and enable us to share the information we want and maintain our privacy where we want.&#xA;&#xA;Nobody can do that alone, but it is possible if we start to pull our resources together and build something that has the distribution of USENET, the UX search model of Google, the interoperability of the semantic web, and the ability for folks to work on a repository of documents like Wikipedia, but many small wikis that can reference eachother. With proper design and consent mechanisms, humans will want to collectively curate the valuable information in our own heads into shared value for globally interconnected local economies. &#xA;&#xA;We&#39;ll dive more into some of these fundamentals in the rest of this series. The next post will dive into the internals of search technology and its relevance to GenAI.&#xA;&#xA;!--emailsub--]]&gt;</description>
      <content:encoded><![CDATA[<h2 id="the-human-in-the-loop-series" id="the-human-in-the-loop-series">The Human in the loop series</h2>

<p>This is the first post in a larger human in the loop series that takes a social libertarian stance on AI and argues why humans are relevant if not required for the wave of AI technology we experience today. Although this view contradicts many proponents of the current AI wave, I also want to lend credibility to the utility of generative AI, more specifically <a href="https://en.wikipedia.org/wiki/Transformer_(deep_learning_architecture)">the transformer architecture</a>.</p>

<p>In my view, the value of transformers lies more in their extension of information retrieval (i.e. search) technologies as opposed the marketed goal of building a centralized superintelligence that requires large amounts of our data and unsustainable energy demands. I believe if done correctly, this technology could set the stage to bring humans back into the loop for understanding, sharing with consent, and indexing our own precious information unlike the post-Google decades that brought us to surveillance capitalism.  </p>



<h3 id="the-genai-to-agi-dichotomy" id="the-genai-to-agi-dichotomy">The GenAI-to-AGI dichotomy</h3>

<p><a href="https://en.wikipedia.org/wiki/Generative_artificial_intelligence">Generative artificial intelligence (GenAI)</a> is a new wave of artificial intelligence that gained the public&#39;s attention in late 2022 when OpenAI released their ChatGPT application. GenAI uses the transformer architecture to train on large amounts of human language scraped from the internet to develop systems that mimic expected language. Despite their convincing prose, these systems lack any human-like intuition or model of the world, yet are convincing enough to fool most people that there is real understanding and critical thinking based on what is asked of them. This has split public opinion to either <a href="https://garymarcus.substack.com/p/generative-ais-crippling-and-widespread">see humans as essential for any AI application</a> to work properly, or <a href="https://archive.ph/CKYh6">sees human intelligence as soon-to-be replaced</a> entirely by GenAI.</p>

<p><a href="https://en.wikipedia.org/wiki/Artificial_general_intelligence">Artificial General Intelligence (AGI)</a> is a theory suggesting once a computer system is given a sufficient amount of training data, it will be able to outperform human&#39;s in nearly every task and update its own understanding, or world model, without the need for human intervention. Unfortunately the GenAI-to-AGI debate has followed suit to our tribal political environment, causing many to support or oppose the use and development of GenAI altogether.</p>

<p><img src="https://i.snap.as/wWvboSHQ.jpg" alt=""/>
<em>Gary Marcus, a psychologist at NYU and early skeptic of the utility of today&#39;s LLM-based AI systems, and OpenAI CEO Sam Altman in 2023. Photo by: Andrew Caballero-Reynolds/AFP—Getty Images</em></p>

<p>AGI skeptics against GenAI investment correctly see current implementations like ChatGPT as <a href="https://futurism.com/meta-copyrighted-books-no-value">exploitative of the humans who created both public and private work</a> without consent, <a href="https://www.technologyreview.com/2025/05/20/1116272/ai-natural-gas-data-centers-energy-power-plants/">energy hungry</a> and <a href="https://www.technologyreview.com/2025/05/20/1116287/ai-data-centers-nevada-water-reno-computing-environmental-impact/">unsustainable</a>, <a href="https://futurism.com/ceos-return-ai-investments">failing to create real value</a>, creating <a href="https://www.cbsnews.com/news/ai-jobs-layoffs-us-2025/">real job loss</a>, and <a href="https://www.nbcnews.com/tech/tech-news/chatgpt-adds-mental-health-guardrails-openai-announces-rcna222999">causing mental health problems for its users</a>.</p>

<p>Those who believe GenAI will achieve AGI, tend to focus on theoretical benefits, often see these downsides as inevitable and temporary precursors to AIs pending value towards global human progress. This group is made up of technooptimists who generally lack of accessible education on how GenAI works, are CEOs investing in AI due to <a href="https://en.wikipedia.org/wiki/Group_decision-making">groupthink</a> consensus, or are otherwise often profiting off of the AGI narrative in some way or another.</p>

<p>There is of course, a group of technophobic folks that also believes GenAI will become AGI but fall into the anti-GenAI category from fear the technology will evolve itself to wipe out humanity. I won&#39;t lie, there was a week where I felt this way before I educated myself on how the parlor trick worked. We can add this diminishing group to the anti-GenAI group. Let&#39;s then focus on the group who allegedly believes GenAI still could become AGI and view it as a positive for humanity.</p>

<p>In an <a href="https://newsroom.ibm.com/2025-05-06-ibm-study-ceos-double-down-on-ai-while-navigating-enterprise-hurdles">IBM study that questioned 2000 CEOs</a>, two-thirds of the participants acknowledge they are taking a risk on AI and don&#39;t have a clear understanding of how it would value their company. A little over third of the CEOs believe, “it’s better to be &#39;fast and wrong&#39; than &#39;right and slow&#39; when it comes to technology adoption.” An even smaller group among the GenAI-to-AGI crowd are the techno-elitists who have a vested stake in selling the notion of AGI as this incentivizes large and centralized AI systems become ubiquitous to build monopoly in a domain with few regulations or public understanding. Further, the AGI narrative has spurred on nationalist technology race between the United States, China, and other countries adding a sense of urgency and impetus to ignore the impact of the scaling war on the climate and energy needs of local populations.</p>

<p>Like many in the AI community, I believe generative language models provide value and should be researched — but <a href="https://arxiv.org/pdf/2502.03689">building AGI superintelligence as the north star</a> with unrestricted resource consumption is where I believe we must draw the line. This is actually where a lot of nuance between the two extreme viewpoints lives, and it&#39;s exactly where the conversation needs to be today. What is important to understand that GenAI is not synonymous with developing superintelligence, displacing humans from jobs, hoarding energy resources from communities, and enabling capitalist to profiteer off our personal information. Despite what either side believes, businesses and investors are running low on patience and there would need to be a drastic boost in performance of the next large model for Silicon Valley to continue this experiment.</p>

<h2 id="the-business-use-case-for-artificial-intelligence" id="the-business-use-case-for-artificial-intelligence">The business use case for artificial intelligence</h2>

<p>If you look at the economy and the language used by company leadership when describing their investment in AI, the sentiment becomes clear. “65% <a href="https://newsroom.ibm.com/2025-05-06-ibm-study-ceos-double-down-on-ai-while-navigating-enterprise-hurdles">(of CEOs)</a> say their organization will use automation to address future skill gaps”, which signals their intent to use GenAI to cut their staff budget. Yet, the <a href="https://nanda.media.mit.edu/">MIT NANDA project</a> just <a href="https://mlq.ai/media/quarterly_decks/v0.1_State_of_AI_in_Business_2025_Report.pdf">released a report</a> that found that, “despite $30–40 billion in enterprise investment into GenAI...95% of organizations are getting zero return.”</p>

<p><img src="https://i.snap.as/08o9HnYs.png" alt=""/></p>

<p>The report continues to point out;</p>

<blockquote><p>the primary factor keeping organizations on the wrong side of the GenAI Divide is the learning gap, tools that don&#39;t learn, integrate poorly, or match workflows. Users prefer ChatGPT for simple tasks, but abandon it for mission-critical work due to its lack of memory. What&#39;s missing is systems that adapt, remember, and evolve, capabilities that define the difference between the two sides of the divide.</p></blockquote>

<p>What I found most striking about this report is its focus on value to mission-critical workflows that rely on carbon-based employees with domain experience to keep it running. The receipts that AI will actually replace us are not showing up due to the lack of leadership to guide current staff to retrofit AI into production workflows to replace their human intuition. Ironically, this report mentions that most employees showed higher value from simpler interface like ChatGPT over inflexible domain-specific tooling that wasn&#39;t able to adapt to a company&#39;s specific needs. That said, simpler interfaces were only useful for the most basic tasks as opposed to complex or domain-specific work.</p>

<p><img src="https://i.snap.as/AOl5lxck.png" alt=""/></p>

<p>The 5% of pilots that were successful in domain-specific workloads, <em>“focus on narrow but high-value use cases, integrate deeply into workflows, and scale through continuous learning rather than broad feature sets. Domain fluency and workflow integration matter more than flashy UX.”</em> This both indicates that there is clearly value to this technology, but the ROI is evident only in narrow AI use cases as opposed to general use cases. Without true ROI to businesses, the centralized super-intelligent system will no longer justify the cost required to fund the costly and energy-inefficient scale of larger and larger models.</p>



<h2 id="where-the-buck-stops" id="where-the-buck-stops">Where the buck stops</h2>

<p>I would like to challenge three sentiments characterized by the current AI companies that are essential to the hype and high valuation:</p>
<ol><li>Language models must be “large” in their training data in order to achieve the value companies see with GenAI today.</li>
<li>Language models must be proprietary and centralized to perform the best in comparison to open models.</li>
<li>Language models do not need humans to evolve their understanding of the world.</li></ol>

<h3 id="does-infinite-scale-mean-infinitely-better-performance" id="does-infinite-scale-mean-infinitely-better-performance">Does infinite scale mean infinitely better performance?</h3>

<p>Cal Newport recounted <a href="https://archive.ph/HKYjM">in his recent New Yorker article</a>, that the original premise set by <a href="https://arxiv.org/abs/2001.08361">an OpenAI paper from 2020</a> argued that language models would likely perform much better at virtually any task if you trained language models on larger data sets. The release of GPT-3 provided compelling evidence as it used ten times more data and saw drastically improved performance over the prior GPT-2 model. This evidence was sufficient enough for venture capital investors to further testing and had a rather convenient narrative to centralize and profit off the technology.</p>

<p>If OpenAI&#39;s theory was correct, they could build a system with the potential to outperform humans on any task, and was only attainable with more money, more computing power, and more data. The continued success of OpenAI&#39;s larger GPT-4 model boosted the momentum of this belief, and had investors throwing money at multiple companies in hopes they would invest in the one that made it to AGI first.</p>

<p>This was only to be followed by years of incremental releases from OpenAI&#39;s competitors on bigger models that barely performed better and OpenAI&#39;s continuous delays of the GPT-5 release. The AI industry began expressing doubt if scaling would consistently provide exponentially growing results. After two and a half years since the GPT-4 release, <a href="https://tech.yahoo.com/ai/articles/evidence-grows-gpt-5-bit-151457162.html">the unimpressive release of GPT-5</a> left the question on everyone&#39;s mind, “Was GPT-4 the peak size and performance for transformer-based models?”</p>

<p>This question causes a great deal of tension today as this larger-means-better approach starts to unfold. The entire viability of Nvidia&#39;s surge in valuation lies at the heart of the truth of this scaling power law. If models don&#39;t require increasingly larger datasets to perform better, then Nvidia&#39;s sales will stagnate once companies have a sufficient amount of GPUs to train and fine-tune existing open models. It&#39;s not that Nvidia won&#39;t sell a lot of GPUs in the near future, it&#39;s that the number has a clear finite bound. This can be further constrained as open models provide a clear path to build custom models without the entry fee of starting from scratch.</p>

<h4 id="open-models-prove-size-isn-t-always-what-matters" id="open-models-prove-size-isn-t-always-what-matters">Open models prove size isn&#39;t always what matters</h4>

<p>Open source has become part of the <a href="https://blog.matt-rickard.com/p/why-did-meta-open-source-llama-2">strategic landscape and how companies like Meta</a> diminish OpenAI&#39;s advantage by growing a community around an open alternative as they did with the LLaMa model. Open language models come in all domains and sizes and are widely available on platforms like <a href="https://www.kaggle.com/models">Kaggle</a> and <a href="https://huggingface.co/">HuggingFace</a>. Some models obfuscate model training and release model weights for use and limited reuse, while other models open their <a href="https://www.openml.org/search?type=data&amp;status=active">entire training sets</a> and publish their methods to communities like <a href="https://docs.openml.org/">OpenML</a> or <a href="https://mlcommons.org/">MLCommons</a>. <a href="https://arxiv.org/html/2406.13713v2">A study from late 2024</a> compared the performance of open models like LLama 2, Phi 2, and Mistral OpenOrca against OpenAI&#39;s GPT-3.5 and GPT-4, which concluded:</p>

<blockquote><p>Open-source models, while showing slightly lower scores in terms of precision and speed, offer an interesting alternative due to their deployment flexibility. For example, Mistral-7b-OpenOrca achieved an 83% exact match and a ROUGE-2 score of 80%, while LLaMA-2 showed a 76% exact match, proving their competitiveness in controlled and secure environments. These open-source models, with their optimized attention mechanisms and adjusted quantization configurations, show that they can compete with proprietary models while allowing companies to customize the models according to their specific needs. These models represent viable and cost-effective solutions for sectors where data privacy and the ability to deploy on private infrastructures are essential.</p></blockquote>

<p>Open models make it possible for more companies to outperform proprietary models with the use of retrieval augmentation or fine-tuning methods. This begged the question for companies who value cost-efficiency, How well do transformer models work with fine-tuned smaller models and other attention tuning like retrieval augmentation?</p>

<p>Fine-tuned small language models (SLMs) have also begun to outperform large language models from traditional tasks like <a href="https://arxiv.org/abs/2406.08660">text classification</a>, to <a href="https://arxiv.org/abs/2503.01933">domain-specific edge models</a> and <a href="https://arxiv.org/abs/2504.16584">coding tasks</a> with lower operating costs and higher ROI. This fine-tuning and RAG optimization doesn&#39;t come for free, but it has more compounding of benefits for users and avoids lock-in to proprietary models. Of the 5% of domain-specific workloads that succeeded in the MIT study, the collaborations between domain-specialists and AI engineers provided substantial benefit through making the mechanisms of generative AI more transparent to knowledge workers, increasing their trust in these tools and providing more confidence to use them to automate other workflows. Domain specialists can then tweak updates of shifting institutional knowledge to a company&#39;s custom model while bringing more efficient workloads into the scaffold of mission-critical tasks.</p>

<p>It&#39;s hard to say that there will never be a case for large models, perhaps everyone will still want ChatGPT but cheaper and less AGI focused. If more research is placed into improving the performance of small language models towards the optimal performance of GPT-4. Then perhaps this is still a valid, albeit way smaller, use case for these general models that could be offered as a service to more and more companies. This does put a nail in the coffin of mindless scaling as a means to generate higher-value for consumers.</p>

<h2 id="will-slms-and-open-models-kill-centralized-or-proprietary-models" id="will-slms-and-open-models-kill-centralized-or-proprietary-models">Will SLMs and open models kill centralized or proprietary models?</h2>



<p>The growing research using open models substantially reduced the overall  necessity and therefore value of a centralized platform. If training does not require substantial investment in a large data center, there would be more long-term value for companies with enough use cases to invest in developing internal or use existing open tools to create and run domain-specific models. As I pointed out in <a href="https://bitsondata.dev/your-own-private-ai">Your Own Private AI</a>, any consumer these days can run open models locally and use RAG to train on their own information. This makes it nearly impossible for a single AI company like OpenAI to become the next <a href="https://apnews.com/article/google-search-antitrust-case-59114d8bf1dc4c8453c08acaa4051f14">Google monopoly</a> in information networks.</p>

<p>In contrast, this opens up room for a new market of smaller proprietary and domain-specific AI in tools. This may look similar to the current proliferation of LLM wrapper companies, with the difference of them taking the time to think through and develop their own foundational models to address specific needs. The models won&#39;t necessarily need to be large, but rather, work incredibly well within their domain. The only centralization of knowledge would exist for that domain, but it is no longer trying to get data from every corner of the planet to solve all problems. This ends up looking like a slightly more generalized version of narrow or traditional AI tooling. This tooling may be so insignificant to any domain, that the application may not even be branded as an AI tool, but rather serving some features of an application much like Markov Chains give us autocorrect on our phones.</p>

<h3 id="what-about-the-job-market" id="what-about-the-job-market">What about the job market?</h3>

<p><img src="https://i.snap.as/vfTj0tlI.jpg" alt=""/></p>

<p>As the narrative that all human value can be produced better and faster by a centralized superintelligence fades into the next AI winter, we are then left with an insecure feeling that AGI didn&#39;t happen this time, but what about the next? Because GenAI was such a convincing parlor trick, it spurred on a lot of conversations and new research across experts in anthropology, neuroscience, computer science, and artificial intelligence. It became very important for us to understand how we humans think, learn, and most importantly, what we take for granted about our organic and specifically human cognition.</p>

<p>I personally took solace in learning that large language models emulate language, but they don&#39;t model a world view, nor are they guided by biological stimulus like emotions that factor into their learning. I&#39;m not saying that computers couldn&#39;t outpace us in some ways, but drove me to question the vast complexity of our own experience that we rarely practice metacognition as we are always thinking much like we always breathe and have a heartbeat. What also becomes clear when looking at this is how early research is on understanding the connection between our thoughts and the biological matter we&#39;ve evolved over millennia. I&#39;d like to share one aspect of human cognition we do understand that already falls outside of what is modeled by a language model, which is, the human nervous system&#39;s role in conscious thought.</p>

<h4 id="language-models-can-t-emulate-human-cognition" id="language-models-can-t-emulate-human-cognition">Language models can&#39;t emulate human cognition</h4>

<p><a href="https://en.wikipedia.org/wiki/Enteric_nervous_system"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/5/5f/Enteric_Nervous_System.png/330px-Enteric_Nervous_System.png" alt=""/></a></p>

<p>I recently learned about the <a href="https://en.wikipedia.org/wiki/Enteric_nervous_system">Enteric Nervous System (ENS)</a>, our “second brain”, which is a mesh-like system of neurons that controls our gastrointestinal track. It can <a href="https://youtu.be/wmgiHDb215U">control digestive function entirely on its own</a> without signals from our brain or the central nervous system. It is responsible for 90% of the serotonin and 50% of the dopamine generated in our body which has a large effect on your emotional state. It is why we use the language of “gut” feelings that describe our intuition that guides our decision making. It also sends signals that make us cranky and <a href="https://en.wikipedia.org/wiki/Hungry_judge_effect">contributes to worse decision-making when we are hungry</a>.  Because humans aren&#39;t just brains with fingers and we have an entire body that dictate how we think and learn, we must consider the many complex systems of human anatomy factor into our experience when making a choice. This is just one of many things that make human thinking distinct from AI, and why we need to avoid comparing language emulation models to human thought.</p>

<p>If that weren&#39;t enough, there is a much more complex way our behaviors are affected by our environment, culture, social interactions, and the information we consume. When we see AI generate false information with no model to verify it against (i.e. “hallucinates”), it is clear that AI is only working in limited language or sensory dimensions such as image and video to generate something that is plausible or possible, but not likely correct. There&#39;s a lot of great reading in AI papers from the 2000s that aimed to create models like <a href="https://dl.acm.org/doi/10.1145/353485.353487">Distributed Cognition</a> not even to build an AI model around it, but simply to create a vocabulary framework around complex cognition seen in animals to clarify different taxonomies of the type of cognition being emulated and which weren&#39;t. It&#39;s a starch reminder about how anthropomorphism and confirmation bias can cause us to be reductive of the complex mechanisms of our cognition. Although it&#39;s still possible that one day we will build AGI that may or may not think like a human, it most definitely won&#39;t be agents or language models. I believe most, if not, all jobs are safe from being replaced from language models.</p>



<p>In the post LLM hype, my anticipation will be a rise in the job market. Transformer-based model research will live on in AI academia and in business, and we&#39;ll see an explosion in developments around open training and open data sets. There will be a larger focus on developing domain-specific smaller models with fine-tuning. There will also be a large interest in embedded AI for IOT devices and lower energy consumption. This type of AI economy makes knowledge of every human valuable. It will take time and will require consent if we do this right.</p>

<p>Companies should still invest in AI, but gradually and following the techniques by the few successful companies that have had success. This will involve bringing back the domain experts and human resources AI suggested we could replace, and instead train them on how to effectively use AI in their workflow. As employee knowledge becomes important, companies must prioritize proper documentation and knowledge work. If leaders focus on proper incentives to capture workflows across teams through internal wikis, software logic mapping and validation, ops reports, meeting summaries, etc... GenAI has a lot of potential to lower the burden of time-consuming communication gaps and can provide a lot of information for experts to share knowledge with newcomers and remain productive within their role. This all comes down to telling the right stories around how we enable individuals to do their best work and their unique experiences and talents to shape the larger company organism into a more efficient being.</p>

<h2 id="genai-and-search" id="genai-and-search">GenAI and Search</h2>

<p>The single greatest pervasive and most influential information technology we know today is search, specifically Google&#39;s search initially powered by <a href="https://en.wikipedia.org/wiki/PageRank">PageRank</a>. Though general open source search engines like <a href="https://solr.apache.org/">Solr</a>, <a href="https://lucene.apache.org/">Lucene</a>, <a href="https://www.elastic.co/elasticsearch">Elasticsearch</a>, meta search engines like <a href="https://docs.searxng.org/">SearXNG</a>, and more modern vector and search hybrids like <a href="https://www.meilisearch.com/">meilisearch</a>, these only provide the mechanisms of how search works, but are missing the gargantuan amount of data that Google possesses through its search monopoly, data-collecting products, adware metrics, and its highly adopted web browser that feed into both providing context for search. The slow coercive shift of society granting Google its incredible influence over how humans around the globe mentally model and obtain knowledge that form our world views also shaped the way in which netizen&#39;s structure their information to be found.</p>

<p>Much of the GenAI data was procured without consent from the large troves of publicly available data. This ranged from troves of information scraped off of forum sites like, Reddit and StackOverlow, to small sites from individual blogs. All of these were used to feed the large data needs of the LLMs. As we sit here in the aftermath of this technology, I think it&#39;s incredibly important that we reflect on the importance of how we structure our information as a internet society. There is clearly power in the conversations we have and information we produce. It&#39;s important that the exploitative practices of companies like OpenAI and Google drive us towards safeguarding personal information, while ensuring our intellectual assets remain available to the public. We should continue building the <a href="https://en.wikipedia.org/wiki/Freedom_of_information">consensual information sharing of information</a> to benefit everyone, while <a href="https://en.wikipedia.org/wiki/Freedom_of_information#Privacy_protections">safeguarding personal information from private benefactors that can leave the public vulnerable</a> in the current political climate.</p>

<p>Many in open source have started developing open alternatives that enable us to create and use our information on our own terms. Social media alternatives have grown through <a href="https://en.wikipedia.org/wiki/Fediverse">Fediverse</a> technologies, such as <a href="https://en.wikipedia.org/wiki/Mastodon_(social_network)">Mastadon</a> and <a href="https://en.wikipedia.org/wiki/Pixelfed">Pixelfed</a>. There are also user-driven search systems such as <a href="https://en.wikipedia.org/wiki/Usenet">USENET</a> that enable users to curate our own search indexes and share it with others. This could both build information netowrks that would create an information economy that expands our existing peer-to-peer blog funding economies. Rather, you would be funded by curating valuable information in your expertise. However, today&#39;s average netizen would consider a system like USENET complex and unusable as it was created before social media and personal phones shapes how users find information.</p>

<p>Much of the current challenge poised to open designers and developers is to match current search mental models with those of a new system, that would also link across sites through semantic web standards. For those who understand how to build these economies can make some early examples themselves and train future generations of individuals and corporations on how to manage and profit off their own open digital gardens. I believe democratizing the ownership of information flow occurs on the internet will break up information bubbles and enable us to share the information we want and maintain our privacy where we want.</p>

<p>Nobody can do that alone, but it is possible if we start to pull our resources together and build something that has the distribution of USENET, the UX search model of Google, the interoperability of the <a href="https://en.wikipedia.org/wiki/Semantic_Web">semantic web</a>, and the ability for folks to work on a repository of documents like Wikipedia, but many small wikis that can reference eachother. With proper design and consent mechanisms, humans will want to collectively curate the valuable information in our own heads into shared value for globally interconnected local economies.</p>

<p>We&#39;ll dive more into some of these fundamentals in the rest of this series. The next post will dive into the internals of search technology and its relevance to GenAI.</p>


]]></content:encoded>
      <guid>https://bitsondata.dev/humans-arent-going-anywhere</guid>
      <pubDate>Thu, 21 Aug 2025 21:30:58 +0000</pubDate>
    </item>
    <item>
      <title>Your own Private AI 🕵️</title>
      <link>https://bitsondata.dev/your-own-private-ai?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[I&#39;ve had a bunch of conversations with family, friends, and folks who avoid tech until it&#39;s necessary about the latest wave of artificial intelligence (AI) craze. A lot of these folks are curious to use AI, but are concerned about privacy or receiving biased views and misinformation. Many technologists have limited this exposure using locally running AI models which avoids sharing personal information to platforms and enables you to choose a model that has more public validation of the data it trains on and therefore why it may have a stance on a particular topic.&#xA;&#xA;I realized how most blog posts on how to run local AI are written by technologists for technologists and everyone else is beholden to ChatGPT and the like. I myself have been quite excited about the potential for AI, but as a socialist who has worked in the Big Data industry and seen how information is being used against the average citizen, I want to help everyone in learning how to take ownership of your information without avoiding participating in this fun and valuable tech. !--more-- &#xA;&#xA;Before we even jump into that, I just want to provide a little context about the main things you need to know about AI since a tool called ChatGPT made its debut in November 2022. Rather than share the massive amounts of activity happening here, I&#39;ll cover the most interesting larger developments mixed with what you&#39;ll need to know for the tutorial below.&#xA;&#xA;!--emailsub--&#xA;&#xA;Generative AI in a nutshell &#xA;&#xA;First, some clarity on the term &#34;AI&#34;... What is commonly referred to in the media in recent years as &#34;AI&#34; actually refers to a subset (i.e. Generative AI) of a subset (i.e. Deep Learning) of the larger field of Artificial Intelligence. Artificial intelligence prior to the year 2022 was interested in statistical models that were easier to create and computationally &#34;small&#34; enough to run on mobile phone chips. This enables features like autocomplete, voice-to-text, facial recognition to log into you phone or run snapchat filters to add a mustache, etc... Before those applications, artificial intelligence was more often seen in video games when a computer would generate a series of actions for another character or adversary during game play.&#xA;&#xA;ChatGPT is the flagship application of the modern wave of artificial intelligence products built by a closed company ironically named OpenAI. They set a record for most downloaded application and set off the interest and investments into a lot of products that internally rely on their services. For most people ChatGPT is a list of text conversations you have in what looks like an instant messenger window, but the words replying back to you are generated by a computer.&#xA;&#xA;Computers generate this text by running programs that copy as much text off the internet and as many books, magazines, YouTube transcripts, or anything a company can freely get public access to, and build a statistical prediction model called Large Language Models. When we say model, think less Heidi Klum and more like a physics model that predicts the weather. LLMs encode the patterns of speech given a context. For example, if I say &#34;It was the best of times, it was the ___ of times&#34; you likely can guess based on your own model and understanding of language (i.e. your intuition) that the missing word is &#34;worst&#34; even if you&#39;ve never read Charles Dickens&#39; A Tale of Two Cities. LLMs trained on different data sets will have different strengths, weaknesses, and biases when faced with various tasks. In a similar way our brain uses mental models to remember or recall information, you can think of LLMs as just stochastic computer-based intuition models. It should also be noted that human mental models are a shallow analogy and despite the propaganda that AI can think the same way humans do, this is utter rubbish and anthropomorphizing AI has the potential to cause a lot of harm. &#xA;&#xA;One of the early open-source families of LLMs that challenged the superior performance of early ChatGPT models is called Llama). Llama brought the first equally powerful open LLM to center stage, making it possible for people to run LLMs that could even outperform ChatGPT on their own computers privately. The release of Llama was a strategic power play from Meta (aka Facebook) and by opening large scale models to the public, Meta could reap the benefits of the adoption and rapid development in open ecosystems and tooling, such as Ollama.&#xA;&#xA;Despite Ollama&#39;s similar name to the Llama LLMs it is not an LLM itself, but a platform to manage and build off of any open LLM. These platforms are also called LLM providers. Ollama&#39;s name came from its initial use of the llama.cpp to customize (aka fine-tuning) Llama&#39;s LLM to new tasks. As more open LLMs were created, Ollama built its own runtime and became a platform for any open source model. Although this tool is very helpful for those in the industry, it still requires anyone with less technical experience to type all of their work in a command line and wasn&#39;t the experience you would get from ChatGPT. This is where tools like Anything LLM become handy. AnythingLLM is a versatile application that can run on your own laptop and provide the similar user interface and features that you might find on ChatGPT.&#xA;&#xA;Now that I&#39;ve given you all of that information, let&#39;s cover an installation of Ollama to download (aka pull) either Llama or Deepseek LLMs down that are small enough to run on the average computer. The Deepseek model should run on most modern laptops and will give a generally better output than the smaller LLama model I&#39;m providing as backup. Once that is complete we will install Anything LLM to give you a nice sleek chat interface similar to ChatGPT which can recall all your previous chats. I&#39;ve added videos to help with installation visuals as I always think just having a human explain stuff helps me with this stuff as well.&#xA;&#xA;Requirements&#xA;&#xA;You should have a computer that has at least a dualcore or ideally quadcore CPU and at least 4GB (ideally 8GB) of RAM. You will also need about 5GB of storage space depending on the LLM you use (more below).&#xA;&#xA;Tutorial&#xA;&#xA;Download and install Ollama and open the terminal.&#xA;If you have an older laptop or have less than 4GB ram, type:&#xA;    ollama run llama3.2 to run the LLaMa LLM (2.0GB of disk space), or&#xA;    ollama run deepseek-r1 to run Deepseek LLM (4.7GB of disk space).&#xA;Download and install Anything LLM Desktop.&#xA;Open Anything LLM and connect to Ollama&#xA;&#xA;iframe width=&#34;560&#34; height=&#34;315&#34; src=&#34;https://www.youtube.com/embed/p82aMGJJLU8?t=100&#34; frameborder=&#34;0&#34; allow=&#34;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&#34; allowfullscreen/iframe&#xA;&#xA;Once you have that set up, you can then you can use AnythingLLM application similar to ChatGPT. You can even import your local documents to the local instance of AnythingLLM. Watch this video for a nice and recent AnythingLLM overview. &#xA;&#xA;If a lot of this terminology still felt confusing it has more to do with the intentional complexity and lack of user design to avoid too much democratization of running your own AI to keep you dependent on a paid service that extracts and reveals your conversations. I&#39;ll do my best to keep this article up-to-date as new tools and models come along. &#xA;&#xA;!--emailsub--&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p>I&#39;ve had a bunch of conversations with family, friends, and folks who avoid tech until it&#39;s necessary about the latest wave of artificial intelligence (AI) craze. A lot of these folks are curious to use AI, but are <a href="https://www.reuters.com/legal/legalindustry/privacy-paradox-with-ai-2023-10-31/">concerned about privacy</a> or <a href="https://casmi.northwestern.edu/news/articles/2024/misinformation-at-scale-elon-musks-grok-and-the-battle-for-truth.html">receiving biased views and misinformation</a>. Many technologists have limited this exposure using locally running AI models which avoids sharing personal information to platforms and enables you to choose a model that has more public validation of the data it trains on and therefore why it may have a stance on a particular topic.</p>

<p>I realized how most blog posts on how to run local AI are written by technologists for technologists and everyone else is beholden to ChatGPT and the like. I myself have been quite excited about the potential for AI, but as a socialist who has worked in the Big Data industry and seen <a href="https://en.wikipedia.org/wiki/The_Age_of_Surveillance_Capitalism">how information is being used against the average citizen</a>, I want to help everyone in learning how to take ownership of your information without avoiding participating in this fun and valuable tech. </p>

<p>Before we even jump into that, I just want to provide a little context about the main things you need to know about AI since a tool called ChatGPT made its debut in November 2022. Rather than share the massive amounts of activity happening here, I&#39;ll cover the most interesting larger developments mixed with what you&#39;ll need to know for the tutorial below.</p>



<h2 id="generative-ai-in-a-nutshell" id="generative-ai-in-a-nutshell">Generative AI in a nutshell</h2>

<p>First, some clarity on the term “AI”... What is commonly referred to in the media in recent years as “AI” actually refers to a subset (i.e. Generative AI) of a subset (i.e. Deep Learning) of the larger field of Artificial Intelligence. Artificial intelligence prior to the year 2022 was interested in statistical models that were easier to create and computationally “small” enough to run on mobile phone chips. This enables features like autocomplete, voice-to-text, facial recognition to log into you phone or run snapchat filters to add a mustache, etc... Before those applications, artificial intelligence was more often seen in video games when a computer would generate a series of actions for another character or adversary during game play.</p>

<p><img src="https://upload.wikimedia.org/wikipedia/commons/6/62/Pong_Game_Test2.gif" alt=""/></p>

<p><a href="https://en.wikipedia.org/wiki/ChatGPT">ChatGPT</a> is the flagship application of the modern wave of artificial intelligence products built by a closed company ironically named OpenAI. They set a record for most downloaded application and set off the interest and investments into a lot of products that internally rely on their services. For most people ChatGPT is a list of text conversations you have in what looks like an instant messenger window, but the words replying back to you are generated by a computer.</p>

<p><img src="https://i.snap.as/v6C7TgWP.jpeg" alt=""/></p>

<p>Computers generate this text by running programs that copy as much text off the internet and as many books, magazines, YouTube transcripts, or anything a company can freely get public access to, and build a statistical prediction model called <a href="https://en.wikipedia.org/wiki/Large_language_model">Large Language Models</a>. When we say model, think less Heidi Klum and more like a physics model that predicts the weather. LLMs encode the patterns of speech given a context. For example, if I say “It was the best of times, it was the ____ of times” you likely can guess based on your own model and understanding of language (i.e. your intuition) that the missing word is “worst” even if you&#39;ve never read Charles Dickens&#39; <em>A Tale of Two Cities</em>. LLMs trained on different data sets will have different strengths, weaknesses, and biases when faced with various tasks. In a similar way our brain uses mental models to remember or recall information, you can think of LLMs as just stochastic computer-based intuition models. It should also be noted that human mental models are a shallow analogy and despite the propaganda that AI can think the same way humans do, this is <a href="https://archive.ph/HKYjM">utter rubbish</a> and anthropomorphizing AI has the potential to cause a lot of harm.</p>

<p>One of the early open-source families of LLMs that challenged the superior performance of early ChatGPT models is called <a href="https://en.wikipedia.org/wiki/Llama_(language_model)">Llama</a>. Llama brought the first equally powerful open LLM to center stage, making it possible for people to run LLMs that could even outperform ChatGPT on their own computers privately. The release of Llama was a strategic power play from Meta (aka Facebook) and by opening large scale models to the public, Meta could reap the benefits of the adoption and rapid development in open ecosystems and tooling, such as <a href="https://ollama.org/">Ollama</a>.</p>

<p><a href="https://i.snap.as/d5U3FBVR.png"><img src="https://i.snap.as/d5U3FBVR.png" alt=""/></a></p>

<p>Despite Ollama&#39;s similar name to the Llama LLMs it is not an LLM itself, but a platform to manage and build off of any open LLM. These platforms are also called LLM providers. Ollama&#39;s name came from its initial use of the llama.cpp to customize (aka fine-tuning) Llama&#39;s LLM to new tasks. As more open LLMs were created, Ollama built its own runtime and became a platform for any open source model. Although this tool is very helpful for those in the industry, it still requires anyone with less technical experience to type all of their work in a command line and wasn&#39;t the experience you would get from ChatGPT. This is where tools like Anything LLM become handy. AnythingLLM is a versatile application that can run on your own laptop and provide the similar user interface and features that you might find on ChatGPT.</p>

<p>Now that I&#39;ve given you all of that information, let&#39;s cover an installation of Ollama to download (aka pull) either Llama or Deepseek LLMs down that are small enough to run on the average computer. The Deepseek model should run on most modern laptops and will give a generally better output than the smaller LLama model I&#39;m providing as backup. Once that is complete we will install Anything LLM to give you a nice sleek chat interface similar to ChatGPT which can recall all your previous chats. I&#39;ve added videos to help with installation visuals as I always think just having a human explain stuff helps me with this stuff as well.</p>

<h2 id="requirements" id="requirements">Requirements</h2>

<p>You should have a computer that has at least a dualcore or ideally quadcore CPU and at least 4GB (ideally 8GB) of RAM. You will also need about 5GB of storage space depending on the LLM you use (more below).</p>

<h2 id="tutorial" id="tutorial">Tutorial</h2>
<ol><li><a href="https://ollama.com/">Download and install Ollama</a> and <a href="https://youtu.be/3W-trR0ROUY">open the terminal</a>.</li>
<li>If you have an older laptop or have less than 4GB ram, type:
<code>ollama run llama3.2</code> to run the LLaMa LLM (2.0GB of disk space), or
<code>ollama run deepseek-r1</code> to run Deepseek LLM (4.7GB of disk space).</li>
<li><a href="https://anythingllm.com/desktop">Download and install Anything LLM Desktop</a>.</li>
<li>Open Anything LLM and <a href="https://docs.anythingllm.com/setup/llm-configuration/local/ollama">connect to Ollama</a></li></ol>

<iframe width="560" height="315" src="https://www.youtube.com/embed/p82aMGJJLU8" frameborder="0" allowfullscreen=""></iframe>

<p>Once you have that set up, you can then you can use AnythingLLM application similar to ChatGPT. You can even import your local documents to the local instance of AnythingLLM. <a href="https://youtu.be/WsQLC1jOO1U?t=164">Watch this video</a> for a nice and recent AnythingLLM overview.</p>

<p>If a lot of this terminology still felt confusing it has more to do with the intentional complexity and lack of user design to avoid too much democratization of running your own AI to keep you dependent on a paid service <a href="https://www.yahoo.com/news/articles/leaked-chatgpt-conversation-shows-user-202618439.html">that extracts and reveals your conversations</a>. I&#39;ll do my best to keep this article up-to-date as new tools and models come along.</p>


]]></content:encoded>
      <guid>https://bitsondata.dev/your-own-private-ai</guid>
      <pubDate>Thu, 14 Aug 2025 01:23:00 +0000</pubDate>
    </item>
    <item>
      <title>Integrating Trino and Snowflake</title>
      <link>https://bitsondata.dev/trino-snowflake-bloomberg-oss-win?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[An open source Success Story&#xA;&#xA;TL;DR: Contributing to open source can be frustrating as the consensus needed for code to align to the project vision is often out of scope for many companies. This post dives deep into the obstacles and wins of two contributors from different companies working together to add the same proprietary connector. It&#39;s both inspiring and carries many lessons to bring along as you venture into open source to gain the pearls and avoid the perils.&#xA;&#xA;We’re seeing open source usher in a challenge to the economic model where the success metric is increasing the commonwealth of economic capital. This acceleration comes from playing positive-sum games with friends online and avoiding limiting a community to a vision that only benefits a small number of corporations or individuals. It’s hard to imagine how to embed such frameworks within our current zero-sum winner-takes-all economic system.!--more-- There’s certainly no shortage of heated debates around how to construct a harmonious relationship between the open source community and companies participating in them. Something we don’t talk about enough are the positive examples of when a coordinated effort in open source sticks the landing, and so many benefit from it.&#xA;&#xA;This post highlights the extraordinary contributions of Erik Anderson, Teng Yu, Yuya Ebihara, and the broader Trino community to finally contribute the long-coveted Trino Snowflake connector. It is a success story paired with a blueprint for individuals and corporations wanting to contribute to open source projects they use. These stories are valuable in that they demonstrate how to be most effective in collaborating with strangers-soon-to-be-friends and common pitfalls to avoid.&#xA;&#xA;!--emailsub--&#xA;&#xA;A common challenge in open source&#xA;&#xA;Despite the importance of delivering marketing and education in a community (aka edutainment), it’s only the first part of the equation of what makes open source projects successful. Once developers see some exciting video or tutorial, they ultimately land on the docs site, GitHub, StackOverflow, or some communication platform in the community. It&#39;s at this point that developers can easily lose the motivation if the docs lacks proper getting started materials or the community is completely silent. This is how I categorize the developer experience (aka devex), which aims to improve both the user and contributor experiences in the developer community by empowering decisions through hands-on learning, removing inefficiencies, and as we&#39;ll cover here, exposing untapped opportunities. &#xA;&#xA;Much like any open source project, maintainers on the Trino project struggle with communicating the lack of proper resources to build and test new features built for various proprietary software. For those less familiar, Trino is a federated query engine with multiple data sources. Trino tests integrations with open data sources by running small local instances of the connecting system. Snowflake is a proprietary, cloud-native data warehouse, also known as cloud data platform. This provided no viable and free way to support testing this integration that was eagerly sought by many. After an initial attempt by my friend Phillipe Gagnon, a similar pattern emerged with the second pull request where the development velocity started strong and after some months stagnated.&#xA;&#xA;Cognitive surplus and communication deficit&#xA;&#xA;A common and unfortunate class of issues are that various well-known larger objectives known among the core group often move faster than less-established individual contributions. These additions are often much needed and welcomed, but often fail to fit a larger project roadmap narrative. As its easier to coordinate between the smaller core group as trust and norms have been communicated and established. This makes changes outside of this group have a higher likelihood to get lost in the shuffle. As an open source project grows, you end up with a cognitive surplus in the form of an abundance of bright people willing to share their time, intellect, and experience with a larger community.&#xA;&#xA;Often both contributors and maintainers are so busy with their day jobs, families, and self care, that they dedicate most of their remaining energy to ensuring they write quality code and tests to the best of their ability. Lack of upfront communication to validate ideas from newer contributors, and lack of communication by maintainers who see a large number of issues to address are two communication issues that stagnate a project. Maintainers are often doers that see more value in addressing quick-win work that flows from the well-established contributors of the project. Followthrough on either side can be difficult as newcomers don&#39;t want to be rude and maintainers accidentally forget or hope someone else will take the time to address the issues on that pull request. &#xA;&#xA;Waiting for your work to be reviewed by someone in the community kind of works like a wishing well, you toss in a coin (i.e. your time and effort represented as code and a pull request) and hope your wish of getting your code reviewed and merged comes true. The satisfaction of hypothetical developers that benefit from your small and significant change floods your mind and you feel like you’ve improved humanity just that one little bit more. &#xA;&#xA;Maintainers are in a constant state of pulling triage on all the surplus of innovation being thrown at them and simultaneously trying to look for more help reviewing and being the expert at some areas of the code. As you can imagine, good communication can be hard to come by as many newcomers are strangers and concerned they are wasting precious time by asking too many questions rather than just showing a proof of concept. This backfires when developers will spend a large portion of their time developing a solution that is not compatible with the project, and maintainers will lose the opportunity to quickly spin up on the value of the new feature. This is why regular contributor meetings help solve both of these issues synchronously to cut out the delayed feedback loops.&#xA;&#xA;History repeats itself, until it doesn&#39;t&#xA;&#xA;It became apparent that each time there was a discussion for how to do integration testing there was no good way to test a Snowflake instance with the lack of funding for the project. Trino has a high bar for quality and none of the maintainers felt it was a risk worth taking due to the likely popularity of the integration and likelihood of future maintenance issues. Once each pull request hit this same fate, it stalled with no clear path to resolve the real issue of funding the Snowflake infrastructure needed by the Trino Software Foundation (TSF). It’s never fun to mention that you can’t move forward on work with constraints like these, and without a monetary solution, silence is what is experienced on the side of the contributor.&#xA;&#xA;Noticing that Teng had already done a significant amount of work to contribute his Snowflake connector, I reached out to him to see if we could brainstorm a solution. Not long after, Erik also reached out to get my thoughts on how to go about contributing Bloomberg&#39;s Snowflake connector. Great, now we have two connector implementations and no solution to getting the infrastructure to get them tested. During the first Trino Contributor Congregation, Erik and I brought up Bloomberg&#39;s desire to contribute a Snowflake connector and I articulated the testing issue. Ironically, this was the first time I had thoroughly articulated the issue to Erik as well.&#xA;&#xA;As soon as I was done, Erik requested the mic said something to the effect of, &#34;Oh I wish I would have known that&#39;s the problem, the solution is simple, Bloomberg will provide the TSF a Snowflake account.&#34;&#xA;&#xA;Done!&#xA;&#xA;Just as in business, you can never underestimate the power of communication in an open source project as well. Shortly after Erik, Teng, and I discussed the best ways to merge their work, they set up the Snowflake accounts for Trino maintainers and start the arduous process of building a thorough test suite with the help of Yuya, Piotr Findeisen, Manfred Moser, and Martin Traverso.&#xA;&#xA;The long road to Snowflake&#xA;&#xA;As Teng and Erik merged their efforts, the process was anything but straightforward. There were setbacks, vacations, meticulous reviews, and infrastructure issues. But the perseverance of everyone involved was unwavering.&#xA;&#xA;Bloomberg started by creating an official Bloomberg Trino repository originally as a means for Teng and Erik to mesh their solutions together and build the testing infrastructure that relied on Bloomberg resources. Without needing to rely on the main Trino project to merge incremental solutions, they were able to quickly iterate the early solutions. This repository also facilitated Bloomberg’s now numerous contributions to Trino.&#xA;&#xA;It took a few months just to get the ForePaaSa name=&#34;fn1&#34;/asupa class=&#34;footnote&#34; href=&#34;#fnref1&#34;1/a/sup and Bloomberg solutions merged. There were valuable takes from each system and better integration tests were written with the new testing infrastructure. The two Snowflake connector implementations were merged together by April of 2023. Finally, the reviews could start. Once the initial two passes happened we anticipated that we would see the Snowflake connector release in the summer of 2023 around Trino Fest. So much so, that we planned a talk with Erik and Teng initially as a reveal, assuming the pull request would be merged by then. Lo and behold, this didn’t happen, as there were still a lot of concerns around use cases not being properly tested.&#xA;&#xA;The halting review problem&#xA;&#xA;A necessary evil that comes with pull request reviews and more broadly, distributed consensus is that reviews can drag on over time. This can lead to countless number of updates you have to make to your changes to accommodate the ever changing project shifting beneath your feet as you simultaneously try to make progress on suggestions from those reviewing your code.&#xA;&#xA;Many critics of open source like to point this out as a drawback, when in fact, this same problem exists in closed source systems. Closed source projects can generally delay difficult decisions to make fast upfront progress to meet certain deadlines. This may be seen as an advantage at first, but as many developers can attest, this simply leads to technical debt and fragile products in most environments that struggle to prioritize a healthy codebase.&#xA;&#xA;Regardless, having to face these larger discussions upfront can induce fatigue, especially when managing external circumstances; personal affairs, a project at work - you know, the entity that pays these engineers - or countless other factors will rear their ugly heads and progress will stagger with ebbs and flows of attention. This can be really dangerous territory and commonly resolves in contributors and reviewers abandoning the PR when it stalls.&#xA;&#xA;This is why I believe open source, while not beholden to any timelines, needs a project and product management role which is currently covered often by project leaders and devex engineers. This can also relieve tension between the needs of open source and big businesses in the community with real deadlines, at least keeping the communication consistent while ensuring bugs and design flaws aren’t introduced to the code base.&#xA;&#xA;What’s in it for Bloomberg and ForePaaS?&#xA;&#xA;If you’ve never worked in open source or for a company that contributes to open source, you may be thinking how the heck do these engineers convince their leadership to let them dump so much time into these contributions? The simple answer is, it’s good for business.&#xA;&#xA;If we peep into why Bloomberg uses Trino, they aggregate data from an unusually large number of data sources across their customers who use their services. Part of this requires them to merge the customer’s dataset with existing aggregate data in Bloomberg’s product. Since Trino can connect to most customer databases out-of-the-box, this requires Bloomberg to manage a small array of custom connectors that provide their services to customers as multiple catalogs in a single convention SQL endpoint. Having engineers maintain a few small connectors rather than an entire distributed query engine themselves saves a lot of time and maintenance.&#xA;&#xA;Despite how many problems Trino already solves for them, Bloomberg and ForePaaS needed this Snowflake connector and through the open source model created it for themselves. The drawback is that the solution must be maintained by the engineers at each company any time they want to upgrade to a new Trino feature. This takes consistently depletes engineering resources and so they want to maintain as few features as possible to relieve their engineer’s time. Open source projects are generally more than happy to accept features that the community benefits from. This doesn’t mean we shouldn’t appreciate when companies contribute. This dual-sum generosity and forward-thinking approach enabled Erik and Teng to combine their battle-tested connectors, crafting a high value creation for the community.&#xA;&#xA;If you are a developer who sees the value in contributing to open source, and you aren&#39;t sure how to convince leadership to get on board, you need to speak their language. Show how companies like Bloomberg get involved in open source, and how it lowers maintenance costs when done correctly. If you see an open project like Trino that could replace 97% of a new project, demonstrate that the upfront cost will pay off when you remove the amount of code to be managed by your team which lowers the future need to expand headcounts. I don’t imagine a world where your boss and colleagues are altruists, but present an economic incentive that lowers the amortized cost of engineers needed to maintain a project, then your strategy becomes helpful to the company&#39;s bottom line.&#xA;&#xA;While the immediate investment shows small gains for a single team on a single company, once that change exists in open source, other companies can immediately benefit and offer better testing and improvements than you could have asked for when managing the original project with your own team. Humanity at large gets to benefit upon every contribution done this way, and the more companies that embrace this, the less we waste our efforts of pointlessly duplicating work.&#xA;&#xA;Esprit de Corps&#xA;&#xA;The marines use the mantra, “Esprit de Corps,” latin for “spirit of the people”, which I mistakenly took the “Corps” part for the Marine Corps rather than the more general meaning of a body or group of people. In fact, it expresses the common spirit existing in the members of a group and inspiring enthusiasm, devotion, and strong regard for the honor of the group. Any time I see this type of shared and selfless cooperation in open source, I’m reminded of the bond, friendships, and care of me and my fellow marines. Despite the unfortunate political circumstances of our mission, I do treasure the shared companionship with both my fellow marines and the local Iraqi people. There is ultimately a power in the gathering of many when aimed for building an altruistic means of improving each others lives.&#xA;&#xA;In the same way, demonstration of human cooperation is about more than just developing a connector; it&#39;s about the shared experiences, the friendships forged, and the skills honed in the pursuit of a common goal. The successful addition of the Trino Snowflake connector is a testament to the positive sum outcomes of open source collaboration. This journey has been about collaboration, learning, and growth that will benefit many. I remember the night I got the email that Yuya had merged the pull request, I was ecstatic to say the least. The connector shipped with Trino version 440, and made connection to the most widely adopted cloud warehouse possible.&#xA;&#xA;Once the hard work was done, many valuable iterations like adding Top-N support(Shoppee), adding Snowflake Iceberg REST catalog support (Starburst), and adding better type mapping(Apple) were added to the Snowflake integration. I love showcasing this trailblazing and yes, altruistic work from Erik, Teng, Yuya, Martin, Manfred, and Piotr - and everyone who helped in the Trino community. A special thanks to the managers and leadership at Bloomberg and ForePaaS for their generous commitment of time and resources.&#xA;&#xA;As we celebrate this milestone, we&#39;re already looking forward to the next adventure. Here&#39;s to federating them all, together!&#xA;&#xA;Notes:&#xA;a name=&#34;fnref1&#34;/asupa class=&#34;footnote-ref&#34; href=&#34;#fn1&#34;1/a/supspan class=&#34;footnote-ref-text&#34;ForePaaS has been integrated into OVHCloud, which is now called Data Platform./span&#xA;&#xA;bits]]&gt;</description>
      <content:encoded><![CDATA[<h2 id="an-open-source-success-story" id="an-open-source-success-story">An open source Success Story</h2>

<p>TL;DR: Contributing to open source can be frustrating as the consensus needed for code to align to the project vision is often out of scope for many companies. This post dives deep into the obstacles and wins of two contributors from different companies working together to add the same proprietary connector. It&#39;s both inspiring and carries many lessons to bring along as you venture into open source to gain the pearls and avoid the perils.</p>

<p><img src="https://i.snap.as/CvkkjKzk.jpeg" alt=""/></p>

<p>We’re seeing open source usher in a challenge to the economic model where the success metric is increasing the commonwealth of economic capital. This acceleration comes from playing positive-sum games with friends online and avoiding limiting a community to a vision that only benefits a small number of corporations or individuals. It’s hard to imagine how to embed such frameworks within our current zero-sum winner-takes-all economic system. There’s certainly no shortage of heated debates around how to construct a harmonious relationship between the open source community and companies participating in them. Something we don’t talk about enough are the positive examples of when a coordinated effort in open source sticks the landing, and so many benefit from it.</p>

<p>This post highlights the extraordinary contributions of <a href="https://www.linkedin.com/in/erikanderson/">Erik Anderson</a>, <a href="https://www.linkedin.com/in/tyu-fr/">Teng Yu</a>, <a href="https://www.linkedin.com/in/ebyhr/">Yuya Ebihara</a>, and the broader <a href="https://github.com/trinodb/trino">Trino community</a> to finally contribute the long-coveted <a href="https://trino.io/docs/current/connector/snowflake.html">Trino Snowflake connector</a>. It is a success story paired with a blueprint for individuals and corporations wanting to contribute to open source projects they use. These stories are valuable in that they demonstrate how to be most effective in collaborating with strangers-soon-to-be-friends and common pitfalls to avoid.</p>



<h2 id="a-common-challenge-in-open-source" id="a-common-challenge-in-open-source">A common challenge in open source</h2>

<p>Despite the importance of delivering marketing and education in a community (aka <a href="https://en.wikipedia.org/wiki/Educational_entertainment">edutainment</a>), it’s only the first part of the equation of what makes open source projects successful. Once developers see some exciting video or tutorial, they ultimately land on the docs site, GitHub, StackOverflow, or some communication platform in the community. It&#39;s at this point that developers can easily lose the motivation if the docs lacks proper getting started materials or the community is completely silent. This is how I categorize the developer experience (aka devex), which aims to improve both the user and contributor experiences in the developer community by <a href="https://en.wikipedia.org/wiki/Experiential_learning">empowering decisions through hands-on learning</a>, <a href="https://trino.io/blog/2023/01/09/cleaning-up-the-trino-backlog">removing inefficiencies</a>, and as we&#39;ll cover here, exposing untapped opportunities.</p>

<p>Much like any open source project, maintainers on the Trino project struggle with communicating the lack of proper resources to build and test new features built for various proprietary software. For those less familiar, Trino is a federated query engine with <a href="https://trino.io/docs/current/connector.html">multiple data sources</a>. Trino tests integrations with open data sources by running small local instances of the connecting system. Snowflake is a proprietary, cloud-native data warehouse, also known as cloud data platform. This provided no viable and free way to support testing this integration that was <a href="https://github.com/trinodb/trino/pull/2551#issuecomment-873082280">eagerly</a> <a href="https://github.com/trinodb/trino/issues/1863">sought</a> <a href="https://github.com/trinodb/trino/issues/7247">by many</a>. After an <a href="https://github.com/trinodb/trino/pull/2551">initial attempt</a> by my friend <a href="https://www.linkedin.com/in/pfgagnon">Phillipe Gagnon</a>, a similar pattern emerged <a href="https://github.com/trinodb/trino/pull/10387">with the second pull request</a> where the development velocity started strong and after some months stagnated.</p>

<h3 id="cognitive-surplus-and-communication-deficit" id="cognitive-surplus-and-communication-deficit">Cognitive surplus and communication deficit</h3>

<p>A common and unfortunate class of issues are that various well-known larger objectives known among the core group often move faster than less-established individual contributions. These additions are often much needed and welcomed, but often fail to fit a larger project roadmap narrative. As its easier to coordinate between the smaller core group as trust and norms have been communicated and established. This makes changes outside of this group have a higher likelihood to get lost in the shuffle. As an open source project grows, you end up with a cognitive surplus in the form of an abundance of bright people willing to share their time, intellect, and experience with a larger community.</p>

<p>Often both contributors and maintainers are so busy with their day jobs, families, and self care, that they dedicate most of their remaining energy to ensuring they write quality code and tests to the best of their ability. Lack of upfront communication to validate ideas from newer contributors, and lack of communication by maintainers who see a large number of issues to address are two communication issues that stagnate a project. Maintainers are often doers that see more value in addressing quick-win work that flows from the well-established contributors of the project. Followthrough on either side can be difficult as newcomers don&#39;t want to be rude and maintainers accidentally forget or hope someone else will take the time to address the issues on that pull request.</p>

<p><img src="https://i.snap.as/7TdSoquQ.jpg" alt=""/></p>

<p>Waiting for your work to be reviewed by someone in the community kind of works like a wishing well, you toss in a coin (i.e. your time and effort represented as code and a pull request) and hope your wish of getting your code reviewed and merged comes true. The satisfaction of hypothetical developers that benefit from your small and significant change floods your mind and you feel like you’ve improved humanity just that one little bit more.</p>

<p>Maintainers are in a constant state of pulling triage on all the surplus of innovation being thrown at them and simultaneously trying to look for more help reviewing and being the expert at some areas of the code. As you can imagine, good communication can be hard to come by as many newcomers are strangers and concerned they are wasting precious time by asking too many questions rather than just showing a proof of concept. This backfires when developers will spend a large portion of their time developing a solution that is not compatible with the project, and maintainers will lose the opportunity to quickly spin up on the value of the new feature. This is why regular <a href="https://github.com/trinodb/trino/wiki/Contributor-meetings">contributor meetings</a> help solve both of these issues synchronously to cut out the delayed feedback loops.</p>

<h3 id="history-repeats-itself-until-it-doesn-t" id="history-repeats-itself-until-it-doesn-t">History repeats itself, until it doesn&#39;t</h3>

<p>It became apparent that each time there was <a href="https://github.com/trinodb/trino/pull/2551#issuecomment-709220790">a discussion</a> for how to do <a href="https://github.com/trinodb/trino/pull/10387#issuecomment-1008430060">integration testing</a> there was no good way to test a Snowflake instance with the lack of funding for the project. Trino has a high bar for quality and none of the maintainers felt it was a risk worth taking due to the likely popularity of the integration and likelihood of future maintenance issues. Once each pull request hit this same fate, it stalled with no clear path to resolve the real issue of funding the Snowflake infrastructure needed by the <a href="https://trino.io/foundation.html">Trino Software Foundation (TSF)</a>. It’s never fun to mention that you can’t move forward on work with constraints like these, and without a monetary solution, silence is what is experienced on the side of the contributor.</p>

<p>Noticing that Teng had already done a significant amount of work to contribute his Snowflake connector, I reached out to him to see if we could brainstorm a solution. Not long after, Erik also reached out to get my thoughts on how to go about contributing Bloomberg&#39;s Snowflake connector. Great, now we have two connector implementations and no solution to getting the infrastructure to get them tested. During the first <a href="https://trino.io/blog/2022/11/21/trino-summit-2022-recap.html#trino-contributor-congregation">Trino Contributor Congregation</a>, Erik and I brought up Bloomberg&#39;s desire to contribute a Snowflake connector and I articulated the testing issue. Ironically, this was the first time I had thoroughly articulated the issue to Erik as well.</p>

<p>As soon as I was done, Erik requested the mic said something to the effect of, “Oh I wish I would have known that&#39;s the problem, the solution is simple, Bloomberg will provide the TSF a Snowflake account.”</p>

<p>Done!</p>

<p>Just as in business, <strong>you can never underestimate the power of communication in an open source project</strong> as well. Shortly after Erik, Teng, and I discussed the best ways to merge their work, they set up the Snowflake accounts for Trino maintainers and start the arduous process of building a thorough test suite with the help of Yuya, <a href="https://www.linkedin.com/in/piotrfindeisen/">Piotr Findeisen</a>, <a href="https://www.linkedin.com/in/manfredmoser/">Manfred Moser</a>, and <a href="https://www.linkedin.com/in/traversomartin/">Martin Traverso</a>.</p>

<h2 id="the-long-road-to-snowflake" id="the-long-road-to-snowflake">The long road to Snowflake</h2>

<p>As Teng and Erik merged their efforts, the process was anything but straightforward. There were setbacks, vacations, meticulous reviews, and infrastructure issues. But the perseverance of everyone involved was unwavering.</p>

<p>Bloomberg started by creating <a href="https://github.com/bloomberg/trino">an official Bloomberg Trino repository</a> originally as a means for Teng and Erik to mesh their solutions together and build the testing infrastructure that relied on Bloomberg resources. Without needing to rely on the main Trino project to merge incremental solutions, they were able to quickly iterate the early solutions. This repository also facilitated Bloomberg’s now numerous contributions to Trino.</p>

<p>It took a few months just to get the ForePaaS<sup><a class="footnote" href="#fnref1">1</a></sup> and Bloomberg solutions merged. There were valuable takes from each system and better integration tests were written with the new testing infrastructure. The two Snowflake connector implementations were merged together by April of 2023. Finally, the reviews could start. Once the initial two passes happened we anticipated that we would see the Snowflake connector release in the summer of 2023 around Trino Fest. So much so, that we planned <a href="https://trino.io/blog/2023/07/12/trino-fest-2023-let-it-snow-recap.html">a talk with Erik and Teng</a> initially as a reveal, assuming the pull request would be merged by then. Lo and behold, this didn’t happen, as there were still a lot of concerns around use cases not being properly tested.</p>

<h3 id="the-halting-review-problem" id="the-halting-review-problem">The halting review problem</h3>

<p>A necessary evil that comes with pull request reviews and more broadly, distributed consensus is that reviews can drag on over time. This can lead to <a href="https://github.com/trinodb/trino/pull/17909#issuecomment-1841809727">countless number of updates</a> you have to make to your changes to accommodate the ever changing project shifting beneath your feet as you simultaneously try to make progress on <a href="https://github.com/trinodb/trino/pull/17909#pullrequestreview-1793724311">suggestions from those reviewing your code</a>.</p>

<p>Many critics of open source like to point this out as a drawback, when in fact, this same problem exists in closed source systems. Closed source projects can generally delay difficult decisions to make fast upfront progress to meet certain deadlines. This may be seen as an advantage at first, but as many developers can attest, this simply leads to technical debt and fragile products in most environments that struggle to prioritize a healthy codebase.</p>

<p><img src="https://i.snap.as/Oi74UR5y.jpg" alt=""/></p>

<p>Regardless, having to face these larger discussions upfront can induce fatigue, especially when managing external circumstances; personal affairs, a project at work – you know, the entity that pays these engineers – or countless other factors will rear their ugly heads and <a href="https://github.com/trinodb/trino/pull/17909#discussion_r1418149737">progress will stagger</a> with ebbs and flows of attention. This can be really dangerous territory and commonly resolves in contributors and reviewers abandoning the PR when it stalls.</p>

<p>This is why I believe open source, while not beholden to any timelines, needs a project and product management role which is currently covered often by project leaders and devex engineers. This can also relieve tension between the needs of open source and big businesses in the community with real deadlines, at least keeping the communication consistent while ensuring bugs and design flaws aren’t introduced to the code base.</p>

<h2 id="what-s-in-it-for-bloomberg-and-forepaas" id="what-s-in-it-for-bloomberg-and-forepaas">What’s in it for Bloomberg and ForePaaS?</h2>

<p>If you’ve never worked in open source or for a company that contributes to open source, you may be thinking how the heck do these engineers convince their leadership to let them dump so much time into these contributions? The simple answer is, it’s good for business.</p>

<p>If we peep into why Bloomberg uses Trino, they aggregate data from an unusually large number of data sources across their customers who use their services. Part of this requires them to merge the customer’s dataset with existing aggregate data in Bloomberg’s product. Since Trino can connect to most customer databases out-of-the-box, this requires Bloomberg to manage a small array of custom connectors that provide their services to customers as multiple catalogs in a single convention SQL endpoint. Having engineers maintain a few small connectors rather than an entire distributed query engine themselves saves a lot of time and maintenance.</p>

<p>Despite how many problems Trino already solves for them, Bloomberg and ForePaaS needed this Snowflake connector and through the open source model created it for themselves. The drawback is that the solution must be maintained by the engineers at each company any time they want to upgrade to a new Trino feature. This takes consistently depletes engineering resources and so they want to maintain as few features as possible to relieve their engineer’s time. Open source projects are generally more than happy to accept features that the community benefits from. This doesn’t mean we shouldn’t appreciate when companies contribute. This dual-sum generosity and forward-thinking approach enabled Erik and Teng to combine their battle-tested connectors, crafting a high value creation for the community.</p>

<p>If you are a developer who sees the value in contributing to open source, and you aren&#39;t sure how to convince leadership to get on board, you need to speak their language. Show how companies like Bloomberg get involved in open source, and how it lowers maintenance costs when done correctly. If you see an open project like Trino that could replace 97% of a new project, demonstrate that the upfront cost will pay off when you remove the amount of code to be managed by your team which lowers the future need to expand headcounts. I don’t imagine a world where your boss and colleagues are altruists, but present an economic incentive that lowers the amortized cost of engineers needed to maintain a project, then your strategy becomes helpful to the company&#39;s bottom line.</p>

<p>While the immediate investment shows small gains for a single team on a single company, once that change exists in open source, other companies can immediately benefit and offer better testing and improvements than you could have asked for when managing the original project with your own team. Humanity at large gets to benefit upon every contribution done this way, and the more companies that embrace this, the less we waste our efforts of pointlessly duplicating work.</p>

<h2 id="esprit-de-corps" id="esprit-de-corps">Esprit de Corps</h2>

<p>The marines use the mantra, “Esprit de Corps,” latin for “spirit of the people”, which I mistakenly took the “Corps” part for the Marine Corps rather than the more general meaning of a body or group of people. In fact, it expresses <a href="https://www.merriam-webster.com/dictionary/esprit%20de%20corps">the common spirit existing in the members of a group and inspiring enthusiasm, devotion, and strong regard for the honor of the group</a>. Any time I see this type of shared and selfless cooperation in open source, I’m reminded of the bond, friendships, and care of me and my fellow marines. Despite the unfortunate political circumstances of our mission, I do treasure the shared companionship with both my fellow marines and the local Iraqi people. There is ultimately a power in the gathering of many when aimed for building an altruistic means of improving each others lives.</p>

<p><img src="https://i.snap.as/TO03Akr4.jpeg" alt=""/></p>

<p>In the same way, demonstration of human cooperation is about more than just developing a connector; it&#39;s about the shared experiences, the friendships forged, and the skills honed in the pursuit of a common goal. The successful addition of the Trino Snowflake connector is a testament to the positive sum outcomes of open source collaboration. This journey has been about collaboration, learning, and growth that will benefit many. I remember the night I got the email that Yuya had <a href="https://github.com/trinodb/trino/pull/17909">merged the pull request</a>, I was ecstatic to say the least. The connector shipped with <a href="https://trino.io/docs/current/release/release-440.html#general">Trino version 440</a>, and made connection to the most widely adopted cloud warehouse possible.</p>

<p>Once the hard work was done, many valuable iterations like <a href="https://github.com/trinodb/trino/pull/21219">adding Top-N support</a>(Shoppee), <a href="https://github.com/trinodb/trino/pull/21365">adding Snowflake Iceberg REST catalog support</a> (Starburst), and <a href="https://github.com/trinodb/trino/pull/21365">adding better type mapping</a>(Apple) were added to the Snowflake integration. I love showcasing this trailblazing and yes, altruistic work from Erik, Teng, Yuya, Martin, Manfred, and Piotr – and everyone who helped in the Trino community. A special thanks to the managers and leadership at Bloomberg and ForePaaS for their generous commitment of time and resources.</p>

<p>As we celebrate this milestone, we&#39;re already looking forward to the next adventure. Here&#39;s to federating them all, together!</p>

<p>Notes:
<sup><a class="footnote-ref" href="#fn1">1</a></sup><span class="footnote-ref-text">ForePaaS has been integrated into <a href="https://ovhcloud.com">OVHCloud</a>, which is now called <a href="https://help.ovhcloud.com/csm/en-public-cloud-data-platform-what-is?id=kb_article_view&amp;sysparm_article=KB0060801">Data Platform</a>.</span></p>

<p><em>bits</em></p>
]]></content:encoded>
      <guid>https://bitsondata.dev/trino-snowflake-bloomberg-oss-win</guid>
      <pubDate>Wed, 08 May 2024 05:00:00 +0000</pubDate>
    </item>
    <item>
      <title>Iceberg won the table format war</title>
      <link>https://bitsondata.dev/iceberg-won-the-table-format-war?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[the night sky is lit up over the water&#xA;&#xA;Photo by Michail Dementiev on Unsplash&#xA;&#xA;TL;DR: I believe Apache Iceberg won the table format wars, not because of a feature race, but primarily because of the open Iceberg spec. There are some features only available in Iceberg due to the breaking of compatibility with Hive, which was also a contributing factor to the adoption of the implementation.&#xA;&#xA;!--more--&#xA;&#xA;Disclaimer: I am the Head of Developer Relations at Tabular and a Developer Advocate in both Apache Iceberg and Trino communities. All of my 🌶️ takes are my biased opinion and not necessarily the opinion of Tabular, the Apache Software Foundation, the Trino Software Foundation, or the communities I work with. This also goes into a bit of my personal story for leaving my previous company but relates to my reasoning so I offer you a TL;DR if you don’t care about the details.&#xA;&#xA;!--emailsub--&#xA;&#xA;My revelation with Iceberg&#xA;&#xA;Two months ago, I made the difficult decision to leave Starburst which was hands down the best job I’ve ever had up to this point. Since I left I’ve had a lot of questions about my motivations for leaving and wanted to put some concerns to rest. This role allowed me to get deeply involved in open-source during working hours and showed me how I could aid the community to get traction on their work and drive the roadmap for many in the project. This was a new calling that overlapped with many altruist parts of how I define myself and was deeply rewarding.&#xA;&#xA;I made some incredible friends, some of which have become invaluable mentors during this process of learning the nuances and interplay between venture capital and an open-source community. So why did I leave this job that I love so much?&#xA;&#xA;Apache Iceberg Baby&#xA;&#xA;Let’s time-travel (pun intended) to the first Iceberg episode of the Trino Community Broadcast. In true ADHD form, I crammed learning about Apache Iceberg well into the night before the broadcast with the creator of Iceberg, Ryan Blue. While setting up that demo, I really started to understand what a game-changer Iceberg was. I had heard the Trino users and maintainers talk about Iceberg replacing Hive but it just didn’t sink in for the first couple of months. I mean really, what could be better than Hive? 🥲&#xA;&#xA;While researching I learned about hidden partitioning, schema evolution, and most importantly, the open specification. The whole package was just such an elegant solution to problems that had caused me and many in the Trino community failed deployments and late-night calls. Just as I had the epiphany with Trino (Presto at the time) of how big of a productivity booster SQL queries over multiple systems were, I had a similar experience with Iceberg that night. Preaching the combination of these two became somewhat of a mission of mine after that.&#xA;&#xA;ANSI SQL + Iceberg + Parquet + S3&#xA;&#xA;Immediately after that show, I wrote a four-blog series on Trino on Iceberg, did a talk, and built out the getting started repository for Iceberg. I was rather hooked on the thought of these two technologies in tandem. You start out with a system that can connect to any data source you throw at it and sees it as yet another SQL table. Take that system and add a table format that interacts with all the popular analytics engines people use today from Spark, Snowflake, and DuckDB, to Trino variants like EMR, Athena, and Starburst.&#xA;&#xA;Standards all the way down&#xA;&#xA;This approach to data virtualization is so interesting as each system offers full leverage over vendors trying to lock you in their particular query language or storage format. It pushes the incentives for vendors to support these open standards which puts them in a seemingly vulnerable position compared to locking users in. However, that’s a fallacy I hope vendors will slowly begin to understand is not true. With the open S3 storage standard, open file standards like Parquet, open table standards like Iceberg, and the ANSI SQL spec closely followed by Trino, the entire analytics warehouse has become a modular stack of truly open components. This is not just open in the sense of the freedom to use and contribute to a project, but the open standard that enables you the freedom to simply move between different projects.&#xA;&#xA;This new freedom gives the user the features of a data warehouse, with the scalability of the cloud, and a free market of a la carte services to handle your needs at whatever price point you need at any given time. All users need to do in this new ecosystem is shop around and choose any open project or vendor that implements the open standard and your migration cost will be practically non-existent. This is the ultimate definition of future-proofing your architecture.&#xA;&#xA;!--emailsub--&#xA;&#xA;Back to why I left Starburst&#xA;&#xA;Trino Community&#xA;&#xA;I’ll quickly tie up why I left Starburst before I reveal why Iceberg won the table format wars. For the last three years, I have worked on building awareness around Trino. My partner in crime, Manfred Moser, had been in the Trino community and literally wrote the book on Trino. Together we spent long days and nights growing the Trino community. I loved every minute of it and honestly didn’t see myself leaving Starburst or shifting focus from Trino until it became an analytics organization standard.&#xA;&#xA;Something became apparent though. Trino community health was thriving, and there were many organic product movements taking place in the Trino community. Cole Bowden was boosting the Trino release process getting us to cutting Trino releases every 1-2 weeks which is unprecedented in open-source. Cole, Manfred, and I did a manual scan over the pull requests and gracefully closed or revived outdated or abandoned pull requests. The Trino community is in great shape.&#xA;&#xA;Iceberg Community&#xA;&#xA;As I looked at Iceberg, the adoption and awareness were growing at an unprecedented rate with Snowflake, BigQuery, Starburst, and Athena all announcing support between 2021 and 2022. However, nothing was moving the needle forward from a developer experience perspective. There was some initial amazing work done by Sam Redai, but there was still so much to be done. I noticed the Iceberg documentation needed improvement. While many vendors were advocating for Iceberg, there was nobody putting in consistent work to the vanilla Iceberg site. PMCs like Ryan Blue, Jack Ye, Russel Spitzer, Dan Weeks, and many others are doing a great shared job of driving roadmap features for Iceberg, but no individual currently has the time to dedicate to the cat herding, improving communication in the project, or bettering the developer and contributor experience for users. Since Trino was on stable ground it felt imperative to move to Iceberg and fill in these gaps. When Ryan approached me with a Head of DevRel position at Tabular, I couldn’t pass up the opportunity. To be clear I left Starburst but not the Trino community. Being at Iceberg also helped me in my mission to continue forging these two technologies that I believe in so much.&#xA;&#xA;Tell us why Iceberg won already!&#xA;&#xA;Moving back to the meat of the subject. My first blog in the Trino community covered what I once called, the invisible Hive spec to alleviate confusion around why Trino would need a “Hive” connector if it’s a query engine itself. The reason we called the Hive connector as such is that it translated Parquet files sitting in an S3 object store into a schema that could be read and modified via a query engine that knew the Hive spec. This had nothing to do with Hive the query engine, but Hive the spec. Why was the Hive spec invisible 👻? Because nobody wrote it down. It was in the minds of the engineers who put Hive together, spread across Cloudera forums by engineers who had bashed their heads against the wall and reverse-engineered the binaries to understand this “spec”.&#xA;&#xA;Why do you even need a spec?&#xA;&#xA;Having an “invisible” spec was rather problematic, as every implementation of that spec ran on different assumptions. I spent days searching Hive solutions on Hortonworks/Cloudera forums trying to solve an error with solutions that for whatever reason, didn’t work on my instance of Hive. There were also implicit dependencies required with using the Hive spec. It’s actually incorrect to call the Hive spec as such because a spec should have independence of platform, programming language, and hardware, while including minimal implementation details not required for interoperability between implementations. SQL, for instance, is a declarative language that runs on systems written in, C++, Rust, Java, and doesn’t get into the business of telling query engines how to answer that query, just what behavior is expected.&#xA;&#xA;The open spec is why Iceberg won&#xA;&#xA;By the time there were various iterations of Hive from the Hadoop and Big Data Boom, there was not a very central source to lay a stake in the ground to standardize this spec. While you may imagine that we would have learned our lesson, until Iceberg, none of the projects officially formalized their assumptions and specifications for their project. Delta Lake and Hudi extended the Hive models to make migration from Hive simpler but kept a few of the issues that Hive introduced, like exposing the partitioning format to users running analysis.&#xA;&#xA;Open specifications and vendor politics&#xA;&#xA;You may seem skeptical that an open specification for a table format holds such weight, but it crept its way into a well-known feud between two large analytics vendors of the day, Databricks and Snowflake. Databricks has been the leading vendor in the data lakehouse market while Snowflake dominated the data warehouse market. Snowflake’s original strategy initially involved encouraging movement from the data lakehouse market back to the data warehouse market, while Databrick’s original strategy was to do the exact opposite. This conveyed the outdated trap that many vendors still fall prey to. This idea is that locking users in will help your business and stakeholders reduce churn and keep customers in the long run. Vendor lock-in was once a decades-long play, but as B2C consumerism expectations creep into B2B consumerism, we are seeing a gradual shift of practitioners demanding interoperability from vendors to give them the level of autonomy they have experienced with open-source projects.&#xA;&#xA;This surfaced with Snowflake when they announced that they would be offering support for an Iceberg external table functionality in 2022. At first, I raised my eyebrow at this as I figured this was a feeble attempt for Snowflake to market itself as an open-friendly warehouse when the external table would be nothing but an easy way to migrate data from the data lake to Snowflake. Whatever their motives, this was good visibility for Iceberg and I was thrilled that Snowflake was showcasing the need for an open specification, gimmick or not. This even pressured other competing data warehouses like BigQuery to add Iceberg support.&#xA;&#xA;!--emailsub--&#xA;&#xA;The final signal that the open Iceberg spec won&#xA;&#xA;I didn’t, however, expect to see what took place at both Snowflake Summit and Data + AI Summit this year in 2023. If you don’t know these are Snowflake and Databricks’ big events that happened on the same week as an extension of their feud. There were already some hints that Snowflake had dropped to signal that they were ramping up their Iceberg support but were doing a great job at keeping it under wraps. The final reveal came at Snowflake Summit; Snowflake now offers managed Iceberg catalog support.&#xA;&#xA;I was thrilled to see that finally, a data warehouse vendor understands there’s no way to beat open standards and they should adopt open storage as part of their core business model. What’s even more about this picture is they show both Trino and Spark engines as open compute alternatives to their own engine. This was beyond my expectations for Snowflake, and definitely showed me they were heading in the right direction for their customers.&#xA;&#xA;Meanwhile, in a California town not far away, Databricks would also have a response to Snowflake’s announcement stored up. Delta Lake 3.0 was announced and it now supports compatibility across Delta Lake, Iceberg, and Hudi (be it limited compatibility for Iceberg for now). Whatever happens, there is now opportunity for Databricks customers to trial Iceberg, and this should excite everyone. We’re one step closer to having this spec become the common denominator. With both of these moves from a company that initially only wanted their proprietary format to win and a company that built on a competing format, I have the opinion that Iceberg has won the format wars.&#xA;&#xA;Now I don’t want to act like these vendors simply have users’ best interests in mind. All they want is for you to make the decision to choose them. I work for a vendor, we also want your money. What I am seeing though is that now the industry is trending towards incentivizing openness as more customers demand it. In order for companies to stay ahead, they must embrace this fact rather than fight it. What is rather historical about this moment in time is that since the dawn of analytics and the data warehouse, there has been vendor lock-in on many fronts of the analytics market. This to me, signals the nail in the coffin of any capability for vendors to do this on the level playing field of open standards. It’s now up to the vendors to keep you happy and continuously stop you from churning. This in the long run is good for users and vendors and will ultimately drive better products.&#xA;&#xA;One quick aside, some may mention that Hudi recently added a “spec” so why does Iceberg having a spec give it the winning vote? I recommend you go back to reading the purpose of an open spec section earlier in this blog, then look at both the Iceberg spec and the Hudi “spec” and determine which one satisfies the criteria. Hudi’s “spec” exposes Java dependencies making it unusable for any system not running on the JDK, doesn’t clarify schema, and has a lot of implementation details rather than leaving that to the systems that implement it. This “spec” is something closer to an architecture document than an open spec.&#xA;&#xA;Will the Iceberg project drive Delta Lake and Hudi to nonexistence?&#xA;&#xA;Maybe, or maybe not. That really depends on the feature set that these three table formats offer and what the actual value they bring to the users is. In other words, you decide what’s important, we decide what we’re going to support, and vendors and the larger data community decide who stays and who goes. This simply boils down to what users want, and if there is enough deviation for it to make sense for multiple formats to exist. Having competition in open-source is every bit as healthy as having competition in the vendor space - with some exceptions:&#xA;&#xA;Exception 1: The projects don’t collectively build on an open specification. This enables vendor lock-in under the guise of being “open” simply because the source is available.&#xA;&#xA;Exception 2: The projects are not just doubled efforts of each other. This is a sad waste of time and adds analysis paralysis to users. If two projects have clear value added in different domains with some overlap, then the choice becomes clearer based on the use case.&#xA;&#xA;Exception 3: Any of the competing OSS projects primarily serve the needs of one or a few companies over the larger community. This exception is an anti-pattern of innovation brought up in the Innovator’s Dilemma. Focusing on the needs of the few will eventually force the majority to move to the next thing. Truly open projects will continue to evolve at the pace of the industry.&#xA;&#xA;Just because I’m touting that Apache Iceberg has “won the table format wars” with an open spec, does not mean I am discrediting the hard work done by the competing table formats or advocating not to use them. Delta Lake has made sizeable efforts to stay competitive on a feature-by-feature basis. I’m also still good friends with Mr. DeltaLake himself, Denny Lee. I hold no grudge against these amazing people trying to do better for their users and customers. I am also excited at the fact that all formats now have some level of interoperability for users outside of the Iceberg ecosystem!&#xA;&#xA;And…Hudi?&#xA;&#xA;I toiled with my take on Hudi. I really enjoyed working with the folks from Hudi and while I usually follow the “don’t say anything unless it’s nice” rule, it would be disingenuous not to give my real opinion on this matter. I even quoted Reddit user u/JudgingYouThisSecond to initially avoid saying my own words, but even his take still doesn’t quite capture my thoughts concisely:&#xA;&#xA;  Ultimately, there is room enough for there to be several winners in the table format space:&#xA;    Hudi, for folks who care about or need near-real-time ingestion and streaming. This project will likely be #3 in the overall pecking order as it&#39;s become less relevant and seems to be losing steam realative to the other two in this list (at least IMO)&#xA;  Delta Lake, for people who play a lot in the Databricks ecosystem and don&#39;t mind that Delta Lake (while OSS) may end up being a bit of a &#34;gateway drug&#34; into a pay-to-play world&#xA;  * Iceberg, for folks looking to avoid vendor lock-in and are looking for a table format with a growing community behind it.&#xA;    While my opinion may change as these projects evolve, I consider myself an &#34;Iceberg Guy&#34; at the moment.&#xA;    Comment by u/JudgingYouThisSecond in dataengineering&#xA;&#xA;I feel like the open-source Hudi project is just not in a state where I would recommend it to a friend. I once thought perhaps Hudi’s upserts made their complexity worth it, but both Delta and Iceberg have improved on this front. My opinion from supporting all three formats in the Trino community is that Hudi is an overly complex implementation with many leaky abstractions such as exposing Hadoop and other legacy dependencies (e.g. the KyroSerializer) and doesn’t actually provide proper transaction semantics.&#xA;&#xA;What I personally hope for is that we get to a point where we converge down to the minimum set of table format projects needed to scratch the different itches that shouldn’t be overlapping, and all adhering to the Iceberg specification. In essence, really only the Iceberg spec has won and Iceberg is currently the only implementation to support it. I would be thrilled to see Delta Lake and Hudi projects support the Iceberg spec and make this a fair race and make the new question, which Iceberg implementation won the most adoption? That game will be fun to play. Imagine Tabular, Databricks, Cloudera, and Onehouse all building on the same spec!&#xA;&#xA;Note: Please don’t respond with silly performance benchmarks with TPC (or any standardized dataset in a vendor-controlled benchmark) to suggest the performance of these benchmarks is relevant to this conversation. It’s not.&#xA;&#xA;Thanks for reading, and if you disagree and want to debate or love this and want to discuss it, reach out to me on LinkedIn, Twitter, or the Apache Iceberg Slack.&#xA;&#xA;Stay classy Data Folks!&#xA;&#xA;#iceberg #opensource #openstandard&#xA;&#xA;bits&#xA;&#xA;!--emailsub--]]&gt;</description>
      <content:encoded><![CDATA[<p><a href="https://images.unsplash.com/photo-1650434908449-9e7db28bbf5a?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwzMDAzMzh8MHwxfHNlYXJjaHwzfHxzdGFycyUyMGljZWJlcmd8ZW58MHx8fHwxNjgxNzg1OTE4&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080"><img src="https://images.unsplash.com/photo-1650434908449-9e7db28bbf5a?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwzMDAzMzh8MHwxfHNlYXJjaHwzfHxzdGFycyUyMGljZWJlcmd8ZW58MHx8fHwxNjgxNzg1OTE4&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=1080" alt="the night sky is lit up over the water" title="the night sky is lit up over the water"/></a></p>

<p>Photo by <a href="https://unsplash.com/@bot_va">Michail Dementiev</a> on <a href="https://unsplash.com/">Unsplash</a></p>

<p><strong>TL;DR:</strong> I believe Apache Iceberg won the table format wars, not because of a feature race, but primarily because of <a href="https://iceberg.apache.org/spec/">the open Iceberg spec</a>. There are <a href="https://iceberg.apache.org/docs/latest/partitioning/#icebergs-hidden-partitioning">some</a> <a href="https://iceberg.apache.org/spec/#snapshot-reference">features</a> only available in Iceberg due to the breaking of compatibility with Hive, which was also a contributing factor to the adoption of the implementation.</p>



<p><strong>Disclaimer:</strong> I am the Head of Developer Relations at Tabular and a Developer Advocate in both Apache Iceberg and Trino communities. All of my 🌶️ takes are my biased opinion and not necessarily the opinion of Tabular, the Apache Software Foundation, the Trino Software Foundation, or the communities I work with. This also goes into a bit of my personal story for leaving my previous company but relates to my reasoning so I offer you a TL;DR if you don’t care about the details.</p>



<h2 id="my-revelation-with-iceberg" id="my-revelation-with-iceberg">My revelation with Iceberg</h2>

<p>Two months ago, I made the difficult decision to leave <a href="https://www.starburst.io/">Starburst</a> which was hands down the best job I’ve ever had up to this point. Since I left I’ve had a lot of questions about my motivations for leaving and wanted to put some concerns to rest. This role allowed me to get deeply involved in open-source during working hours and showed me how I could aid the community to get traction on their work and drive the roadmap for many in the project. This was a new calling that overlapped with many altruist parts of how I define myself and was deeply rewarding.</p>

<p><a href="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38c6c7f7-dc35-4960-8808-02a4a3a723b7_2000x1015.png"><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F38c6c7f7-dc35-4960-8808-02a4a3a723b7_2000x1015.png" alt=""/></a></p>

<p>I made some incredible friends, some of which have become invaluable mentors during this process of learning the nuances and interplay between venture capital and an open-source community. So why did I leave this job that I love so much?</p>

<h2 id="apache-iceberg-baby" id="apache-iceberg-baby">Apache Iceberg Baby</h2>

<p>Let’s time-travel (<a href="https://iceberg.apache.org/docs/latest/branching/">pun intended</a>) to the <a href="https://trino.io/episodes/15.html">first Iceberg episode of the Trino Community Broadcast</a>. In true ADHD form, I crammed learning about <a href="https://iceberg.apache.org/">Apache Iceberg</a> well into the night before the broadcast with the creator of Iceberg, <a href="https://www.linkedin.com/in/rdblue/">Ryan Blue</a>. While setting up that demo, I really started to understand what a game-changer Iceberg was. I had heard the Trino users and maintainers talk about Iceberg replacing Hive but it just didn’t sink in for the first couple of months. I mean really, what could be better than Hive? 🥲</p>

<p>While researching I learned about <a href="https://iceberg.apache.org/docs/latest/partitioning/#icebergs-hidden-partitioning">hidden partitioning</a>, <a href="https://iceberg.apache.org/docs/latest/evolution/#schema-evolution">schema evolution</a>, and most importantly, <a href="https://iceberg.apache.org/spec/">the open specification</a>. The whole package was just such an elegant solution to problems that had caused me and many in the Trino community failed deployments and late-night calls. Just as I had the epiphany with Trino (<a href="https://trino.io/blog/2022/08/02/leaving-facebook-meta-best-for-trino.html">Presto at the time</a>) of how big of a productivity booster SQL queries over multiple systems were, I had a similar experience with Iceberg that night. Preaching the combination of these two became somewhat of a mission of mine after that.</p>

<p><a href="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea787b8f-f9fa-4cc0-8e26-c18868bdf0ff_1151x647.png"><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea787b8f-f9fa-4cc0-8e26-c18868bdf0ff_1151x647.png" alt=""/></a></p>

<h2 id="ansi-sql-iceberg-parquet-s3" id="ansi-sql-iceberg-parquet-s3">ANSI SQL + Iceberg + Parquet + S3</h2>

<p>Immediately after that show, <a href="https://trino.io/blog/2021/05/03/a-gentle-introduction-to-iceberg.html">I wrote a four-blog series</a> on Trino on Iceberg, <a href="https://www.youtube.com/watch?v=5-Q74rCX2Z8">did a talk</a>, and built out <a href="https://github.com/bitsondatadev/trino-getting-started/tree/main/iceberg/trino-iceberg-minio">the getting started repository for Iceberg</a>. I was rather hooked on the thought of these two technologies in tandem. You start out with a system that can connect to <a href="https://trino.io/docs/current/connector.html">any data source</a> you <a href="https://trino.io/docs/current/develop/spi-overview.html">throw at it</a> and sees it as yet another SQL table. Take that system and add a table format that interacts with all the popular analytics engines people use today from <a href="https://iceberg.apache.org/docs/latest/getting-started/">Spark</a>, <a href="https://www.snowflake.com/blog/single-platform-improves-performance-analytics-data-types/">Snowflake</a>, and <a href="https://py.iceberg.apache.org/api/#duckdb">DuckDB</a>, to Trino variants like <a href="https://docs.aws.amazon.com/emr/latest/ReleaseGuide/emr-iceberg.html">EMR</a>, <a href="https://docs.aws.amazon.com/athena/latest/ug/querying-iceberg.html">Athena</a>, and <a href="https://docs.starburst.io/latest/connector/iceberg.html">Starburst</a>.</p>

<h3 id="standards-all-the-way-down" id="standards-all-the-way-down">Standards all the way down</h3>

<p>This <a href="https://www.youtube.com/watch?v=2bkPMrjqxgs&amp;list=PLIlqnK97FLdtQEad5pi22BefNuaSeBt1e">approach to data virtualization</a> is so interesting as each system offers full leverage over vendors trying to lock you in their particular query language or storage format. It pushes the incentives for vendors to support these open standards which puts them in a seemingly vulnerable position compared to locking users in. However, that’s a fallacy I hope vendors will slowly begin to understand is not true. With the open S3 storage standard, open file standards like Parquet, open table standards like Iceberg, and the ANSI SQL spec closely followed by Trino, the entire analytics warehouse has become a modular stack of truly open components. This is not just open in the sense of the freedom to use and contribute to a project, but the open standard that enables you the freedom to simply move between different projects.</p>

<p>This new freedom gives the user the features of a data warehouse, with the scalability of the cloud, and a free market of a la carte services to handle your needs at whatever price point you need at any given time. All users need to do in this new ecosystem is shop around and choose any open project or vendor that implements the open standard and your migration cost will be practically non-existent. This is the ultimate definition of future-proofing your architecture.</p>

<p><a href="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03492f03-0475-4682-addd-90623a616c64_500x500.jpeg"><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F03492f03-0475-4682-addd-90623a616c64_500x500.jpeg" alt=""/></a></p>



<h2 id="back-to-why-i-left-starburst" id="back-to-why-i-left-starburst">Back to why I left Starburst</h2>

<h3 id="trino-community" id="trino-community">Trino Community</h3>

<p>I’ll quickly tie up why I left Starburst before I reveal why Iceberg won the table format wars. For the last three years, I have worked on building awareness around Trino. My partner in crime, <a href="https://www.linkedin.com/in/manfredmoser/">Manfred Moser</a>, had been in the Trino community and <a href="https://simpligility.ca/2022/10/trino-the-definitive-guide-2nd-edition/">literally wrote the book on Trino</a>. Together we spent long days and nights growing the Trino community. I loved every minute of it and honestly didn’t see myself leaving Starburst or shifting focus from Trino until it became an analytics organization standard.</p>

<p>Something became apparent though. Trino <a href="https://trino.io/blog/2023/01/10/trino-2022-the-rabbit-reflects.html">community health was thriving</a>, and there were many <a href="https://github.com/trinodb/trino/pull/17940">organic</a> <a href="https://www.youtube.com/watch?v=JMUtPl-cMRc">product</a> <a href="https://github.com/trinodb/trino/pull/17909">movements</a> taking place in the Trino community. <a href="https://www.linkedin.com/in/cole-m-bowden/">Cole Bowden</a> was boosting the Trino release process getting us to cutting Trino releases every 1-2 weeks which is unprecedented in open-source. Cole, Manfred, and I did a manual scan over the pull requests and gracefully closed or revived outdated or abandoned pull requests. The Trino community is in great shape.</p>

<h3 id="iceberg-community" id="iceberg-community">Iceberg Community</h3>

<p>As I looked at Iceberg, the adoption and awareness were growing at an unprecedented rate with <a href="https://www.snowflake.com/blog/expanding-the-data-cloud-with-apache-iceberg/">Snowflake</a>, <a href="https://cloud.google.com/bigquery/docs/iceberg-tables#create-using-biglake-metastore">BigQuery</a>, <a href="https://www.techtarget.com/searchdatamanagement/news/252509796/Starburst-Enterprise-brings-Apache-Iceberg-to-data-lakehouse">Starburst</a>, and <a href="https://docs.aws.amazon.com/athena/latest/ug/querying-iceberg.html">Athena</a> all announcing support between 2021 and 2022. However, nothing was moving the needle forward from a developer experience perspective. There was some initial amazing work done by <a href="https://www.linkedin.com/in/sredai/">Sam Redai</a>, but there was still so much to be done. I noticed the <a href="https://github.com/apache/iceberg/issues?q=is%3Aopen+is%3Aissue+label%3Adocs">Iceberg documentation needed improvement</a>. While many vendors were advocating for Iceberg, there was nobody putting in consistent work to the vanilla Iceberg site. PMCs like Ryan Blue, Jack Ye, Russel Spitzer, Dan Weeks, and many others are doing a great shared job of driving roadmap features for Iceberg, but no individual currently has the time to dedicate to the cat herding, improving communication in the project, or bettering the developer and contributor experience for users. Since Trino was on stable ground it felt imperative to move to Iceberg and fill in these gaps. When Ryan approached me with a Head of DevRel position at Tabular, I couldn’t pass up the opportunity. To be clear I left Starburst but not the Trino community. Being at Iceberg also helped me in my mission to continue forging these two technologies that I believe in so much.</p>

<h2 id="tell-us-why-iceberg-won-already" id="tell-us-why-iceberg-won-already">Tell us why Iceberg won already!</h2>

<p>Moving back to the meat of the subject. My first blog in the Trino community covered what I once called, <a href="https://trino.io/blog/2020/10/20/intro-to-hive-connector.html">the invisible Hive spec</a> to alleviate confusion around why Trino would need a “Hive” connector if it’s a query engine itself. The reason we called the Hive connector as such is that it translated Parquet files sitting in an S3 object store into a schema that could be read and modified via a query engine that knew the Hive spec. This had nothing to do with Hive the query engine, but Hive the spec. Why was the Hive spec <em>invisible</em> 👻? Because nobody wrote it down. It was in the minds of the engineers who put Hive together, spread across Cloudera forums by engineers who had bashed their heads against the wall and reverse-engineered the binaries to understand this “spec”.</p>

<h3 id="why-do-you-even-need-a-spec" id="why-do-you-even-need-a-spec">Why do you even need a spec?</h3>

<p>Having an “invisible” spec was rather problematic, as every implementation of that spec ran on different assumptions. I spent days <a href="https://community.cloudera.com/t5/forums/filteredbylabelpage/board-id/Questions/label-name/Apache%20Hive?type=everything">searching Hive solutions on Hortonworks/Cloudera forums</a> trying to solve an error with solutions that for whatever reason, didn’t work on my instance of Hive. There were also implicit dependencies required with using the Hive spec. It’s actually incorrect to call the Hive spec as such because a spec should have independence of platform, programming language, and hardware, while including minimal implementation details not required for interoperability between implementations. SQL, for instance, is a declarative language that runs on systems written in, C++, Rust, Java, and doesn’t get into the business of telling query engines how to answer that query, just what behavior is expected.</p>

<h3 id="the-open-spec-is-why-iceberg-won" id="the-open-spec-is-why-iceberg-won">The open spec is why Iceberg won</h3>

<p>By the time there were various iterations of Hive from the Hadoop and Big Data Boom, there was not a very central source to lay a stake in the ground to standardize this spec. While you may imagine that we would have learned our lesson, until Iceberg, none of the projects officially formalized their assumptions and specifications for their project. Delta Lake and Hudi extended the Hive models to make migration from Hive simpler but kept a few of the issues that Hive introduced, like exposing the partitioning format to users running analysis.</p>

<h3 id="open-specifications-and-vendor-politics" id="open-specifications-and-vendor-politics">Open specifications and vendor politics</h3>

<p>You may seem skeptical that an open specification for a table format holds such weight, but it crept its way into <a href="https://www.reddit.com/r/dataengineering/comments/13wqhby/databricks_and_snowflake_stop_fighting_on_social/">a well-known feud</a> between two large analytics vendors of the day, Databricks and Snowflake. Databricks has been the leading vendor in the data lakehouse market while Snowflake dominated the data warehouse market. Snowflake’s <a href="https://web.archive.org/web/20230201001308/https://www.snowflake.com/guides/what-data-lakehouse">original strategy</a> initially involved encouraging movement from the data lakehouse market back to the data warehouse market, while Databrick’s <a href="https://web.archive.org/web/20230626115324/https://www.databricks.com/glossary/data-warehouse">original strategy</a> was to do the exact opposite. This conveyed the outdated trap that many vendors still fall prey to. This idea is that locking users in will help your business and stakeholders reduce churn and keep customers in the long run. Vendor lock-in was once a decades-long play, but as B2C consumerism expectations creep into B2B consumerism, we are seeing a gradual shift of practitioners demanding interoperability from vendors to give them the level of autonomy they have experienced with open-source projects.</p>

<p>This surfaced with Snowflake when they announced that they would be offering <a href="https://www.snowflake.com/blog/expanding-the-data-cloud-with-apache-iceberg/">support for an Iceberg external table functionality</a> in 2022. At first, I raised my eyebrow at this as I figured this was a feeble attempt for Snowflake to market itself as an open-friendly warehouse when the external table would be nothing but an easy way to migrate data from the data lake to Snowflake. Whatever their motives, this was good visibility for Iceberg and I was thrilled that Snowflake was showcasing the need for an open specification, gimmick or not. This even pressured other competing data warehouses like BigQuery to add Iceberg support.</p>



<h3 id="the-final-signal-that-the-open-iceberg-spec-won" id="the-final-signal-that-the-open-iceberg-spec-won">The final signal that the open Iceberg spec won</h3>

<p>I didn’t, however, expect to see what took place at both Snowflake Summit and Data + AI Summit this year in 2023. If you don’t know these are Snowflake and Databricks’ big events that happened on the same week as an extension of their feud. There were <a href="https://www.reddit.com/r/dataengineering/comments/13wqhby/comment/jmdo259/">already some hints that Snowflake had dropped</a> to signal that they were ramping up their Iceberg support but were doing a great job at keeping it under wraps. The final reveal came at Snowflake Summit; Snowflake now offers <a href="https://www.linkedin.com/feed/update/urn:li:activity:7079822990607089665">managed Iceberg catalog support</a>.</p>

<p><a href="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9168cb20-ff36-4f58-873f-1a7a568fc75f_4096x3072.png"><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9168cb20-ff36-4f58-873f-1a7a568fc75f_4096x3072.png" alt=""/></a></p>

<p>I was thrilled to see that finally, a data warehouse vendor understands there’s no way to beat open standards and they should adopt open storage as part of their core business model. What’s even more about this picture is they show both Trino and Spark engines as open compute alternatives to their own engine. This was beyond my expectations for Snowflake, and definitely showed me they were heading in the right direction for their customers.</p>

<p>Meanwhile, in a California town not far away, Databricks would also have a response to Snowflake’s announcement stored up. <a href="https://www.databricks.com/company/newsroom/press-releases/announcing-delta-lake-30-new-universal-format-offers-automatic">Delta Lake 3.0 was announced</a> and it now supports compatibility across Delta Lake, Iceberg, and Hudi (be it <a href="https://github.com/delta-io/delta/commit/54492016f0eb9c12d25cbbdcc85804fb0a5d9a3a">limited compatibility for Iceberg for now</a>). Whatever happens, there is now opportunity for Databricks customers to trial Iceberg, and this should excite everyone. We’re one step closer to having this spec become the common denominator. With both of these moves from a company that initially only wanted their proprietary format to win and a company that built on a competing format, I have the opinion that Iceberg has won the format wars.</p>

<p>Now I don’t want to act like these vendors simply have users’ best interests in mind. All they want is for you to make the decision to choose them. I work for a vendor, we also want your money. What I am seeing though is that now the industry is trending towards incentivizing openness as more customers demand it. In order for companies to stay ahead, they must embrace this fact rather than fight it. What is rather historical about this moment in time is that since the dawn of analytics and the data warehouse, there has been vendor lock-in on many fronts of the analytics market. This to me, signals the nail in the coffin of any capability for vendors to do this on the level playing field of open standards. It’s now up to the vendors to keep you happy and continuously stop you from churning. This in the long run is good for users and vendors and will ultimately drive better products.</p>

<p>One quick aside, some may mention that Hudi recently added a “spec” so why does Iceberg having a spec give it the winning vote? I recommend you go back to reading the purpose of an open spec section earlier in this blog, then look at both the Iceberg <a href="https://web.archive.org/web/20230521215437/https://iceberg.apache.org/spec/">spec</a> and the Hudi <a href="https://web.archive.org/web/20230609034725/https://hudi.apache.org/tech-specs/">“spec”</a> and determine which one satisfies the criteria. Hudi’s “spec” <a href="https://web.archive.org/web/20230609034725/https://hudi.apache.org/tech-specs/#log-file-format">exposes Java dependencies</a> making it unusable for any system not running on the JDK, doesn’t clarify schema, and has <a href="https://web.archive.org/web/20230609034725/https://hudi.apache.org/tech-specs/#reader-expectations">a lot of implementation details</a> rather than leaving that to the systems that implement it. This “spec” is something closer to an architecture document than an open spec.</p>

<h3 id="will-the-iceberg-project-drive-delta-lake-and-hudi-to-nonexistence" id="will-the-iceberg-project-drive-delta-lake-and-hudi-to-nonexistence">Will the Iceberg project drive Delta Lake and Hudi to nonexistence?</h3>

<p>Maybe, or maybe not. That really depends on the feature set that these three table formats offer and what the <em><strong>actual value</strong></em> they bring to the users is. In other words, you decide what’s important, we decide what we’re going to support, and vendors and the larger data community decide who stays and who goes. This simply boils down to what users want, and if there is enough deviation for it to make sense for multiple formats to exist. Having competition in open-source is every bit as healthy as having competition in the vendor space – with some exceptions:</p>

<p>Exception 1: The projects don’t collectively build on an open specification. This enables vendor lock-in under the guise of being “open” simply because the source is available.</p>

<p>Exception 2: The projects are not just doubled efforts of each other. This is a sad waste of time and adds analysis paralysis to users. If two projects have clear value added in different domains with some overlap, then the choice becomes clearer based on the use case.</p>

<p>Exception 3: Any of the competing OSS projects primarily serve the needs of one or a few companies over the larger community. This exception is an anti-pattern of innovation brought up in <a href="https://en.wikipedia.org/wiki/The_Innovator%27s_Dilemma">the Innovator’s Dilemma</a>. Focusing on the needs of the few will eventually force the majority to move to the next thing. Truly open projects will continue to evolve at the pace of the industry.</p>

<p>Just because I’m touting that Apache Iceberg has “won the table format wars” with an open spec, does not mean I am discrediting the hard work done by the competing table formats or advocating not to use them. Delta Lake has made <a href="https://www.databricks.com/blog/2022/06/30/open-sourcing-all-of-delta-lake.html">sizeable efforts to stay competitive</a> on a feature-by-feature basis. I’m also <a href="https://www.linkedin.com/feed/update/urn:li:ugcPost:7055051825321832448?commentUrn=urn%3Ali%3Acomment%3A%28ugcPost%3A7055051825321832448%2C7055173804460818432%29&amp;replyUrn=urn%3Ali%3Acomment%3A%28ugcPost%3A7055051825321832448%2C7055245121428094976%29&amp;dashCommentUrn=urn%3Ali%3Afsd_comment%3A%287055173804460818432%2Curn%3Ali%3AugcPost%3A7055051825321832448%29&amp;dashReplyUrn=urn%3Ali%3Afsd_comment%3A%287055245121428094976%2Curn%3Ali%3AugcPost%3A7055051825321832448%29">still good friends</a> with Mr. DeltaLake himself, <a href="https://www.linkedin.com/in/dennyglee/">Denny Lee</a>. I hold no grudge against these amazing people trying to do better for their users and customers. I am also excited at the fact that all formats now have some level of interoperability for users outside of the Iceberg ecosystem!</p>

<p><a href="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43f2c01a-a8ec-4368-b106-a351c1b4f3fb_1280x960.jpeg"><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43f2c01a-a8ec-4368-b106-a351c1b4f3fb_1280x960.jpeg" alt=""/></a></p>

<h3 id="and-hudi" id="and-hudi">And…Hudi?</h3>

<p>I toiled with my take on Hudi. I <a href="https://www.youtube.com/watch?v=aL3PfMjvFM4">really enjoyed working with the folks from Hudi</a> and while I usually follow the “don’t say anything unless it’s nice” rule, it would be disingenuous not to give my real opinion on this matter. I even quoted Reddit user u/JudgingYouThisSecond to initially avoid saying my own words, but even his take still doesn’t quite capture my thoughts concisely:</p>

<blockquote><p>Ultimately, there is room enough for there to be several winners in the table format space:</p>
<ul><li>Hudi, for folks who care about or need near-real-time ingestion and streaming. This project will likely be #3 in the overall pecking order as it&#39;s become less relevant and seems to be losing steam realative to the other two in this list (at least IMO)</li>
<li>Delta Lake, for people who play a lot in the Databricks ecosystem and don&#39;t mind that Delta Lake (while OSS) may end up being a bit of a “gateway drug” into a pay-to-play world</li>
<li>Iceberg, for folks looking to avoid vendor lock-in and are looking for a table format with a growing community behind it.</li></ul>

<p>While my opinion may change as these projects evolve, I consider myself an “Iceberg Guy” at the moment.</p>

<p><a href="https://www.reddit.com/r/dataengineering/comments/13gevpu/whats_the_view_on_apache_iceberg/jjzntu1/">Comment</a> by <a href="https://www.reddit.com/user/JudgingYouThisSecond">u/JudgingYouThisSecond</a> in <a href="https://www.reddit.com/r/dataengineering/">dataengineering</a></p></blockquote>

<p>I feel like the open-source Hudi project is just not in a state where I would recommend it to a friend. I once thought perhaps Hudi’s upserts made their complexity worth it, but both <a href="https://docs.delta.io/latest/delta-streaming.html#table-streaming-reads-and-writes">Delta</a> and <a href="https://lists.apache.org/thread/1pjn2010r45lq6tk7jn2g4klg0xv68zn">Iceberg</a> have improved on this front. My opinion from supporting all three formats in the Trino community is that Hudi is <a href="https://www.reddit.com/r/dataengineering/comments/11qvpnl/is_hudi_a_hardcomplex_tool_as_it_seems/">an overly complex implementation</a> with many leaky abstractions such as exposing Hadoop and other legacy dependencies (e.g. the KyroSerializer) and doesn’t actually provide proper transaction semantics.</p>

<p>What I personally hope for is that we get to a point where we converge down to the minimum set of table format projects needed to scratch the different itches that shouldn’t be overlapping, and all adhering to the Iceberg specification. In essence, really only the Iceberg spec has won and Iceberg is currently the only implementation to support it. I would be thrilled to see Delta Lake and Hudi projects support the Iceberg spec and make this a fair race and make the new question, which Iceberg implementation won the most adoption? That game will be fun to play. Imagine Tabular, Databricks, Cloudera, and Onehouse all building on the same spec!</p>

<p>Note: Please don’t respond with <a href="https://write.as/bitsondatadev/what-is-benchmarketing-and-why-is-it-bad">silly performance benchmarks with TPC</a> (or any standardized dataset in a vendor-controlled benchmark) to suggest the performance of these benchmarks is relevant to this conversation. It’s not.</p>

<p>Thanks for reading, and if you disagree and want to debate or love this and want to discuss it, reach out to me on <a href="https://www.linkedin.com/in/bitsondatadev/">LinkedIn</a>, <a href="https://twitter.com/bitsondatadev">Twitter</a>, or the <a href="https://join.slack.com/t/apache-iceberg/shared_invite/zt-1uva9gyp1-TrLQl7o~nZ5PsTVgl6uoEQ">Apache Iceberg Slack</a>.</p>

<p>Stay classy Data Folks!</p>

<p><a href="https://bitsondata.dev/tag:iceberg" class="hashtag"><span>#</span><span class="p-category">iceberg</span></a> <a href="https://bitsondata.dev/tag:opensource" class="hashtag"><span>#</span><span class="p-category">opensource</span></a> <a href="https://bitsondata.dev/tag:openstandard" class="hashtag"><span>#</span><span class="p-category">openstandard</span></a></p>

<p><em>bits</em></p>


]]></content:encoded>
      <guid>https://bitsondata.dev/iceberg-won-the-table-format-war</guid>
      <pubDate>Wed, 05 Jul 2023 17:00:00 +0000</pubDate>
    </item>
    <item>
      <title>Intro to Trino for the Trinewbie</title>
      <link>https://bitsondata.dev/intro-to-trino-for-the-trinewbie?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[Learn how to quickly join data across multiple sources&#xA;&#xA;If you haven’t heard of Trino before, it is a query engine that speaks the language of many genres of databases. As such, Trino is commonly used to provide fast ad-hoc queries across heterogeneous data sources. Trino’s initial use case was built around replacing the Hive runtime engine to allow for faster querying of Big Data warehouses and data lakes. This may be the first time you have heard of Trino, but you’ve likely heard of the project from which it was “forklifted”, Presto. If you want to learn more about why the creators of Presto now work on Trino (formerly PrestoSQL) you can read the renaming blog that they produced earlier this year. Before you commit too much to this blog, I’d like to let you know why you should even care about Trino.&#xA;&#xA;!--more--&#xA;&#xA;!--emailsub--&#xA;&#xA;So what is Trino anyways?&#xA;&#xA;The first thing I like to make sure people know about when discussing Trino is that it is a SQL query engine, but not a SQL database. What does that mean? Traditional databases typically consist of a query engine and a storage engine. Trino is just a query engine and does not store data. Instead, Trino interacts with various databases that store their own data in their own formats. Trino parses and analyzes the SQL query you pass in, creates and optimizes a query execution plan that includes the data sources, and then schedules worker nodes that are able to intelligently query the underlying databases they connect to.&#xA;&#xA;I say intelligently, specifically talking about pushdown queries. That’s right, the most intelligent thing for Trino to do is to avoid making more work for itself, and try to offload that work to the underlying database. This makes sense as the underlying databases generally have special indexes and data that are stored in a specific format to optimize the read time. It would be silly of Trino to ignore all of that optimized reading capability and do a linear scan of all the data to run the query itself. The goal in most optimizations for Trino is to push down the query to the database and only get back the smallest amount of data needed to join with another dataset from another database, do some further Trino specific processing, or simply return as the correct result set for the query.&#xA;&#xA;Query all the things&#xA;&#xA;So I still have not really answered your question of why you should care about Trino. The short answer is, Trino acts as a single access point to query all the things. Yup. Oh, and it’s super fast at ad-hoc queries over various data sources including data lakes (e.g. Iceberg/Databricks) or data warehouses (e.g. Hive/Snowflake). It has a connector architecture that allows it to speak the language of a whole bunch of databases. If you have a special use case, you can write your own connector that abstracts any database or service away to just be another table in Trino’s domain. Pretty cool right? But that’s actually rarely needed because the most common databases already have a connector written for them. If not, more connectors are getting added by Trino’s open source community every few months.&#xA;&#xA;To make the benefits of running federated queries a bit more tangible, I will present an example. Trino brings users the ability to map standardized ANSI SQL query to query databases that have a custom query DSL like Elasticsearch. With Trino it’s incredibly simple to set up an Elasticsearch catalog and start running SQL queries on it. If that doesn’t blow your mind, let me explain why that’s so powerful.&#xA;&#xA;Imagine you have five different data stores, each with its own independent query language. Your data science or analyst team just wants access to these data stores. It would take a ridiculous amount of time for them to have to go to each data system individually, look up the different commands to pull data out of each one, and dump the data into one location and clean it up so that they can actually run meaningful queries. With Trino all they need to use is SQL to access them through Trino. Also, it doesn’t just stop at accessing the data, your data science team is also able to join data across tables of different databases like a search engine like Elasticsearch with an operational database like MySQL. Further, using Trino even enables joining data sources with themselves where joins are not supported, like in Elasticsearch and MongoDB. Did it happen yet? Is your mind blown?&#xA;&#xA;Getting Started with Trino&#xA;&#xA;So what is required to give Trino a test drive? Relative to many open-source database projects, Trino is one of the more simple projects to install, but this still doesn’t mean it is easy. An important element to a successful project is how it adapts to newer users and expands capability for growth and adoption. This really pushes the importance of making sure that there are multiple avenues of entry into using a product all of which have varying levels of difficulty, cost, customizability, interoperability, and scalability. As you increase in the level of customizability, interoperability, and scalability, you will generally see an increase in difficulty or cost and vice versa. Luckily, when you are starting out, you just really need to play with Trino.&#xA;&#xA;Image added by Author&#xA;&#xA;The low-cost and low difficulty way to try out Trino is to use Docker containers. The nice thing about these containers is that you don’t have to really know anything about the installation process of Trino to play around with Trino. While many enjoy poking around documentation and working with Trino to get it set up, it may not be for all. I certainly have my days where I prefer a nice chill CLI sesh and other days where I just need to opt-out. If you want to skip to the Easy Button way to deploy Trino (hint, it’s the SaaS deployment) then skip the next few sections.&#xA;&#xA;!--emailsub--&#xA;&#xA;Using Trino With Docker&#xA;&#xA;Trino ships with a Docker image that does a lot of the setup necessary for Trino to run. Outside of simply running a docker container, there are a few things that need to happen for setup. First, in order to use a database like MySQL, we actually need to run a MySQL container as well using the official mysql image. There is a trino-getting-started repository that contains a lot of the setup needed for using Trino on your own computer or setting it up on a test server as a proof of concept. Clone this repository and follow the instructions in the README to install Docker if it is not already.&#xA;&#xA;You can actually run a query before learning the specifics of how this compose file works. Before you run the query, you will need to run the mysql and trino-coordinator instances. To do this, navigate to the mysql/trino-mysql/ directory that contains the docker-compose.yml and run:&#xA;&#xA;docker-compose up -d&#xA;&#xA;Running your first query!&#xA;&#xA;Now that you have Trino running in Docker, you need to open a session to access it. The easiest way to do this is via a console. Run the following Docker command to connect to a terminal on the coordinator:&#xA;&#xA;docker container exec -it trino-mysqltrino-coordinator1 trino&#xA;&#xA;This will bring you to the Trino terminal.&#xA;&#xA;trino  Your first query will actually be to generate data from the tpch catalog and then query the data that was loaded into mysql catalog. In the terminal, run the following two queries:&#xA;&#xA;CREATE TABLE mysql.tiny.customer&#xA;AS SELECT * FROM tpch.tiny.customer;&#xA;&#xA;SELECT custkey, name, nationkey, phone &#xA;FROM mysql.tiny.customer LIMIT 5;&#xA;&#xA;The output should look like this.&#xA;&#xA;|custkey|name              |nationkey|phone          |&#xA;|-------|------------------|---------|---------------|&#xA;|751    |Customer#000000751|0        |10-658-550-2257|&#xA;|752    |Customer#000000752|8        |18-924-993-6038|&#xA;|753    |Customer#000000753|17       |27-817-126-3646|&#xA;|754    |Customer#000000754|0        |10-646-595-5871|&#xA;|755    |Customer#000000755|16       |26-395-247-2207|&#xA;&#xA;Congrats! You just ran your first query on Trino. Did you feel the rush!? Okay well, technically we just copied data from a data generation connector and moved it into a MySQL database and queried that back out. It’s fine if this simple exercise didn’t send goosebumps flying down your spine but hopefully, you can extrapolate the possibilities when connecting to other datasets.&#xA;&#xA;A good initial exercise to study the compose file and directories before jumping into the Trino installation documentation. Let’s see how this was possible by breaking down the docker-compose file that you just ran.&#xA;&#xA;version: &#39;3.7&#39;&#xA;services:&#xA;  trino-coordinator:&#xA;    image: &#39;trinodb/trino:latest&#39;&#xA;    hostname: trino-coordinator&#xA;    ports:&#xA;      &#39;8080:8080&#39;&#xA;    volumes:&#xA;      ./etc:/etc/trino&#xA;    networks:&#xA;      trino-network&#xA;&#xA;  mysql:&#xA;    image: mysql:latest&#xA;    hostname: mysql&#xA;    environment:&#xA;      MYSQLROOTPASSWORD: admin&#xA;      MYSQLUSER: admin&#xA;      MYSQLPASSWORD: admin&#xA;      MYSQLDATABASE: tiny&#xA;    ports:&#xA;      &#39;3306:3306&#39;&#xA;    networks:&#xA;      trino-network&#xA;networks:&#xA;  trino-network:&#xA;    driver: bridge&#xA;&#xA;Notice that the hostname of mysql matches the instance name, and the mysql instance is on the trino-network that the trino-coordinator instance will also join. Also notice that the mysql image exposes port 3306 on the network.&#xA;&#xA;Finally, we will use the trinodb/trino image for the trino-coordinator instance, and use the volumes option to map our local custom configurations for Trino to the /etc/trino directory we discuss further down in the Trino Configuration section. Trino should also be added to the trino-network and expose ports 8080 which is how external clients can access Trino. Below is an example of the docker-compose.yml file. The full configurations can be found in this getting started with Trino repository.&#xA;&#xA;These instructions are a basic overview of the more complete installation instructions if you’re really going for it! If you’re not that interested in the installation, feel free to skip ahead to the Deploying Trino at Scale with Kubernetes section. If you’d rather not deal with Kubernetes I offer you another pass to the easy button section of this blog.&#xA;&#xA;Trino requirements&#xA;&#xA;The first requirement is that Trino must be run on a POSIX-compliant system such as Linux or Unix. There are some folks in the community that have gotten Trino to run on Windows for testing using runtime environments like cygwin but this is not supported officially. However, in our world of containerization, this is less of an issue and you will be able to at least test this on Docker no matter which operating system you use.&#xA;&#xA;Trino is written in Java and so it requires the Java Runtime Environment (JRE). Trino requires a 64-bit version of Java 11, with a minimum required version of 11.0.7. Newer patch versions such as 11.0.8 or 11.0.9 are recommended. The launch scripts for Trino bin/launcher, also require python version 2.6.x, 2.7.x, or 3.x.&#xA;&#xA;Trino Configuration&#xA;&#xA;To configure Trino, you need to first know the Trino configuration directory. If you were installing Trino by hand, the default would be in a etc/ directory relative to the installation directory. For our example, I’m going to use the default installation directory of the Trino Docker image, which is set in the run-trino script as /etc/trino. We need to create four files underneath this base directory. I will describe what these files do and you can see an example in the docker image I have created below.&#xA;&#xA;config.properties — This is the primary configuration for each node in the trino cluster. There are plenty of options that can be set here, but you’ll typically want to use the default settings when testing. The required configurations include indicating if the node is the coordinator, setting the http port that Trino communicates on, and the discovery node url so that Trino servers can find each other.&#xA;&#xA;jvm.config — This configuration contains the command line arguments you will pass down to the java process that runs Trino.&#xA;&#xA;log.properties — This configuration is helpful to indicate the log levels of various java classes in Trino. It can be left empty to use the default log level for all classes.&#xA;&#xA;node.properties — This configuration is used to uniquely identify nodes in the cluster and specify locations of directories in the node.&#xA;&#xA;The next directory you need to know about is the catalog/ directory, located in the root configuration directory. In the docker container, it will be in /etc/trino/catalog. This is the directory that will contain the catalog configurations that Trino will use to connect to the different data sources. For our example, we’ll configure two catalogs, the mysql catalog, and the tpch catalog. The tpch catalog is a simple data generation catalog that simply needs the conector.name property to be configured and is located in /etc/trino/catalog/tpch.properties.&#xA;&#xA;tpch.properties&#xA;&#xA;connector.name=tpch&#xA;&#xA;The mysql catalog just needs the connector.name to specify which connector plugin to use, the connection-url property to point to the mysql instance, and the connection-user and connection-password properties for the mysql user.&#xA;&#xA;mysql.properties&#xA;&#xA;connector.name=mysql&#xA;connection-url=jdbc:mysql://mysql:3306&#xA;connection-user=root&#xA;connection-password=admin&#xA;&#xA;Note: the name of the configuration file becomes the name of the catalog in Trino. If you are familiar with MySQL, you are likely to know that MySQL supports a two-tiered containment hierarchy, though you may have never known it was called that. This containment hierarchy refers to databases and tables. The first tier of the hierarchy is the tables, while the second tier consists of databases. A database contains multiple tables and therefore two tables can have the same name provided they live under a different database.&#xA;&#xA;Image by Author&#xA;&#xA;Since Trino has to connect to multiple databases, it supports a three-tiered containment hierarchy. Rather than call the second tier, databases, Trino refers to this tier as schemas. So a database in MySQL is equivalent to a schema in Trino. The third tier allows Trino to distinguish between multiple underlying data sources which are made of catalogs. Since the file provided to Trino is called mysql.properties it automatically names the catalog mysql without the .properties file type. To query the customer table in MySQL under the tiny you specify the following table name mysql.tiny.customer.&#xA;&#xA;If you’ve reached this far, congratulations, you now know how to set up catalogs and query them through Trino! The benefits at this point should be clear, and making a proof of concept is easy to do this way. It’s time to put together that proof of concept for your team and your boss! What next though? How do you actually get this deployed in a reproducible and scalable manner? The next section covers a brief overview of faster ways to get Trino deployed at scale.&#xA;&#xA;!--emailsub--&#xA;&#xA;Deploying Trino at Scale with Kubernetes&#xA;&#xA;Up to this point, this post only describes the deployment process. What about after that once you’ve deployed Trino to production and you slowly onboard engineering, BI/Analytics, and your data science teams. As many Trino users have experienced, the demand on your Trino cluster grows quickly as it becomes the single point of access to all of your data. This is where these small proof-of-concept size installations start to fall apart and you will need something more pliable to scale as your system starts to take on heavier workloads.&#xA;&#xA;You will need to monitor your cluster and will likely need to stand up other services that run these monitoring tasks. This also applies to running other systems for security and authentication management. This list of complexity grows as you consider all of these systems need to scale and adapt around the growing Trino clusters. You may, for instance, consider deploying multiple clusters to handle different workloads, or possibly running tens or hundreds of Trino clusters to provide a self-service platform to provide isolated tenancy in your platform.&#xA;&#xA;The solution to express all of these complex scenarios as the configuration is already solved by using an orchestration platform like Kubernetes, and its package manager project, Helm. Kubernetes offers a powerful way to express all the complex adaptable infrastructures based on your use cases.&#xA;&#xA;In the interest of brevity, I will not include the full set of instructions on how to run a helm chart or cover the basics of running Trino on Kubernetes. Rather, I will refer you to an episode of Trino Community Broadcast that discusses Kubernetes, the community helm chart, and the basics of running Trino on Kubernetes. In the interest of transparency, the official Trino helm charts are still in an early phase of development. There is a very popular community-contributed helm chart that is adapted by many users to suit their needs and it is currently the best open source option for self-managed deployments of Trino. If you decide to take this route, proceed with caution and know that there is development to support the helm deployments moving forward.&#xA;&#xA;While this will provide all the tools to enable a well-suited engineering department to run and maintain their own Trino cluster, this begs the question, based on your engineering team size, should you and your company be investing costly data engineer hours into maintaining, scaling, and hacking required to keep a full-size production infrastructure afloat?&#xA;&#xA;Starburst Galaxy: The Easy Button method of deploying and maintaining Trino&#xA;&#xA;Full Disclosure: This blog post was originally written while I was working at Starburst. I still stand by Starburst Galaxy as one of the better options but I will add the caveat that it depends on your use case and things change so reach out if you need my latest thoughts on the matter. That said, Galaxy is the general purpose version of Trino the creators never got to build at Facebook. If you have custom features you need that you&#39;d like to contribute, a lot of folks run an open source cluster in testing and production is run by Starburst. You can then test and develop features to contribute to open source that will eventually upstream to Galaxy, Athena, or any other Trino variant.&#xA;&#xA;Image By: lostvegas, License: CC BY-NC-ND 2.0&#xA;&#xA;As mentioned, Trino has a relatively_ simple deployment setup, with an emphasis on relatively. This blog really only hits the tip of the iceberg when it comes to the complexity involved in managing and scaling Trino. While it is certainly possible to manage running Trino and even do so at scale with helm charts in Kubernetes, it is still a difficult setup for Trinewbies and difficult to maintain and scale for those who already have experience maintaining Trino. I experienced firsthand many of these difficulties myself when I began my Trino journey years ago and started on my own quest to help others overcome some of these challenges. This is what led me to cross paths with Starburst, the company behind the SaaS Trino platform Galaxy.&#xA;&#xA;Galaxy makes Trino accessible to companies having difficulties scaling and customizing Trino to their needs. Unless you are in a company that houses a massive data platform and you have dedicated data and DevOps engineers to each system in your platform, many of these options won’t be feasible for you in the long run.&#xA;&#xA;One thing to make clear is that a Galaxy cluster is really just a Trino cluster on demand. Outside of managing the scaling policies, to avoid any surprises on your cloud bill, you really don’t have to think about scaling Trino up or down, or suspending it when it is not in use. The beautiful thing about Trino and therefore Galaxy is that it is an ephemeral compute engine much like AWS Lambda that you can quickly spin up or down. Not only are you able to run ad-hoc and federated queries over disparate data sources, but now you can also run the infrastructure for those queries on-demand with almost no cost to your engineering team’s time.&#xA;&#xA;Getting Started With Galaxy&#xA;&#xA;Here’s a quick getting started guide with the Starburst Galaxy that mirrors the setup we realized with the Docker example above with Trino and MySQL.&#xA;&#xA;Set up a trial of Galaxy by filling in your information at the bottom of the Galaxy information page.&#xA;Once you receive a link, you will see this sign-up screen. Fill out the email address, enter the pin sent to the email, and choose the domain for your cluster.&#xA;The rest of the tutorial is provided in the video below provides a basic demo of what you’ll need to do to get started.&#xA;&#xA;This introduction may feel a bit underwhelming but extrapolate being able to run federated queries across your relational databases like MySQL, a data lake storing data in S3, or soon data in many NoSQL and real-time data stores. The true power of Starburst Galaxy is that now your team will no longer need to dedicate a giant backlog of tickets aimed at scaling up and down, monitoring, and securing Trino. Rather you can return to focus on the business problems and the best model for the data in your domain.&#xA;&#xA;trino&#xA;&#xA;!--emailsub--&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<h2 id="learn-how-to-quickly-join-data-across-multiple-sources" id="learn-how-to-quickly-join-data-across-multiple-sources">Learn how to quickly join data across multiple sources</h2>

<p>If you haven’t heard of Trino before, it is a query engine that speaks the language of many genres of databases. As such, Trino is commonly used to provide fast ad-hoc queries across heterogeneous data sources. Trino’s initial use case was built around replacing the Hive runtime engine to allow for faster querying of Big Data warehouses and data lakes. This may be the first time you have heard of <a href="https://trino.io/">Trino</a>, but you’ve likely heard of the project from which it was <a href="https://venturebeat.com/2021/08/27/who-owns-open-source-projects-people-or-companies/">“forklifted”</a>, Presto. If you want to learn more about <a href="https://trino.io/blog/2020/12/27/announcing-trino.html">why the creators of Presto now work on Trino (formerly PrestoSQL)</a> you can read the renaming blog that they produced earlier this year. Before you commit too much to this blog, I’d like to let you know why you should even care about Trino.</p>

<p><a href="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91ba54f-4f2c-4516-99fc-59c5c7cd8fd0_512x241.png"><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe91ba54f-4f2c-4516-99fc-59c5c7cd8fd0_512x241.png" alt=""/></a></p>





<h3 id="so-what-is-trino-anyways" id="so-what-is-trino-anyways">So what is Trino anyways?</h3>

<p>The first thing I like to make sure people know about when discussing Trino is that it is a SQL query engine, but not a SQL database. What does that mean? Traditional databases typically consist of a query engine and a storage engine. Trino is just a query engine and does not store data. Instead, Trino interacts with various databases that store their own data in their own formats. Trino parses and analyzes the SQL query you pass in, creates and optimizes a query execution plan that includes the data sources, and then schedules worker nodes that are able to intelligently query the underlying databases they connect to.</p>

<p>I say intelligently, specifically talking about pushdown queries. That’s right, the most intelligent thing for Trino to do is to avoid making more work for itself, and try to offload that work to the underlying database. This makes sense as the underlying databases generally have special indexes and data that are stored in a specific format to optimize the read time. It would be silly of Trino to ignore all of that optimized reading capability and do a linear scan of all the data to run the query itself. The goal in most optimizations for Trino is to push down the query to the database and only get back the smallest amount of data needed to join with another dataset from another database, do some further Trino specific processing, or simply return as the correct result set for the query.</p>

<h4 id="query-all-the-things" id="query-all-the-things">Query all the things</h4>

<p>So I still have not really answered your question of why you should care about Trino. The short answer is, Trino acts as a single access point to query all the things. Yup. Oh, and it’s super fast at ad-hoc queries over various data sources including data lakes (e.g. Iceberg/Databricks) or data warehouses (e.g. Hive/Snowflake). It has a <a href="https://trino.io/docs/current/develop/connectors.html">connector architecture</a> that allows it to speak the language of <a href="https://trino.io/docs/current/connector.html">a whole bunch of databases</a>. If you have a special use case, you can write your own connector that abstracts any database or service away to just be another table in Trino’s domain. Pretty cool right? But that’s actually rarely needed because the most common databases already have a connector written for them. If not, <a href="https://github.com/trinodb/trino/issues/4500">more connectors are getting added by Trino’s open source community every few months</a>.</p>

<p>To make the benefits of running federated queries a bit more tangible, I will present an example. Trino brings users the ability to map <a href="https://trino.io/docs/current/language.html">standardized ANSI SQL</a> query to query databases that have a <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html">custom query DSL like Elasticsearch</a>. With Trino it’s incredibly simple to set up an Elasticsearch catalog and start running SQL queries on it. If that doesn’t blow your mind, let me explain why that’s so powerful.</p>

<p>Imagine you have five different data stores, each with its own independent query language. Your data science or analyst team just wants access to these data stores. It would take a ridiculous amount of time for them to have to go to each data system individually, look up the different commands to pull data out of each one, and dump the data into one location and clean it up so that they can actually run meaningful queries. With Trino all they need to use is SQL to access them through Trino. Also, it doesn’t just stop at accessing the data, your data science team is also able to join data across tables of different databases like a search engine like Elasticsearch with an operational database like MySQL. Further, using Trino even enables joining data sources with themselves where joins are not supported, like in Elasticsearch and MongoDB. Did it happen yet? Is your mind blown?</p>

<h3 id="getting-started-with-trino" id="getting-started-with-trino">Getting Started with Trino</h3>

<p>So what is required to give Trino a test drive? Relative to many open-source database projects, Trino is one of the more simple projects to install, but this still doesn’t mean it is easy. An important element to a successful project is how it adapts to newer users and expands capability for growth and adoption. This really pushes the importance of making sure that there are multiple avenues of entry into using a product all of which have varying levels of difficulty, cost, customizability, interoperability, and scalability. As you increase in the level of customizability, interoperability, and scalability, you will generally see an increase in difficulty or cost and vice versa. Luckily, when you are starting out, you just really need to play with Trino.</p>

<p><a href="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9876726b-409f-4e0d-a768-967bba0abe9e_600x390.png"><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9876726b-409f-4e0d-a768-967bba0abe9e_600x390.png" alt=""/></a></p>

<p>Image added by Author</p>

<p>The low-cost and low difficulty way to try out Trino is to use <a href="https://www.docker.com/">Docker containers</a>. The nice thing about these containers is that you don’t have to really know anything about the installation process of Trino to play around with Trino. While many enjoy poking around documentation and working with Trino to get it set up, it may not be for all. I certainly have my days where I prefer a nice chill CLI sesh and other days where I just need to opt-out. If you want to skip to the Easy Button way to deploy Trino (hint, it’s the SaaS deployment) then skip the next few sections.</p>



<h4 id="using-trino-with-docker" id="using-trino-with-docker">Using Trino With Docker</h4>

<p>Trino ships with <a href="https://hub.docker.com/r/trinodb/trino">a Docker image</a> that does a lot of the setup necessary for Trino to run. Outside of simply running a docker container, there are a few things that need to happen for setup. First, in order to use a database like MySQL, we actually need to run a MySQL container as well using the official mysql image. There is <a href="https://github.com/bitsondatadev/trino-getting-started">a trino-getting-started repository</a> that contains a lot of the setup needed for using Trino on your own computer or setting it up on a test server as a proof of concept. Clone this repository and follow the instructions in the README to install Docker if it is not already.</p>

<p>You can actually run a query before learning the specifics of how this compose file works. Before you run the query, you will need to run the mysql and trino-coordinator instances. To do this, navigate to the mysql/trino-mysql/ directory that contains the docker-compose.yml and run:</p>

<pre><code>docker-compose up -d
</code></pre>

<h4 id="running-your-first-query" id="running-your-first-query">Running your first query!</h4>

<p>Now that you have Trino running in Docker, you need to open a session to access it. The easiest way to do this is via a console. Run the following Docker command to connect to a terminal on the coordinator:</p>

<pre><code>docker container exec -it trino-mysql_trino-coordinator_1 trino
</code></pre>

<p>This will bring you to the Trino terminal.</p>

<pre><code>trino&gt;
</code></pre>

<p>Your first query will actually be to generate data from the tpch catalog and then query the data that was loaded into mysql catalog. In the terminal, run the following two queries:</p>

<pre><code>CREATE TABLE mysql.tiny.customer
AS SELECT * FROM tpch.tiny.customer;
</code></pre>

<pre><code>SELECT custkey, name, nationkey, phone 
FROM mysql.tiny.customer LIMIT 5;
</code></pre>

<p>The output should look like this.</p>

<pre><code>|custkey|name              |nationkey|phone          |
|-------|------------------|---------|---------------|
|751    |Customer#000000751|0        |10-658-550-2257|
|752    |Customer#000000752|8        |18-924-993-6038|
|753    |Customer#000000753|17       |27-817-126-3646|
|754    |Customer#000000754|0        |10-646-595-5871|
|755    |Customer#000000755|16       |26-395-247-2207|
</code></pre>

<p>Congrats! You just ran your first query on Trino. Did you feel the rush!? Okay well, technically we just copied data from a data generation connector and moved it into a MySQL database and queried that back out. It’s fine if this simple exercise didn’t send goosebumps flying down your spine but hopefully, you can extrapolate the possibilities when connecting to other datasets.</p>

<p>A good initial exercise to study the compose file and directories before jumping into the Trino installation documentation. Let’s see how this was possible by breaking down the docker-compose file that you just ran.</p>

<pre><code>version: &#39;3.7&#39;
services:
  trino-coordinator:
    image: &#39;trinodb/trino:latest&#39;
    hostname: trino-coordinator
    ports:
      - &#39;8080:8080&#39;
    volumes:
      - ./etc:/etc/trino
    networks:
      - trino-network

  mysql:
    image: mysql:latest
    hostname: mysql
    environment:
      MYSQL_ROOT_PASSWORD: admin
      MYSQL_USER: admin
      MYSQL_PASSWORD: admin
      MYSQL_DATABASE: tiny
    ports:
      - &#39;3306:3306&#39;
    networks:
      - trino-network
networks:
  trino-network:
    driver: bridge
</code></pre>

<p>Notice that the hostname of mysql matches the instance name, and the mysql instance is on the trino-network that the trino-coordinator instance will also join. Also notice that the mysql image exposes port 3306 on the network.</p>

<p>Finally, we will use the trinodb/trino image for the trino-coordinator instance, and use the volumes option to map our local custom configurations for Trino to the /etc/trino directory we discuss further down in the <em>Trino Configuration</em> section. Trino should also be added to the trino-network and expose ports 8080 which is how external clients can access Trino. Below is an example of the docker-compose.yml file. The full configurations can be found in this <a href="https://github.com/bitsondatadev/trino-getting-started/tree/main/mysql/trino-mysql">getting started with Trino repository</a>.</p>

<p>These instructions are a basic overview of <a href="https://trino.io/docs/current/installation/deployment.html">the more complete installation instructions</a> if you’re really going for it! If you’re not that interested in the installation, feel free to skip ahead to the Deploying Trino at Scale with Kubernetes section. If you’d rather not deal with Kubernetes I offer you another pass to the easy button section of this blog.</p>

<h4 id="trino-requirements" id="trino-requirements">Trino requirements</h4>

<p>The first requirement is that Trino must be run on a POSIX-compliant system such as Linux or Unix. There are some folks in the community that have gotten Trino to run on Windows for testing using runtime environments like cygwin but this is not supported officially. However, in our world of containerization, this is less of an issue and you will be able to at least test this on <a href="https://www.docker.com/">Docker</a> no matter which operating system you use.</p>

<p>Trino is written in Java and so it requires the Java Runtime Environment (JRE). Trino requires a 64-bit version of Java 11, with a minimum required version of 11.0.7. Newer patch versions such as 11.0.8 or 11.0.9 are recommended. The launch scripts for Trino bin/launcher, also require python version 2.6.x, 2.7.x, or 3.x.</p>

<h4 id="trino-configuration" id="trino-configuration">Trino Configuration</h4>

<p>To configure Trino, you need to first know the Trino configuration directory. If you were installing Trino by hand, the default would be in a etc/ directory relative to the installation directory. For our example, I’m going to use the default installation directory of the <a href="https://hub.docker.com/r/trinodb/trino">Trino Docker image</a>, which is <a href="https://github.com/trinodb/trino/blob/356/core/docker/bin/run-trino#L15">set in the run-trino script</a> as /etc/trino. We need to create four files underneath this base directory. I will describe what these files do and you can see an example in the docker image I have created below.</p>
<ol><li><p>config.properties — This is the primary configuration for each node in the trino cluster. There are plenty of options that can be set here, but you’ll typically want to use the default settings when testing. The required configurations include indicating if the node is the coordinator, setting the http port that Trino communicates on, and the discovery node url so that Trino servers can find each other.</p></li>

<li><p>jvm.config — This configuration contains the command line arguments you will pass down to the java process that runs Trino.</p></li>

<li><p>log.properties — This configuration is helpful to indicate the log levels of various java classes in Trino. It can be left empty to use the default log level for all classes.</p></li>

<li><p>node.properties — This configuration is used to uniquely identify nodes in the cluster and specify locations of directories in the node.</p></li></ol>

<p>The next directory you need to know about is the catalog/ directory, located in the root configuration directory. In the docker container, it will be in /etc/trino/catalog. This is the directory that will contain the catalog configurations that Trino will use to connect to the different data sources. For our example, we’ll configure two catalogs, the mysql catalog, and the tpch catalog. The tpch catalog is a simple data generation catalog that simply needs the conector.name property to be configured and is located in /etc/trino/catalog/tpch.properties.</p>

<p>tpch.properties</p>

<pre><code>connector.name=tpch
</code></pre>

<p>The mysql catalog just needs the connector.name to specify which connector plugin to use, the connection-url property to point to the mysql instance, and the connection-user and connection-password properties for the mysql user.</p>

<p>mysql.properties</p>

<pre><code>connector.name=mysql
connection-url=jdbc:mysql://mysql:3306
connection-user=root
connection-password=admin
</code></pre>

<p>Note: the name of the configuration file becomes the name of the catalog in Trino. If you are familiar with MySQL, you are likely to know that MySQL supports a two-tiered containment hierarchy, though you may have never known it was called that. This containment hierarchy refers to databases and tables. The first tier of the hierarchy is the <em>tables</em>, while the second tier consists of <em>databases</em>. A database contains multiple tables and therefore two tables can have the same name provided they live under a different database.</p>

<p><a href="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5f2b877-50b9-4d11-b064-f5ae0b8323db_800x450.png"><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa5f2b877-50b9-4d11-b064-f5ae0b8323db_800x450.png" alt=""/></a></p>

<p>Image by Author</p>

<p>Since Trino has to connect to multiple databases, it supports a three-tiered containment hierarchy. Rather than call the second tier, databases, Trino refers to this tier as <em>schemas</em>. So a database in MySQL is equivalent to a schema in Trino. The third tier allows Trino to distinguish between multiple underlying data sources which are made of <em>catalogs</em>. Since the file provided to Trino is called mysql.properties it automatically names the catalog mysql without the .properties file type. To query the customer table in MySQL under the tiny you specify the following table name mysql.tiny.customer.</p>

<p>If you’ve reached this far, congratulations, you now know how to set up catalogs and query them through Trino! The benefits at this point should be clear, and making a proof of concept is easy to do this way. It’s time to put together that proof of concept for your team and your boss! What next though? How do you actually get this deployed in a reproducible and scalable manner? The next section covers a brief overview of faster ways to get Trino deployed at scale.</p>



<h3 id="deploying-trino-at-scale-with-kubernetes" id="deploying-trino-at-scale-with-kubernetes">Deploying Trino at Scale with Kubernetes</h3>

<p>Up to this point, this post only describes the deployment process. What about after that once you’ve deployed Trino to production and you slowly onboard engineering, BI/Analytics, and your data science teams. As many Trino users have experienced, the demand on your Trino cluster grows quickly as it becomes the single point of access to all of your data. This is where these small proof-of-concept size installations start to fall apart and you will need something more pliable to scale as your system starts to take on heavier workloads.</p>

<p>You will need to monitor your cluster and will likely need to stand up other services that run these monitoring tasks. This also applies to running other systems for security and authentication management. This list of complexity grows as you consider all of these systems need to scale and adapt around the growing Trino clusters. You may, for instance, consider deploying <a href="https://shopify.engineering/faster-trino-query-execution-infrastructure">multiple clusters to handle different workloads</a>, or possibly running tens or hundreds of Trino clusters to provide a self-service platform to provide isolated tenancy in your platform.</p>

<p>The solution to express all of these complex scenarios as the configuration is already solved by using an orchestration platform like Kubernetes, and its package manager project, Helm. Kubernetes offers a powerful way to express all the complex adaptable infrastructures based on your use cases.</p>

<p>In the interest of brevity, I will not include the full set of instructions on how to run a helm chart or cover the basics of running Trino on Kubernetes. Rather, I will refer you to <a href="https://trino.io/episodes/24.html">an episode of Trino Community Broadcast</a> that discusses Kubernetes, the community helm chart, and the basics of running Trino on Kubernetes. In the interest of transparency, <a href="https://github.com/trinodb/charts">the official Trino helm charts</a> are still in an early phase of development. There is a very popular <a href="https://github.com/valeriano-manassero/helm-charts/tree/main/valeriano-manassero/trino">community-contributed helm chart</a> that is adapted by many users to suit their needs and it is currently the best open source option for self-managed deployments of Trino. If you decide to take this route, proceed with caution and know that there is <a href="https://github.com/trinodb/charts/pull/11">development to support the helm deployments</a> moving forward.</p>

<p>While this will provide all the tools to enable a well-suited engineering department to run and maintain their own Trino cluster, this begs the question, based on your engineering team size, should you and your company be investing costly data engineer hours into maintaining, scaling, and hacking required to keep a full-size production infrastructure afloat?</p>

<h3 id="starburst-galaxy-the-easy-button-method-of-deploying-and-maintaining-trino" id="starburst-galaxy-the-easy-button-method-of-deploying-and-maintaining-trino">Starburst Galaxy: The Easy Button method of deploying and maintaining Trino</h3>

<p><em>Full Disclosure:</em> This blog post was originally written while I was working at Starburst. I still stand by Starburst Galaxy as one of the better options but I will add the caveat that it depends on your use case and things change so reach out if you need my latest thoughts on the matter. That said, Galaxy is the general purpose version of Trino the creators never got to build at Facebook. If you have custom features you need that you&#39;d like to contribute, a lot of folks run an open source cluster in testing and production is run by Starburst. You can then test and develop features to contribute to open source that will eventually upstream to Galaxy, Athena, or any other Trino variant.</p>

<p><a href="https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf49db7c-b00d-4291-b010-3835451379d6_800x572.jpeg"><img src="https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf49db7c-b00d-4291-b010-3835451379d6_800x572.jpeg" alt=""/></a></p>

<p>Image By: lostvegas, License: CC BY-NC-ND 2.0</p>

<p>As mentioned, Trino has a <em>relatively</em> simple deployment setup, with an emphasis on relatively. This blog really only hits the tip of the iceberg when it comes to the complexity involved in managing and scaling Trino. While it is certainly possible to manage running Trino and even do so at scale with helm charts in Kubernetes, it is still a difficult setup for Trinewbies and difficult to maintain and scale for those who already have experience maintaining Trino. I experienced firsthand many of these difficulties myself when I began my Trino journey years ago and started on my own quest to help others overcome some of these challenges. This is what led me to cross paths with Starburst, the company behind the SaaS Trino platform Galaxy.</p>

<p>Galaxy makes Trino accessible to companies having difficulties scaling and customizing Trino to their needs. Unless you are in a company that houses a massive data platform and you have dedicated data and DevOps engineers to each system in your platform, many of these options won’t be feasible for you in the long run.</p>

<p>One thing to make clear is that a Galaxy cluster is really just a Trino cluster on demand. Outside of managing the scaling policies, to avoid any surprises on your cloud bill, you really don’t have to think about scaling Trino up or down, or suspending it when it is not in use. The beautiful thing about Trino and therefore Galaxy is that it is an ephemeral compute engine much like AWS Lambda that you can quickly spin up or down. Not only are you able to run ad-hoc and federated queries over disparate data sources, but now you can also run the infrastructure for those queries on-demand with almost no cost to your engineering team’s time.</p>

<h4 id="getting-started-with-galaxy" id="getting-started-with-galaxy">Getting Started With Galaxy</h4>

<p>Here’s a quick getting started guide with the Starburst Galaxy that mirrors the setup we realized with the Docker example above with Trino and MySQL.</p>
<ul><li>Set up a trial of Galaxy by filling in your information at the bottom of the <a href="http://starburst.io/galaxy">Galaxy information page</a>.</li>
<li>Once you receive a link, you will see this sign-up screen. Fill out the email address, enter the pin sent to the email, and choose the domain for your cluster.</li>
<li>The rest of the tutorial is provided in the video below provides a basic demo of what you’ll need to do to get started.</li></ul>

<p>This introduction may feel a bit underwhelming but extrapolate being able to run federated queries across your relational databases like MySQL, a data lake storing data in S3, or soon data in many NoSQL and real-time data stores. The true power of Starburst Galaxy is that now your team will no longer need to dedicate a giant backlog of tickets aimed at scaling up and down, monitoring, and securing Trino. Rather you can return to focus on the business problems and the best model for the data in your domain.</p>

<p><a href="https://bitsondata.dev/tag:trino" class="hashtag"><span>#</span><span class="p-category">trino</span></a></p>


]]></content:encoded>
      <guid>https://bitsondata.dev/intro-to-trino-for-the-trinewbie</guid>
      <pubDate>Fri, 17 Dec 2021 18:00:00 +0000</pubDate>
    </item>
    <item>
      <title>Trino on ice IV: Deep dive into Iceberg internals</title>
      <link>https://bitsondata.dev/trino-iceberg-iv-deep-dive?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[&#xA;&#xA;So far, this series has covered some very interesting user level concepts of the Iceberg model, and how you can take advantage of them using the Trino query engine. This blog post dives into some implementation details of Iceberg by dissecting some files that result from various operations carried out using Trino. To dissect you must use some surgical instrumentation, namely Trino, Avro tools, the MinIO client tool and Iceberg’s core library. It’s useful to dissect how these files work, not only to help understand how Iceberg works, but also to aid in troubleshooting issues, should you have any issues during ingestion or querying of your Iceberg table. I like to think of this type of debugging much like a fun game of operation, and you’re looking to see what causes the red errors to fly by on your screen.&#xA;&#xA;!--more--&#xA;&#xA;---&#xA;&#xA;Trino on ice is a series, covering the details around how the Iceberg table format works with the Trino query engine. It’s recommended to read the posts sequentially as the examples build on previous posts in this series:&#xA;&#xA;Trino on ice I: A gentle introduction to Iceberg&#xA;Trino on ice II: In-place table evolution and cloud compatibility with Iceberg&#xA;Trino on ice III: Iceberg concurrency model, snapshots, and the Iceberg spec&#xA;Trino on ice IV: Deep dive into Iceberg internals&#xA;&#xA;---&#xA;&#xA;Understanding Iceberg metadata&#xA;&#xA;Iceberg can use any compatible metastore, but for Trino, it only supports the  Hive metastore and AWS Glue similar to the Hive connector. This is because there is already a vast amount of testing and support for using the Hive metastore in Trino. Likewise, many Trino use cases that currently use data lakes already use the Hive connector and therefore the Hive metastore. This makes it convenient to have as the leading supported use case as existing users can easily migrate between Hive to Iceberg tables. Since there is no indication of which connector is actually executed in the diagram of the Hive connector architecture, it serves as a diagram that can be used for both Hive and Iceberg. The only difference is the connector used, but if you create a table in Hive, you can  view the same table in Iceberg.&#xA;&#xA;To recap the steps taken from the first three blogs; the first blog created an events table, while the first two blogs ran two insert statements. The first insert contained three records, while the second insert contained a single record.&#xA;&#xA;Up until this point, the state of the files in MinIO haven’t really been shown except some of the manifest list pointers from the snapshot in the third blog post. Using the MinIO client tool, you can list files that Iceberg generated through all these operations and then try to understand what purpose they are serving.&#xA;&#xA;% mc tree -f local/&#xA;local/&#xA;└─ iceberg&#xA;   └─ logging.db&#xA;      └─ events&#xA;         ├─ data&#xA;         │  ├─ eventtimeday=2021-04-01&#xA;         │  │  ├─ 51eb1ea6-266b-490f-8bca-c63391f02d10.orc&#xA;         │  │  └─ cbcf052d-240d-4881-8a68-2bbc0f7e5233.orc&#xA;         │  └─ eventtimeday=2021-04-02&#xA;         │     └─ b012ec20-bbdd-47f5-89d3-57b9e32ea9eb.orc&#xA;         └─ metadata&#xA;            ├─ 00000-c5cfaab4-f82f-4351-b2a5-bd0e241f84bc.metadata.json&#xA;            ├─ 00001-27c8c2d1-fdbb-429d-9263-3654d818250e.metadata.json&#xA;            ├─ 00002-33d69acc-94cb-44bc-b2a1-71120e749d9a.metadata.json&#xA;            ├─ 23cc980c-9570-42ed-85cf-8658fda2727d-m0.avro&#xA;            ├─ 92382234-a4a6-4a1b-bc9b-24839472c2f6-m0.avro&#xA;            ├─ snap-2720489016575682283-1-92382234-a4a6-4a1b-bc9b-24839472c2f6.avro&#xA;            ├─ snap-4564366177504223943-1-23cc980c-9570-42ed-85cf-8658fda2727d.avro&#xA;            └─ snap-6967685587675910019-1-bcbe9133-c51c-42a9-9c73-f5b745702cb0.avro&#xA;&#xA;There are a lot of files here, but here are a couple of patterns that you can observe with these files.&#xA;&#xA;First, the top two directories are named data and metadata.&#xA;&#xA;/bucket/database/table/data//bucket/database/table/metadata/&#xA;&#xA;As you might expect, data contains the actual ORC files split by partition. This is akin to what you would see in a Hive table data directory. What is really of interest here is the metadata directory. There are specifically three patterns of files you’ll find here.&#xA;&#xA;/bucket/database/table/metadata/file-id.avro&#xA;&#xA;/bucket/database/table/metadata/snap-snapshot-id-version-file-id.avro&#xA;&#xA;/bucket/database/table/metadata/version-commit-UUID.metadata.json&#xA;&#xA;Iceberg has a persistent tree structure that manages various snapshots of the data that are created for every mutation of the data. This enables not only a concurrency model that supports serializable isolation, but also cool features like time travel across a linear progression of snapshots.&#xA;&#xA;This tree structure contains two types of Avro files, manifest lists and manifest files. Manifest list files contain pointers to various manifest files and the manifest files themselves point to various data files. This post starts out by covering these manifest files, and later covers the table metadata files that are suffixed by .metadata.json.&#xA;&#xA;The last blog covered the command in Trino that shows the snapshot information that is stored in the metastore. Here is that command and its output again for your review.&#xA;&#xA;SELECT manifestlist &#xA;FROM iceberg.logging.&#34;events$snapshots&#34;;&#xA;&#xA;Result:&#xA;&#xA;table&#xA;trthsnapshots/th/tr&#xA;trtds3a://iceberg/logging.db/events/metadata/snap-6967685587675910019-1-bcbe9133-c51c-42a9-9c73-f5b745702cb0.avro/td/tr&#xA;trtds3a://iceberg/logging.db/events/metadata/snap-2720489016575682283-1-92382234-a4a6-4a1b-bc9b-24839472c2f6.avro/td/tr&#xA;trtds3a://iceberg/logging.db/events/metadata/snap-4564366177504223943-1-23cc980c-9570-42ed-85cf-8658fda2727d.avro/td/tr&#xA;/table&#xA;&#xA;You’ll notice that the manifest list returns the Avro files prefixed with&#xA;snap- are returned. These files are directly correlated with the snapshot record stored in the metastore. According to the diagram above, snapshots are records in the metastore that contain the url of the manifest list in the Avro file. Avro files are binary files and not something you can just open up in a text editor to read. Using the avro-tools.jar tool distributed by the Apache Avro project, you can actually inspect the contents of this file to get a better understanding of how it is used by Iceberg.&#xA;&#xA;The first snapshot is generated on the creation of the events table. Upon inspecting this file, you notice that the file is empty. The output is an empty line that the jq JSON command line utility removes on pretty printing the JSON that is returned, which is just a newline. This snapshot represents an empty state of the table upon creation. To investigate the snapshots you need to download the files to your local filesystem. Let&#39;s move them to the home  directory:&#xA;&#xA;% java -jar  ~/Desktop/avrofiles/avro-tools-1.10.0.jar tojson ~/snap-6967685587675910019-1-bcbe9133-c51c-42a9-9c73-f5b745702cb0.avro | jq .&#xA;&#xA;Result: (is empty)&#xA;&#xA;The second snapshot is a little more interesting and actually shows us the contents of a manifest list.&#xA;&#xA;% java -jar  ~/Desktop/avrofiles/avro-tools-1.10.0.jar tojson ~/snap-2720489016575682283-1-92382234-a4a6-4a1b-bc9b-24839472c2f6.avro | jq .&#xA;&#xA;Result:&#xA;&#xA;{&#xA;   &#34;manifestpath&#34;:&#34;s3a://iceberg/logging.db/events/metadata/92382234-a4a6-4a1b-bc9b-24839472c2f6-m0.avro&#34;,&#xA;   &#34;manifestlength&#34;:6114,&#xA;   &#34;partitionspecid&#34;:0,&#xA;   &#34;addedsnapshotid&#34;:{&#xA;      &#34;long&#34;:2720489016575682000&#xA;   },&#xA;   &#34;addeddatafilescount&#34;:{&#xA;      &#34;int&#34;:2&#xA;   },&#xA;   &#34;existingdatafilescount&#34;:{&#xA;      &#34;int&#34;:0&#xA;   },&#xA;   &#34;deleteddatafilescount&#34;:{&#xA;      &#34;int&#34;:0&#xA;   },&#xA;   &#34;partitions&#34;:{&#xA;      &#34;array&#34;:[&#xA;         {&#xA;            &#34;containsnull&#34;:false,&#xA;            &#34;lowerbound&#34;:{&#xA;               &#34;bytes&#34;:&#34;\u001eI\u0000\u0000&#34;&#xA;            },&#xA;            &#34;upperbound&#34;:{&#xA;               &#34;bytes&#34;:&#34;\u001fI\u0000\u0000&#34;&#xA;            }&#xA;         }&#xA;      ]&#xA;   },&#xA;   &#34;addedrowscount&#34;:{&#xA;      &#34;long&#34;:3&#xA;   },&#xA;   &#34;existingrowscount&#34;:{&#xA;      &#34;long&#34;:0&#xA;   },&#xA;   &#34;deletedrowscount&#34;:{&#xA;      &#34;long&#34;:0&#xA;   }&#xA;}&#xA;&#xA;To understand each of the values in each of these rows, you can refer to the  Iceberg &#xA;specification in the manifest list file section. Instead of covering these exhaustively, let&#39;s focus on a few key fields. Below are the fields, and their definition according to the specification.&#xA;&#xA;manifestpath - Location of the manifest file.&#xA;partitionspecid - ID of a partition spec used to write the manifest; must be listed in table metadata partition-specs.&#xA;addedsnapshotid - ID of the snapshot where the manifest file was added.&#xA;partitions - A list of field summaries for each partition field in the spec. Each field in the list corresponds to a field in the manifest file’s partition spec.&#xA;addedrowscount - Number of rows in all files in the manifest that have status ADDED, when null this is assumed to be non-zero.&#xA;&#xA;As mentioned above, manifest lists hold references to various manifest files. These manifest paths are the pointers in the persistent tree that tells any client using Iceberg where to find all of the manifest files associated with a particular snapshot. To traverse this tree, you can look over the different manifest paths to find all the manifest files associated with the particular snapshot you want to traverse. Partition spec ids are helpful to know the current partition specification which are stored in the table metadata in the metastore. This references where to find the spec in the metastore. Added snapshot ids tells you which snapshot is associated with the manifest list. Partitions hold some high level partition bound information to make for faster querying. If a query is looking for a particular value, it only traverses the manifest files where the query values fall within the range of the file values. Finally, you get a few metrics like the number of changed rows and data files, one of which is the count of added rows. The first operation consisted of three rows inserts and the second operation was the insertion of one row. Using the row counts you can easily determine which manifest file belongs to which operation.&#xA;&#xA;The following command shows the final snapshot after both operations executed and filters out only the fields pointed out above.&#xA;&#xA;% java -jar  ~/Desktop/avrofiles/avro-tools-1.10.0.jar tojson ~/snap-4564366177504223943-1-23cc980c-9570-42ed-85cf-8658fda2727d.avro | jq &#39;. | {manifestpath: .manifestpath, partitionspecid: .partitionspecid, addedsnapshotid: .addedsnapshotid, partitions: .partitions, addedrowscount: .addedrowscount }&#39;&#xA;&#xA;Result: &#xA;&#xA;{&#xA;   &#34;manifestpath&#34;:&#34;s3a://iceberg/logging.db/events/metadata/23cc980c-9570-42ed-85cf-8658fda2727d-m0.avro&#34;,&#xA;   &#34;partitionspecid&#34;:0,&#xA;   &#34;addedsnapshotid&#34;:{&#xA;      &#34;long&#34;:4564366177504223700&#xA;   },&#xA;   &#34;partitions&#34;:{&#xA;      &#34;array&#34;:[&#xA;         {&#xA;            &#34;containsnull&#34;:false,&#xA;            &#34;lowerbound&#34;:{&#xA;               &#34;bytes&#34;:&#34;\u001eI\u0000\u0000&#34;&#xA;            },&#xA;            &#34;upperbound&#34;:{&#xA;               &#34;bytes&#34;:&#34;\u001eI\u0000\u0000&#34;&#xA;            }&#xA;         }&#xA;      ]&#xA;   },&#xA;   &#34;addedrowscount&#34;:{&#xA;      &#34;long&#34;:1&#xA;   }&#xA;}&#xA;{&#xA;   &#34;manifestpath&#34;:&#34;s3a://iceberg/logging.db/events/metadata/92382234-a4a6-4a1b-bc9b-24839472c2f6-m0.avro&#34;,&#xA;   &#34;partitionspecid&#34;:0,&#xA;   &#34;addedsnapshotid&#34;:{&#xA;      &#34;long&#34;:2720489016575682000&#xA;   },&#xA;   &#34;partitions&#34;:{&#xA;      &#34;array&#34;:[&#xA;         {&#xA;            &#34;containsnull&#34;:false,&#xA;            &#34;lowerbound&#34;:{&#xA;               &#34;bytes&#34;:&#34;\u001eI\u0000\u0000&#34;&#xA;            },&#xA;            &#34;upperbound&#34;:{&#xA;               &#34;bytes&#34;:&#34;\u001fI\u0000\u0000&#34;&#xA;            }&#xA;         }&#xA;      ]&#xA;   },&#xA;   &#34;addedrowscount&#34;:{&#xA;      &#34;long&#34;:3&#xA;   }&#xA;}&#xA;&#xA;In the listing of the manifest file related to the last snapshot, you notice the first operation where three rows were inserted is contained in the manifest file in the second JSON object. You can determine this from the snapshot id, as well as, the number of rows that were added in the operation. The first JSON object contains the last operation that inserted a single row. So the most recent operations are listed in reverse commit order.&#xA;&#xA;The next command does the same listing of the file that you ran with the manifest list, except you run this on the manifest files themselves to expose their contents and discuss them. To begin with, you run the command to show the contents of the manifest file associated with the insertion of three rows.&#xA;&#xA;% java -jar  ~/avro-tools-1.10.0.jar tojson ~/Desktop/avrofiles/92382234-a4a6-4a1b-bc9b-24839472c2f6-m0.avro | jq .&#xA;&#xA;Result: &#xA;&#xA;{&#xA;   &#34;status&#34;:1,&#xA;   &#34;snapshotid&#34;:{&#xA;      &#34;long&#34;:2720489016575682000&#xA;   },&#xA;   &#34;datafile&#34;:{&#xA;      &#34;filepath&#34;:&#34;s3a://iceberg/logging.db/events/data/eventtimeday=2021-04-01/51eb1ea6-266b-490f-8bca-c63391f02d10.orc&#34;,&#xA;      &#34;fileformat&#34;:&#34;ORC&#34;,&#xA;      &#34;partition&#34;:{&#xA;         &#34;eventtimeday&#34;:{&#xA;            &#34;int&#34;:18718&#xA;         }&#xA;      },&#xA;      &#34;recordcount&#34;:1,&#xA;      &#34;filesizeinbytes&#34;:870,&#xA;      &#34;blocksizeinbytes&#34;:67108864,&#xA;      &#34;columnsizes&#34;:null,&#xA;      &#34;valuecounts&#34;:{&#xA;         &#34;array&#34;:[&#xA;            {&#xA;               &#34;key&#34;:1,&#xA;               &#34;value&#34;:1&#xA;            },&#xA;            {&#xA;               &#34;key&#34;:2,&#xA;               &#34;value&#34;:1&#xA;            },&#xA;            {&#xA;               &#34;key&#34;:3,&#xA;               &#34;value&#34;:1&#xA;            },&#xA;            {&#xA;               &#34;key&#34;:4,&#xA;               &#34;value&#34;:1&#xA;            }&#xA;         ]&#xA;      },&#xA;      &#34;nullvaluecounts&#34;:{&#xA;         &#34;array&#34;:[&#xA;            {&#xA;               &#34;key&#34;:1,&#xA;               &#34;value&#34;:0&#xA;            },&#xA;            {&#xA;               &#34;key&#34;:2,&#xA;               &#34;value&#34;:0&#xA;            },&#xA;            {&#xA;               &#34;key&#34;:3,&#xA;               &#34;value&#34;:0&#xA;            },&#xA;            {&#xA;               &#34;key&#34;:4,&#xA;               &#34;value&#34;:0&#xA;            }&#xA;         ]&#xA;      },&#xA;      &#34;nanvaluecounts&#34;:null,&#xA;      &#34;lowerbounds&#34;:{&#xA;         &#34;array&#34;:[&#xA;            {&#xA;               &#34;key&#34;:1,&#xA;               &#34;value&#34;:&#34;ERROR&#34;&#xA;            },&#xA;            {&#xA;               &#34;key&#34;:3,&#xA;               &#34;value&#34;:&#34;Oh noes&#34;&#xA;            }&#xA;         ]&#xA;      },&#xA;      &#34;upperbounds&#34;:{&#xA;         &#34;array&#34;:[&#xA;            {&#xA;               &#34;key&#34;:1,&#xA;               &#34;value&#34;:&#34;ERROR&#34;&#xA;            },&#xA;            {&#xA;               &#34;key&#34;:3,&#xA;               &#34;value&#34;:&#34;Oh noes&#34;&#xA;            }&#xA;         ]&#xA;      },&#xA;      &#34;keymetadata&#34;:null,&#xA;      &#34;splitoffsets&#34;:null&#xA;   }&#xA;}&#xA;{&#xA;   &#34;status&#34;:1,&#xA;   &#34;snapshotid&#34;:{&#xA;      &#34;long&#34;:2720489016575682000&#xA;   },&#xA;   &#34;datafile&#34;:{&#xA;      &#34;filepath&#34;:&#34;s3a://iceberg/logging.db/events/data/eventtimeday=2021-04-02/b012ec20-bbdd-47f5-89d3-57b9e32ea9eb.orc&#34;,&#xA;      &#34;fileformat&#34;:&#34;ORC&#34;,&#xA;      &#34;partition&#34;:{&#xA;         &#34;eventtimeday&#34;:{&#xA;            &#34;int&#34;:18719&#xA;         }&#xA;      },&#xA;      &#34;recordcount&#34;:2,&#xA;      &#34;filesizeinbytes&#34;:1084,&#xA;      &#34;blocksizeinbytes&#34;:67108864,&#xA;      &#34;columnsizes&#34;:null,&#xA;      &#34;valuecounts&#34;:{&#xA;         &#34;array&#34;:[&#xA;            {&#xA;               &#34;key&#34;:1,&#xA;               &#34;value&#34;:2&#xA;            },&#xA;            {&#xA;               &#34;key&#34;:2,&#xA;               &#34;value&#34;:2&#xA;            },&#xA;            {&#xA;               &#34;key&#34;:3,&#xA;               &#34;value&#34;:2&#xA;            },&#xA;            {&#xA;               &#34;key&#34;:4,&#xA;               &#34;value&#34;:2&#xA;            }&#xA;         ]&#xA;      },&#xA;      &#34;nullvaluecounts&#34;:{&#xA;         &#34;array&#34;:[&#xA;            {&#xA;               &#34;key&#34;:1,&#xA;               &#34;value&#34;:0&#xA;            },&#xA;            {&#xA;               &#34;key&#34;:2,&#xA;               &#34;value&#34;:0&#xA;            },&#xA;            {&#xA;               &#34;key&#34;:3,&#xA;               &#34;value&#34;:0&#xA;            },&#xA;            {&#xA;               &#34;key&#34;:4,&#xA;               &#34;value&#34;:0&#xA;            }&#xA;         ]&#xA;      },&#xA;      &#34;nanvaluecounts&#34;:null,&#xA;      &#34;lowerbounds&#34;:{&#xA;         &#34;array&#34;:[&#xA;            {&#xA;               &#34;key&#34;:1,&#xA;               &#34;value&#34;:&#34;ERROR&#34;&#xA;            },&#xA;            {&#xA;               &#34;key&#34;:3,&#xA;               &#34;value&#34;:&#34;Double oh noes&#34;&#xA;            }&#xA;         ]&#xA;      },&#xA;      &#34;upperbounds&#34;:{&#xA;         &#34;array&#34;:[&#xA;            {&#xA;               &#34;key&#34;:1,&#xA;               &#34;value&#34;:&#34;WARN&#34;&#xA;            },&#xA;            {&#xA;               &#34;key&#34;:3,&#xA;               &#34;value&#34;:&#34;Maybeh oh noes?&#34;&#xA;            }&#xA;         ]&#xA;      },&#xA;      &#34;keymetadata&#34;:null,&#xA;      &#34;splitoffsets&#34;:null&#xA;   }&#xA;}&#xA;&#xA;Now this is a very big output, but in summary, there’s really not too much to these files. As before, there is a Manifest section in the Iceberg spec that details what each of these fields means. Here are the important fields:&#xA;&#xA;snapshotid - Snapshot id where the file was added, or deleted if status is two. Inherited when null.&#xA;datafile - Field containing metadata about the data files pertaining to the manifest file, such as file path, partition tuple, metrics, etc…&#xA;datafile.filepath - Full URI for the file with FS scheme.&#xA;datafile.partition - Partition data tuple, schema based on the partition spec.&#xA;datafile.recordcount - Number of records in the data file.&#xA;datafile.count - Multiple fields that contain a map from column id to  number of values, null, nan counts in the file. These can be used to quickly  filter out unnecessary get operations.&#xA;datafile.bounds - Multiple fields that contain a map from column id to lower or upper bound in the column serialized as binary. Each value must be less than or equal to all non-null, non-NaN values in the column for the file.&#xA;&#xA;Each data file struct contains a partition and data file that it maps to. These files only be scanned and returned if the criteria for the query is met when  checking all of the count, bounds, and other statistics that are recorded in the file. Ideally only files that contain data relevant to the query should be scanned at all. Having information like the record count may also help in the query planning process to determine splits and other information. This particular optimization hasn’t been completed yet as planning typically happens before traversal of the files. It is still in ongoing discussion and is discussed a bit by Iceberg creator Ryan Blue in a recent meetup. If this is something you are interested in, keep posted on the Slack channel and releases as the Trino Iceberg connector progresses in this area.&#xA;&#xA;As mentioned above, the last set of files that you find in the metadata directory which are suffixed with .metadata.json. These files at baseline are a bit strange as they aren’t stored in the Avro format, but instead the JSON format. This is because they are not part of the persistent tree structure. These files are essentially a copy of the table metadata that is stored in the metastore. You can find the fields for the table metadata listed in the Iceberg specification. These tables are typically stored persistently in a metasture much like the Hive metastore but could easily be replaced by any datastore that can support an atomic swap (check-and-put) operation required for Iceberg to support the optimistic concurrency operation.&#xA;&#xA;The naming of the table metadata includes a table version and UUID: &#xA;table-version-UUID.metadata.json. To commit a new metadata version, which just adds 1 to the current version number, the writer performs these steps:&#xA;&#xA;It creates a new table metadata file using the current metadata.&#xA;It writes the new table metadata to a file following the naming with the next version number.&#xA;It requests the metastore swap the table’s metadata pointer from the old location to the new location.&#xA;&#xA;    If the swap succeeds, the commit succeeded. The new file is now the &#xA;    current metadata.&#xA;    If the swap fails, another writer has already created their own. The&#xA;    current writer goes back to step 1.&#xA;&#xA;If you want to see where this is stored in the Hive metastore, you can reference the TABLEPARAMS table. At the time of writing, this is the only method of using the metastore that is supported by the Trino Iceberg connector.&#xA;&#xA;SELECT PARAMKEY, PARAMVALUE FROM metastore.TABLEPARAMS;&#xA;&#xA;Result:&#xA;&#xA;table&#xA;trthPARAMKEY/ththPARAMVALUE/th/tr&#xA;trtdEXTERNAL/tdtdTRUE/td/tr&#xA;trtdmetadatalocation/tdtds3a://iceberg/logging.db/events/metadata/00002-33d69acc-94cb-44bc-b2a1-71120e749d9a.metadata.json/td/tr&#xA;trtdnumFiles/tdtd2/td/tr&#xA;trtdpreviousmetadatalocation/tdtds3a://iceberg/logging.db/events/metadata/00001-27c8c2d1-fdbb-429d-9263-3654d818250e.metadata.json/td/tr&#xA;trtdtabletype/tdtdiceberg/td/tr&#xA;trtdtotalSize/tdtd5323/td/tr&#xA;trtdtransientlastDdlTime/tdtd1622865672/td/tr&#xA;/table&#xA;&#xA;So as you can see, the metastore is saying the current metadata location is the&#xA;00002-33d69acc-94cb-44bc-b2a1-71120e749d9a.metadata.json file. Now you can dive in to see the table metadata that is being used by the Iceberg connector.&#xA;&#xA;% cat ~/Desktop/avrofiles/00002-33d69acc-94cb-44bc-b2a1-71120e749d9a.metadata.json&#xA;&#xA;Result: &#xA;&#xA;{&#xA;   &#34;format-version&#34;:1,&#xA;   &#34;table-uuid&#34;:&#34;32e3c271-84a9-4be5-9342-2148c878227a&#34;,&#xA;   &#34;location&#34;:&#34;s3a://iceberg/logging.db/events&#34;,&#xA;   &#34;last-updated-ms&#34;:1622865686323,&#xA;   &#34;last-column-id&#34;:5,&#xA;   &#34;schema&#34;:{&#xA;      &#34;type&#34;:&#34;struct&#34;,&#xA;      &#34;fields&#34;:[&#xA;         {&#xA;            &#34;id&#34;:1,&#xA;            &#34;name&#34;:&#34;level&#34;,&#xA;            &#34;required&#34;:false,&#xA;            &#34;type&#34;:&#34;string&#34;&#xA;         },&#xA;         {&#xA;            &#34;id&#34;:2,&#xA;            &#34;name&#34;:&#34;eventtime&#34;,&#xA;            &#34;required&#34;:false,&#xA;            &#34;type&#34;:&#34;timestamp&#34;&#xA;         },&#xA;         {&#xA;            &#34;id&#34;:3,&#xA;            &#34;name&#34;:&#34;message&#34;,&#xA;            &#34;required&#34;:false,&#xA;            &#34;type&#34;:&#34;string&#34;&#xA;         },&#xA;         {&#xA;            &#34;id&#34;:4,&#xA;            &#34;name&#34;:&#34;callstack&#34;,&#xA;            &#34;required&#34;:false,&#xA;            &#34;type&#34;:{&#xA;               &#34;type&#34;:&#34;list&#34;,&#xA;               &#34;element-id&#34;:5,&#xA;               &#34;element&#34;:&#34;string&#34;,&#xA;               &#34;element-required&#34;:false&#xA;            }&#xA;         }&#xA;      ]&#xA;   },&#xA;   &#34;partition-spec&#34;:[&#xA;      {&#xA;         &#34;name&#34;:&#34;eventtimeday&#34;,&#xA;         &#34;transform&#34;:&#34;day&#34;,&#xA;         &#34;source-id&#34;:2,&#xA;         &#34;field-id&#34;:1000&#xA;      }&#xA;   ],&#xA;   &#34;default-spec-id&#34;:0,&#xA;   &#34;partition-specs&#34;:[&#xA;      {&#xA;         &#34;spec-id&#34;:0,&#xA;         &#34;fields&#34;:[&#xA;            {&#xA;               &#34;name&#34;:&#34;eventtime_day&#34;,&#xA;               &#34;transform&#34;:&#34;day&#34;,&#xA;               &#34;source-id&#34;:2,&#xA;               &#34;field-id&#34;:1000&#xA;            }&#xA;         ]&#xA;      }&#xA;   ],&#xA;   &#34;default-sort-order-id&#34;:0,&#xA;   &#34;sort-orders&#34;:[&#xA;      {&#xA;         &#34;order-id&#34;:0,&#xA;         &#34;fields&#34;:[&#xA;            &#xA;         ]&#xA;      }&#xA;   ],&#xA;   &#34;properties&#34;:{&#xA;      &#34;write.format.default&#34;:&#34;ORC&#34;&#xA;   },&#xA;   &#34;current-snapshot-id&#34;:4564366177504223943,&#xA;   &#34;snapshots&#34;:[&#xA;      {&#xA;         &#34;snapshot-id&#34;:6967685587675910019,&#xA;         &#34;timestamp-ms&#34;:1622865672882,&#xA;         &#34;summary&#34;:{&#xA;            &#34;operation&#34;:&#34;append&#34;,&#xA;            &#34;changed-partition-count&#34;:&#34;0&#34;,&#xA;            &#34;total-records&#34;:&#34;0&#34;,&#xA;            &#34;total-data-files&#34;:&#34;0&#34;,&#xA;            &#34;total-delete-files&#34;:&#34;0&#34;,&#xA;            &#34;total-position-deletes&#34;:&#34;0&#34;,&#xA;            &#34;total-equality-deletes&#34;:&#34;0&#34;&#xA;         },&#xA;         &#34;manifest-list&#34;:&#34;s3a://iceberg/logging.db/events/metadata/snap-6967685587675910019-1-bcbe9133-c51c-42a9-9c73-f5b745702cb0.avro&#34;&#xA;      },&#xA;      {&#xA;         &#34;snapshot-id&#34;:2720489016575682283,&#xA;         &#34;parent-snapshot-id&#34;:6967685587675910019,&#xA;         &#34;timestamp-ms&#34;:1622865680419,&#xA;         &#34;summary&#34;:{&#xA;            &#34;operation&#34;:&#34;append&#34;,&#xA;            &#34;added-data-files&#34;:&#34;2&#34;,&#xA;            &#34;added-records&#34;:&#34;3&#34;,&#xA;            &#34;added-files-size&#34;:&#34;1954&#34;,&#xA;            &#34;changed-partition-count&#34;:&#34;2&#34;,&#xA;            &#34;total-records&#34;:&#34;3&#34;,&#xA;            &#34;total-data-files&#34;:&#34;2&#34;,&#xA;            &#34;total-delete-files&#34;:&#34;0&#34;,&#xA;            &#34;total-position-deletes&#34;:&#34;0&#34;,&#xA;            &#34;total-equality-deletes&#34;:&#34;0&#34;&#xA;         },&#xA;         &#34;manifest-list&#34;:&#34;s3a://iceberg/logging.db/events/metadata/snap-2720489016575682283-1-92382234-a4a6-4a1b-bc9b-24839472c2f6.avro&#34;&#xA;      },&#xA;      {&#xA;         &#34;snapshot-id&#34;:4564366177504223943,&#xA;         &#34;parent-snapshot-id&#34;:2720489016575682283,&#xA;         &#34;timestamp-ms&#34;:1622865686278,&#xA;         &#34;summary&#34;:{&#xA;            &#34;operation&#34;:&#34;append&#34;,&#xA;            &#34;added-data-files&#34;:&#34;1&#34;,&#xA;            &#34;added-records&#34;:&#34;1&#34;,&#xA;            &#34;added-files-size&#34;:&#34;746&#34;,&#xA;            &#34;changed-partition-count&#34;:&#34;1&#34;,&#xA;            &#34;total-records&#34;:&#34;4&#34;,&#xA;            &#34;total-data-files&#34;:&#34;3&#34;,&#xA;            &#34;total-delete-files&#34;:&#34;0&#34;,&#xA;            &#34;total-position-deletes&#34;:&#34;0&#34;,&#xA;            &#34;total-equality-deletes&#34;:&#34;0&#34;&#xA;         },&#xA;         &#34;manifest-list&#34;:&#34;s3a://iceberg/logging.db/events/metadata/snap-4564366177504223943-1-23cc980c-9570-42ed-85cf-8658fda2727d.avro&#34;&#xA;      }&#xA;   ],&#xA;   &#34;snapshot-log&#34;:[&#xA;      {&#xA;         &#34;timestamp-ms&#34;:1622865672882,&#xA;         &#34;snapshot-id&#34;:6967685587675910019&#xA;      },&#xA;      {&#xA;         &#34;timestamp-ms&#34;:1622865680419,&#xA;         &#34;snapshot-id&#34;:2720489016575682283&#xA;      },&#xA;      {&#xA;         &#34;timestamp-ms&#34;:1622865686278,&#xA;         &#34;snapshot-id&#34;:4564366177504223943&#xA;      }&#xA;   ],&#xA;   &#34;metadata-log&#34;:[&#xA;      {&#xA;         &#34;timestamp-ms&#34;:1622865672894,&#xA;         &#34;metadata-file&#34;:&#34;s3a://iceberg/logging.db/events/metadata/00000-c5cfaab4-f82f-4351-b2a5-bd0e241f84bc.metadata.json&#34;&#xA;      },&#xA;      {&#xA;         &#34;timestamp-ms&#34;:1622865680524,&#xA;         &#34;metadata-file&#34;:&#34;s3a://iceberg/logging.db/events/metadata/00001-27c8c2d1-fdbb-429d-9263-3654d818250e.metadata.json&#34;&#xA;      }&#xA;   ]&#xA;}&#xA;&#xA;As you can see, these JSON files can quickly grow as you perform different updates on your table. This file contains a pointer to all of the snapshots and manifest list files, much like the output you found from looking at the snapshots in the table. A really important piece to note is the schema is stored here. This is what Trino uses for validation on inserts and reads. As you may expect, there is the root location of the table itself, as well as a unique table identifier. The final part I’d like to note about this file is the partition-spec and partition-specs fields. The partition-spec field holds the current partition spec, while the partition-specs is an array that can hold a list of all partition specs that have existed for this table. As pointed out earlier, you can have many different manifest files that use different partition specs. That wraps up all of the metadata file types you can expect to see in Iceberg!&#xA;&#xA;This post wraps up the Trino on ice series. Hopefully these blog posts serve as a helpful initial dialogue about what is expected to grow as a vital portion of an open data lakehouse stack. What are you waiting for? Come join the fun and help us implement some of the missing features or instead go ahead and try Trino on Ice(berg) yourself!&#xA;&#xA;#trino #iceberg]]&gt;</description>
      <content:encoded><![CDATA[<p><img src="https://trino.io/assets/blog/trino-on-ice/trino-iceberg.png" alt=""/></p>

<p>So far, this series has covered some very interesting user level concepts of the Iceberg model, and how you can take advantage of them using the Trino query engine. This blog post dives into some implementation details of Iceberg by dissecting some files that result from various operations carried out using Trino. To dissect you must use some surgical instrumentation, namely Trino, Avro tools, the MinIO client tool and Iceberg’s core library. It’s useful to dissect how these files work, not only to help understand how Iceberg works, but also to aid in troubleshooting issues, should you have any issues during ingestion or querying of your Iceberg table. I like to think of this type of debugging much like a fun game of operation, and you’re looking to see what causes the red errors to fly by on your screen.</p>



<hr/>

<p>Trino on ice is a series, covering the details around how the Iceberg table format works with the Trino query engine. It’s recommended to read the posts sequentially as the examples build on previous posts in this series:</p>
<ul><li><a href="https://bitsondata.dev/trino-iceberg-i-gentle-intro">Trino on ice I: A gentle introduction to Iceberg</a></li>
<li><a href="https://bitsondata.dev/trino-iceberg-ii-table-evolution-cloud">Trino on ice II: In-place table evolution and cloud compatibility with Iceberg</a></li>
<li><a href="https://write.as/bitsondatadev/trino-iceberg-iii-concurrency-snapshots-spec">Trino on ice III: Iceberg concurrency model, snapshots, and the Iceberg spec</a></li>
<li><a href="https://bitsondata.dev/trino-iceberg-iv-deep-dive">Trino on ice IV: Deep dive into Iceberg internals</a></li></ul>

<hr/>

<p><img src="https://trino.io/assets/blog/trino-on-ice/operation.gif" alt=""/></p>

<h2 id="understanding-iceberg-metadata" id="understanding-iceberg-metadata">Understanding Iceberg metadata</h2>

<p>Iceberg can use any compatible metastore, but for Trino, it only supports the  Hive metastore and AWS Glue similar to the Hive connector. This is because there is already a vast amount of testing and support for using the Hive metastore in Trino. Likewise, many Trino use cases that currently use data lakes already use the Hive connector and therefore the Hive metastore. This makes it convenient to have as the leading supported use case as existing users can easily migrate between Hive to Iceberg tables. Since there is no indication of which connector is actually executed in the diagram of the Hive connector architecture, it serves as a diagram that can be used for both Hive and Iceberg. The only difference is the connector used, but if you create a table in Hive, you can  view the same table in Iceberg.</p>

<p><img src="https://trino.io/assets/blog/trino-on-ice/iceberg-metadata.png" alt=""/></p>

<p>To recap the steps taken from the first three blogs; the first blog created an events table, while the first two blogs ran two insert statements. The first insert contained three records, while the second insert contained a single record.</p>

<p><img src="https://trino.io/assets/blog/trino-on-ice/iceberg-snapshot-files.png" alt=""/></p>

<p>Up until this point, the state of the files in MinIO haven’t really been shown except some of the manifest list pointers from the snapshot in the third blog post. Using the <a href="https://docs.min.io/minio/baremetal/reference/minio-cli/minio-mc.html">MinIO client tool</a>, you can list files that Iceberg generated through all these operations and then try to understand what purpose they are serving.</p>

<pre><code>% mc tree -f local/
local/
└─ iceberg
   └─ logging.db
      └─ events
         ├─ data
         │  ├─ event_time_day=2021-04-01
         │  │  ├─ 51eb1ea6-266b-490f-8bca-c63391f02d10.orc
         │  │  └─ cbcf052d-240d-4881-8a68-2bbc0f7e5233.orc
         │  └─ event_time_day=2021-04-02
         │     └─ b012ec20-bbdd-47f5-89d3-57b9e32ea9eb.orc
         └─ metadata
            ├─ 00000-c5cfaab4-f82f-4351-b2a5-bd0e241f84bc.metadata.json
            ├─ 00001-27c8c2d1-fdbb-429d-9263-3654d818250e.metadata.json
            ├─ 00002-33d69acc-94cb-44bc-b2a1-71120e749d9a.metadata.json
            ├─ 23cc980c-9570-42ed-85cf-8658fda2727d-m0.avro
            ├─ 92382234-a4a6-4a1b-bc9b-24839472c2f6-m0.avro
            ├─ snap-2720489016575682283-1-92382234-a4a6-4a1b-bc9b-24839472c2f6.avro
            ├─ snap-4564366177504223943-1-23cc980c-9570-42ed-85cf-8658fda2727d.avro
            └─ snap-6967685587675910019-1-bcbe9133-c51c-42a9-9c73-f5b745702cb0.avro
</code></pre>

<p>There are a lot of files here, but here are a couple of patterns that you can observe with these files.</p>

<p>First, the top two directories are named <code>data</code> and <code>metadata</code>.</p>

<pre><code>/&lt;bucket&gt;/&lt;database&gt;/&lt;table&gt;/data//&lt;bucket&gt;/&lt;database&gt;/&lt;table&gt;/metadata/
</code></pre>

<p>As you might expect, <code>data</code> contains the actual ORC files split by partition. This is akin to what you would see in a Hive table <code>data</code> directory. What is really of interest here is the <code>metadata</code> directory. There are specifically three patterns of files you’ll find here.</p>

<pre><code>/&lt;bucket&gt;/&lt;database&gt;/&lt;table&gt;/metadata/&lt;file-id&gt;.avro

/&lt;bucket&gt;/&lt;database&gt;/&lt;table&gt;/metadata/snap-&lt;snapshot-id&gt;-&lt;version&gt;-&lt;file-id&gt;.avro

/&lt;bucket&gt;/&lt;database&gt;/&lt;table&gt;/metadata/&lt;version&gt;-&lt;commit-UUID&gt;.metadata.json
</code></pre>

<p>Iceberg has a persistent tree structure that manages various snapshots of the data that are created for every mutation of the data. This enables not only a concurrency model that supports serializable isolation, but also cool features like time travel across a linear progression of snapshots.</p>

<p><img src="https://trino.io/assets/blog/trino-on-ice/iceberg-metastore-files.png" alt=""/></p>

<p>This tree structure contains two types of Avro files, manifest lists and manifest files. Manifest list files contain pointers to various manifest files and the manifest files themselves point to various data files. This post starts out by covering these manifest files, and later covers the table metadata files that are suffixed by <code>.metadata.json</code>.</p>

<p><a href="https://bitsondata.dev/trino-on-ice-iii-iceberg-concurrency-snapshots-spec">The last blog covered</a> the command in Trino that shows the snapshot information that is stored in the metastore. Here is that command and its output again for your review.</p>

<pre><code>SELECT manifest_list 
FROM iceberg.logging.&#34;events$snapshots&#34;;
</code></pre>

<p>Result:</p>

<table>
<tr><th>snapshots</th></tr>
<tr><td>s3a://iceberg/logging.db/events/metadata/snap-6967685587675910019-1-bcbe9133-c51c-42a9-9c73-f5b745702cb0.avro</td></tr>
<tr><td>s3a://iceberg/logging.db/events/metadata/snap-2720489016575682283-1-92382234-a4a6-4a1b-bc9b-24839472c2f6.avro</td></tr>
<tr><td>s3a://iceberg/logging.db/events/metadata/snap-4564366177504223943-1-23cc980c-9570-42ed-85cf-8658fda2727d.avro</td></tr>
</table>

<p>You’ll notice that the manifest list returns the Avro files prefixed with
<code>snap-</code> are returned. These files are directly correlated with the snapshot record stored in the metastore. According to the diagram above, snapshots are records in the metastore that contain the url of the manifest list in the Avro file. Avro files are binary files and not something you can just open up in a text editor to read. Using the <a href="https://downloads.apache.org/avro/avro-1.10.2/java/avro-tools-1.10.2.jar">avro-tools.jar tool</a> distributed by the <a href="https://avro.apache.org/docs/current/index.html">Apache Avro project</a>, you can actually inspect the contents of this file to get a better understanding of how it is used by Iceberg.</p>

<p>The first snapshot is generated on the creation of the events table. Upon inspecting this file, you notice that the file is empty. The output is an empty line that the <code>jq</code> JSON command line utility removes on pretty printing the JSON that is returned, which is just a newline. This snapshot represents an empty state of the table upon creation. To investigate the snapshots you need to download the files to your local filesystem. Let&#39;s move them to the home  directory:</p>

<pre><code>% java -jar  ~/Desktop/avro_files/avro-tools-1.10.0.jar tojson ~/snap-6967685587675910019-1-bcbe9133-c51c-42a9-9c73-f5b745702cb0.avro | jq .
</code></pre>

<p>Result: (is empty)</p>

<pre><code>
</code></pre>

<p>The second snapshot is a little more interesting and actually shows us the contents of a manifest list.</p>

<pre><code>% java -jar  ~/Desktop/avro_files/avro-tools-1.10.0.jar tojson ~/snap-2720489016575682283-1-92382234-a4a6-4a1b-bc9b-24839472c2f6.avro | jq .
</code></pre>

<p>Result:</p>

<pre><code>{
   &#34;manifest_path&#34;:&#34;s3a://iceberg/logging.db/events/metadata/92382234-a4a6-4a1b-bc9b-24839472c2f6-m0.avro&#34;,
   &#34;manifest_length&#34;:6114,
   &#34;partition_spec_id&#34;:0,
   &#34;added_snapshot_id&#34;:{
      &#34;long&#34;:2720489016575682000
   },
   &#34;added_data_files_count&#34;:{
      &#34;int&#34;:2
   },
   &#34;existing_data_files_count&#34;:{
      &#34;int&#34;:0
   },
   &#34;deleted_data_files_count&#34;:{
      &#34;int&#34;:0
   },
   &#34;partitions&#34;:{
      &#34;array&#34;:[
         {
            &#34;contains_null&#34;:false,
            &#34;lower_bound&#34;:{
               &#34;bytes&#34;:&#34;\u001eI\u0000\u0000&#34;
            },
            &#34;upper_bound&#34;:{
               &#34;bytes&#34;:&#34;\u001fI\u0000\u0000&#34;
            }
         }
      ]
   },
   &#34;added_rows_count&#34;:{
      &#34;long&#34;:3
   },
   &#34;existing_rows_count&#34;:{
      &#34;long&#34;:0
   },
   &#34;deleted_rows_count&#34;:{
      &#34;long&#34;:0
   }
}
</code></pre>

<p>To understand each of the values in each of these rows, you can refer to the  Iceberg
<a href="https://iceberg.apache.org/spec/#manifest-lists">specification in the manifest list file section</a>. Instead of covering these exhaustively, let&#39;s focus on a few key fields. Below are the fields, and their definition according to the specification.</p>
<ul><li><code>manifest_path</code> – Location of the manifest file.</li>
<li><code>partition_spec_id</code> – ID of a partition spec used to write the manifest; must be listed in table metadata partition-specs.</li>
<li><code>added_snapshot_id</code> – ID of the snapshot where the manifest file was added.</li>
<li><code>partitions</code> – A list of field summaries for each partition field in the spec. Each field in the list corresponds to a field in the manifest file’s partition spec.</li>
<li><code>added_rows_count</code> – Number of rows in all files in the manifest that have status ADDED, when null this is assumed to be non-zero.</li></ul>

<p>As mentioned above, manifest lists hold references to various manifest files. These manifest paths are the pointers in the persistent tree that tells any client using Iceberg where to find all of the manifest files associated with a particular snapshot. To traverse this tree, you can look over the different manifest paths to find all the manifest files associated with the particular snapshot you want to traverse. Partition spec ids are helpful to know the current partition specification which are stored in the table metadata in the metastore. This references where to find the spec in the metastore. Added snapshot ids tells you which snapshot is associated with the manifest list. Partitions hold some high level partition bound information to make for faster querying. If a query is looking for a particular value, it only traverses the manifest files where the query values fall within the range of the file values. Finally, you get a few metrics like the number of changed rows and data files, one of which is the count of added rows. The first operation consisted of three rows inserts and the second operation was the insertion of one row. Using the row counts you can easily determine which manifest file belongs to which operation.</p>

<p>The following command shows the final snapshot after both operations executed and filters out only the fields pointed out above.</p>

<pre><code>% java -jar  ~/Desktop/avro_files/avro-tools-1.10.0.jar tojson ~/snap-4564366177504223943-1-23cc980c-9570-42ed-85cf-8658fda2727d.avro | jq &#39;. | {manifest_path: .manifest_path, partition_spec_id: .partition_spec_id, added_snapshot_id: .added_snapshot_id, partitions: .partitions, added_rows_count: .added_rows_count }&#39;
</code></pre>

<p>Result:</p>

<pre><code>{
   &#34;manifest_path&#34;:&#34;s3a://iceberg/logging.db/events/metadata/23cc980c-9570-42ed-85cf-8658fda2727d-m0.avro&#34;,
   &#34;partition_spec_id&#34;:0,
   &#34;added_snapshot_id&#34;:{
      &#34;long&#34;:4564366177504223700
   },
   &#34;partitions&#34;:{
      &#34;array&#34;:[
         {
            &#34;contains_null&#34;:false,
            &#34;lower_bound&#34;:{
               &#34;bytes&#34;:&#34;\u001eI\u0000\u0000&#34;
            },
            &#34;upper_bound&#34;:{
               &#34;bytes&#34;:&#34;\u001eI\u0000\u0000&#34;
            }
         }
      ]
   },
   &#34;added_rows_count&#34;:{
      &#34;long&#34;:1
   }
}
{
   &#34;manifest_path&#34;:&#34;s3a://iceberg/logging.db/events/metadata/92382234-a4a6-4a1b-bc9b-24839472c2f6-m0.avro&#34;,
   &#34;partition_spec_id&#34;:0,
   &#34;added_snapshot_id&#34;:{
      &#34;long&#34;:2720489016575682000
   },
   &#34;partitions&#34;:{
      &#34;array&#34;:[
         {
            &#34;contains_null&#34;:false,
            &#34;lower_bound&#34;:{
               &#34;bytes&#34;:&#34;\u001eI\u0000\u0000&#34;
            },
            &#34;upper_bound&#34;:{
               &#34;bytes&#34;:&#34;\u001fI\u0000\u0000&#34;
            }
         }
      ]
   },
   &#34;added_rows_count&#34;:{
      &#34;long&#34;:3
   }
}
</code></pre>

<p>In the listing of the manifest file related to the last snapshot, you notice the first operation where three rows were inserted is contained in the manifest file in the second JSON object. You can determine this from the snapshot id, as well as, the number of rows that were added in the operation. The first JSON object contains the last operation that inserted a single row. So the most recent operations are listed in reverse commit order.</p>

<p>The next command does the same listing of the file that you ran with the manifest list, except you run this on the manifest files themselves to expose their contents and discuss them. To begin with, you run the command to show the contents of the manifest file associated with the insertion of three rows.</p>

<pre><code>% java -jar  ~/avro-tools-1.10.0.jar tojson ~/Desktop/avro_files/92382234-a4a6-4a1b-bc9b-24839472c2f6-m0.avro | jq .
</code></pre>

<p>Result:</p>

<pre><code>{
   &#34;status&#34;:1,
   &#34;snapshot_id&#34;:{
      &#34;long&#34;:2720489016575682000
   },
   &#34;data_file&#34;:{
      &#34;file_path&#34;:&#34;s3a://iceberg/logging.db/events/data/event_time_day=2021-04-01/51eb1ea6-266b-490f-8bca-c63391f02d10.orc&#34;,
      &#34;file_format&#34;:&#34;ORC&#34;,
      &#34;partition&#34;:{
         &#34;event_time_day&#34;:{
            &#34;int&#34;:18718
         }
      },
      &#34;record_count&#34;:1,
      &#34;file_size_in_bytes&#34;:870,
      &#34;block_size_in_bytes&#34;:67108864,
      &#34;column_sizes&#34;:null,
      &#34;value_counts&#34;:{
         &#34;array&#34;:[
            {
               &#34;key&#34;:1,
               &#34;value&#34;:1
            },
            {
               &#34;key&#34;:2,
               &#34;value&#34;:1
            },
            {
               &#34;key&#34;:3,
               &#34;value&#34;:1
            },
            {
               &#34;key&#34;:4,
               &#34;value&#34;:1
            }
         ]
      },
      &#34;null_value_counts&#34;:{
         &#34;array&#34;:[
            {
               &#34;key&#34;:1,
               &#34;value&#34;:0
            },
            {
               &#34;key&#34;:2,
               &#34;value&#34;:0
            },
            {
               &#34;key&#34;:3,
               &#34;value&#34;:0
            },
            {
               &#34;key&#34;:4,
               &#34;value&#34;:0
            }
         ]
      },
      &#34;nan_value_counts&#34;:null,
      &#34;lower_bounds&#34;:{
         &#34;array&#34;:[
            {
               &#34;key&#34;:1,
               &#34;value&#34;:&#34;ERROR&#34;
            },
            {
               &#34;key&#34;:3,
               &#34;value&#34;:&#34;Oh noes&#34;
            }
         ]
      },
      &#34;upper_bounds&#34;:{
         &#34;array&#34;:[
            {
               &#34;key&#34;:1,
               &#34;value&#34;:&#34;ERROR&#34;
            },
            {
               &#34;key&#34;:3,
               &#34;value&#34;:&#34;Oh noes&#34;
            }
         ]
      },
      &#34;key_metadata&#34;:null,
      &#34;split_offsets&#34;:null
   }
}
{
   &#34;status&#34;:1,
   &#34;snapshot_id&#34;:{
      &#34;long&#34;:2720489016575682000
   },
   &#34;data_file&#34;:{
      &#34;file_path&#34;:&#34;s3a://iceberg/logging.db/events/data/event_time_day=2021-04-02/b012ec20-bbdd-47f5-89d3-57b9e32ea9eb.orc&#34;,
      &#34;file_format&#34;:&#34;ORC&#34;,
      &#34;partition&#34;:{
         &#34;event_time_day&#34;:{
            &#34;int&#34;:18719
         }
      },
      &#34;record_count&#34;:2,
      &#34;file_size_in_bytes&#34;:1084,
      &#34;block_size_in_bytes&#34;:67108864,
      &#34;column_sizes&#34;:null,
      &#34;value_counts&#34;:{
         &#34;array&#34;:[
            {
               &#34;key&#34;:1,
               &#34;value&#34;:2
            },
            {
               &#34;key&#34;:2,
               &#34;value&#34;:2
            },
            {
               &#34;key&#34;:3,
               &#34;value&#34;:2
            },
            {
               &#34;key&#34;:4,
               &#34;value&#34;:2
            }
         ]
      },
      &#34;null_value_counts&#34;:{
         &#34;array&#34;:[
            {
               &#34;key&#34;:1,
               &#34;value&#34;:0
            },
            {
               &#34;key&#34;:2,
               &#34;value&#34;:0
            },
            {
               &#34;key&#34;:3,
               &#34;value&#34;:0
            },
            {
               &#34;key&#34;:4,
               &#34;value&#34;:0
            }
         ]
      },
      &#34;nan_value_counts&#34;:null,
      &#34;lower_bounds&#34;:{
         &#34;array&#34;:[
            {
               &#34;key&#34;:1,
               &#34;value&#34;:&#34;ERROR&#34;
            },
            {
               &#34;key&#34;:3,
               &#34;value&#34;:&#34;Double oh noes&#34;
            }
         ]
      },
      &#34;upper_bounds&#34;:{
         &#34;array&#34;:[
            {
               &#34;key&#34;:1,
               &#34;value&#34;:&#34;WARN&#34;
            },
            {
               &#34;key&#34;:3,
               &#34;value&#34;:&#34;Maybeh oh noes?&#34;
            }
         ]
      },
      &#34;key_metadata&#34;:null,
      &#34;split_offsets&#34;:null
   }
}
</code></pre>

<p>Now this is a very big output, but in summary, there’s really not too much to these files. As before, there is a <a href="https://iceberg.apache.org/spec/#manifests">Manifest section in the Iceberg spec</a> that details what each of these fields means. Here are the important fields:</p>
<ul><li><code>snapshot_id</code> – Snapshot id where the file was added, or deleted if status is two. Inherited when null.</li>
<li><code>data_file</code> – Field containing metadata about the data files pertaining to the manifest file, such as file path, partition tuple, metrics, etc…</li>
<li><code>data_file.file_path</code> – Full URI for the file with FS scheme.</li>
<li><code>data_file.partition</code> – Partition data tuple, schema based on the partition spec.</li>
<li><code>data_file.record_count</code> – Number of records in the data file.</li>
<li><code>data_file.*_count</code> – Multiple fields that contain a map from column id to  number of values, null, nan counts in the file. These can be used to quickly  filter out unnecessary get operations.</li>
<li><code>data_file.*_bounds</code> – Multiple fields that contain a map from column id to lower or upper bound in the column serialized as binary. Each value must be less than or equal to all non-null, non-NaN values in the column for the file.</li></ul>

<p>Each data file struct contains a partition and data file that it maps to. These files only be scanned and returned if the criteria for the query is met when  checking all of the count, bounds, and other statistics that are recorded in the file. Ideally only files that contain data relevant to the query should be scanned at all. Having information like the record count may also help in the query planning process to determine splits and other information. This particular optimization hasn’t been completed yet as planning typically happens before traversal of the files. It is still in ongoing discussion and <a href="https://youtu.be/ifXpOn0NJWk?t=2132">is discussed a bit by Iceberg creator Ryan Blue in a recent meetup</a>. If this is something you are interested in, keep posted on the Slack channel and releases as the Trino Iceberg connector progresses in this area.</p>

<p>As mentioned above, the last set of files that you find in the metadata directory which are suffixed with <code>.metadata.json</code>. These files at baseline are a bit strange as they aren’t stored in the Avro format, but instead the JSON format. This is because they are not part of the persistent tree structure. These files are essentially a copy of the table metadata that is stored in the metastore. You can find the fields for the table metadata listed <a href="https://iceberg.apache.org/spec/#table-metadata-fields">in the Iceberg specification</a>. These tables are typically stored persistently in a metasture much like the Hive metastore but could easily be replaced by any datastore that can support <a href="https://iceberg.apache.org/spec/#metastore-tables">an atomic swap (check-and-put) operation</a> required for Iceberg to support the optimistic concurrency operation.</p>

<p>The naming of the table metadata includes a table version and UUID:
<code>&lt;table-version&gt;-&lt;UUID&gt;.metadata.json</code>. To commit a new metadata version, which just adds 1 to the current version number, the writer performs these steps:</p>
<ol><li>It creates a new table metadata file using the current metadata.</li>
<li>It writes the new table metadata to a file following the naming with the next version number.</li>

<li><p>It requests the metastore swap the table’s metadata pointer from the old location to the new location.</p>
<ol><li>If the swap succeeds, the commit succeeded. The new file is now the
current metadata.</li>
<li>If the swap fails, another writer has already created their own. The
current writer goes back to step 1.</li></ol></li></ol>

<p>If you want to see where this is stored in the Hive metastore, you can reference the <code>TABLE_PARAMS</code> table. At the time of writing, this is the only method of using the metastore that is supported by the Trino Iceberg connector.</p>

<pre><code>SELECT PARAM_KEY, PARAM_VALUE FROM metastore.TABLE_PARAMS;
</code></pre>

<p>Result:</p>

<table>
<tr><th>PARAM_KEY</th><th>PARAM_VALUE</th></tr>
<tr><td>EXTERNAL</td><td>TRUE</td></tr>
<tr><td>metadata_location</td><td>s3a://iceberg/logging.db/events/metadata/00002-33d69acc-94cb-44bc-b2a1-71120e749d9a.metadata.json</td></tr>
<tr><td>numFiles</td><td>2</td></tr>
<tr><td>previous_metadata_location</td><td>s3a://iceberg/logging.db/events/metadata/00001-27c8c2d1-fdbb-429d-9263-3654d818250e.metadata.json</td></tr>
<tr><td>table_type</td><td>iceberg</td></tr>
<tr><td>totalSize</td><td>5323</td></tr>
<tr><td>transient_lastDdlTime</td><td>1622865672</td></tr>
</table>

<p>So as you can see, the metastore is saying the current metadata location is the
<code>00002-33d69acc-94cb-44bc-b2a1-71120e749d9a.metadata.json</code> file. Now you can dive in to see the table metadata that is being used by the Iceberg connector.</p>

<pre><code>% cat ~/Desktop/avro_files/00002-33d69acc-94cb-44bc-b2a1-71120e749d9a.metadata.json
</code></pre>

<p>Result:</p>

<pre><code>{
   &#34;format-version&#34;:1,
   &#34;table-uuid&#34;:&#34;32e3c271-84a9-4be5-9342-2148c878227a&#34;,
   &#34;location&#34;:&#34;s3a://iceberg/logging.db/events&#34;,
   &#34;last-updated-ms&#34;:1622865686323,
   &#34;last-column-id&#34;:5,
   &#34;schema&#34;:{
      &#34;type&#34;:&#34;struct&#34;,
      &#34;fields&#34;:[
         {
            &#34;id&#34;:1,
            &#34;name&#34;:&#34;level&#34;,
            &#34;required&#34;:false,
            &#34;type&#34;:&#34;string&#34;
         },
         {
            &#34;id&#34;:2,
            &#34;name&#34;:&#34;event_time&#34;,
            &#34;required&#34;:false,
            &#34;type&#34;:&#34;timestamp&#34;
         },
         {
            &#34;id&#34;:3,
            &#34;name&#34;:&#34;message&#34;,
            &#34;required&#34;:false,
            &#34;type&#34;:&#34;string&#34;
         },
         {
            &#34;id&#34;:4,
            &#34;name&#34;:&#34;call_stack&#34;,
            &#34;required&#34;:false,
            &#34;type&#34;:{
               &#34;type&#34;:&#34;list&#34;,
               &#34;element-id&#34;:5,
               &#34;element&#34;:&#34;string&#34;,
               &#34;element-required&#34;:false
            }
         }
      ]
   },
   &#34;partition-spec&#34;:[
      {
         &#34;name&#34;:&#34;event_time_day&#34;,
         &#34;transform&#34;:&#34;day&#34;,
         &#34;source-id&#34;:2,
         &#34;field-id&#34;:1000
      }
   ],
   &#34;default-spec-id&#34;:0,
   &#34;partition-specs&#34;:[
      {
         &#34;spec-id&#34;:0,
         &#34;fields&#34;:[
            {
               &#34;name&#34;:&#34;event_time_day&#34;,
               &#34;transform&#34;:&#34;day&#34;,
               &#34;source-id&#34;:2,
               &#34;field-id&#34;:1000
            }
         ]
      }
   ],
   &#34;default-sort-order-id&#34;:0,
   &#34;sort-orders&#34;:[
      {
         &#34;order-id&#34;:0,
         &#34;fields&#34;:[
            
         ]
      }
   ],
   &#34;properties&#34;:{
      &#34;write.format.default&#34;:&#34;ORC&#34;
   },
   &#34;current-snapshot-id&#34;:4564366177504223943,
   &#34;snapshots&#34;:[
      {
         &#34;snapshot-id&#34;:6967685587675910019,
         &#34;timestamp-ms&#34;:1622865672882,
         &#34;summary&#34;:{
            &#34;operation&#34;:&#34;append&#34;,
            &#34;changed-partition-count&#34;:&#34;0&#34;,
            &#34;total-records&#34;:&#34;0&#34;,
            &#34;total-data-files&#34;:&#34;0&#34;,
            &#34;total-delete-files&#34;:&#34;0&#34;,
            &#34;total-position-deletes&#34;:&#34;0&#34;,
            &#34;total-equality-deletes&#34;:&#34;0&#34;
         },
         &#34;manifest-list&#34;:&#34;s3a://iceberg/logging.db/events/metadata/snap-6967685587675910019-1-bcbe9133-c51c-42a9-9c73-f5b745702cb0.avro&#34;
      },
      {
         &#34;snapshot-id&#34;:2720489016575682283,
         &#34;parent-snapshot-id&#34;:6967685587675910019,
         &#34;timestamp-ms&#34;:1622865680419,
         &#34;summary&#34;:{
            &#34;operation&#34;:&#34;append&#34;,
            &#34;added-data-files&#34;:&#34;2&#34;,
            &#34;added-records&#34;:&#34;3&#34;,
            &#34;added-files-size&#34;:&#34;1954&#34;,
            &#34;changed-partition-count&#34;:&#34;2&#34;,
            &#34;total-records&#34;:&#34;3&#34;,
            &#34;total-data-files&#34;:&#34;2&#34;,
            &#34;total-delete-files&#34;:&#34;0&#34;,
            &#34;total-position-deletes&#34;:&#34;0&#34;,
            &#34;total-equality-deletes&#34;:&#34;0&#34;
         },
         &#34;manifest-list&#34;:&#34;s3a://iceberg/logging.db/events/metadata/snap-2720489016575682283-1-92382234-a4a6-4a1b-bc9b-24839472c2f6.avro&#34;
      },
      {
         &#34;snapshot-id&#34;:4564366177504223943,
         &#34;parent-snapshot-id&#34;:2720489016575682283,
         &#34;timestamp-ms&#34;:1622865686278,
         &#34;summary&#34;:{
            &#34;operation&#34;:&#34;append&#34;,
            &#34;added-data-files&#34;:&#34;1&#34;,
            &#34;added-records&#34;:&#34;1&#34;,
            &#34;added-files-size&#34;:&#34;746&#34;,
            &#34;changed-partition-count&#34;:&#34;1&#34;,
            &#34;total-records&#34;:&#34;4&#34;,
            &#34;total-data-files&#34;:&#34;3&#34;,
            &#34;total-delete-files&#34;:&#34;0&#34;,
            &#34;total-position-deletes&#34;:&#34;0&#34;,
            &#34;total-equality-deletes&#34;:&#34;0&#34;
         },
         &#34;manifest-list&#34;:&#34;s3a://iceberg/logging.db/events/metadata/snap-4564366177504223943-1-23cc980c-9570-42ed-85cf-8658fda2727d.avro&#34;
      }
   ],
   &#34;snapshot-log&#34;:[
      {
         &#34;timestamp-ms&#34;:1622865672882,
         &#34;snapshot-id&#34;:6967685587675910019
      },
      {
         &#34;timestamp-ms&#34;:1622865680419,
         &#34;snapshot-id&#34;:2720489016575682283
      },
      {
         &#34;timestamp-ms&#34;:1622865686278,
         &#34;snapshot-id&#34;:4564366177504223943
      }
   ],
   &#34;metadata-log&#34;:[
      {
         &#34;timestamp-ms&#34;:1622865672894,
         &#34;metadata-file&#34;:&#34;s3a://iceberg/logging.db/events/metadata/00000-c5cfaab4-f82f-4351-b2a5-bd0e241f84bc.metadata.json&#34;
      },
      {
         &#34;timestamp-ms&#34;:1622865680524,
         &#34;metadata-file&#34;:&#34;s3a://iceberg/logging.db/events/metadata/00001-27c8c2d1-fdbb-429d-9263-3654d818250e.metadata.json&#34;
      }
   ]
}
</code></pre>

<p>As you can see, these JSON files can quickly grow as you perform different updates on your table. This file contains a pointer to all of the snapshots and manifest list files, much like the output you found from looking at the snapshots in the table. A really important piece to note is the schema is stored here. This is what Trino uses for validation on inserts and reads. As you may expect, there is the root location of the table itself, as well as a unique table identifier. The final part I’d like to note about this file is the partition-spec and partition-specs fields. The partition-spec field holds the current partition spec, while the partition-specs is an array that can hold a list of all partition specs that have existed for this table. As pointed out earlier, you can have many different manifest files that use different partition specs. That wraps up all of the metadata file types you can expect to see in Iceberg!</p>

<p>This post wraps up the Trino on ice series. Hopefully these blog posts serve as a helpful initial dialogue about what is expected to grow as a vital portion of an open data lakehouse stack. What are you waiting for? Come join the fun and help us implement some of the missing features or instead go ahead and try <a href="https://github.com/bitsondatadev/trino-getting-started/tree/main/iceberg/trino-iceberg-minio">Trino on Ice(berg)</a> yourself!</p>

<p><a href="https://bitsondata.dev/tag:trino" class="hashtag"><span>#</span><span class="p-category">trino</span></a> <a href="https://bitsondata.dev/tag:iceberg" class="hashtag"><span>#</span><span class="p-category">iceberg</span></a></p>
]]></content:encoded>
      <guid>https://bitsondata.dev/trino-iceberg-iv-deep-dive</guid>
      <pubDate>Thu, 12 Aug 2021 05:00:00 +0000</pubDate>
    </item>
    <item>
      <title>Trino on ice III: Iceberg concurrency model, snapshots, and the Iceberg spec</title>
      <link>https://bitsondata.dev/trino-iceberg-iii-concurrency-snapshots-spec?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[&#xA;&#xA;In the last two blog posts, we’ve covered a lot of cool feature improvements of Iceberg over the Hive model. I recommend you take a look at those if you haven’t yet. We introduced concepts and issues that table formats address. This blog closes up the overview of Iceberg features by discussing the concurrency model Iceberg uses to ensure data integrity, how to use snapshots via Trino, and the Iceberg Specification.&#xA;&#xA;!--more--&#xA;&#xA;---&#xA;&#xA;Trino on ice is a series, covering the details around how the Iceberg table format works with the Trino query engine. It’s recommended to read the posts sequentially as the examples build on previous posts in this series:&#xA;&#xA;Trino on ice I: A gentle introduction to Iceberg&#xA;Trino on ice II: In-place table evolution and cloud compatibility with Iceberg&#xA;Trino on ice III: Iceberg concurrency model, snapshots, and the Iceberg spec&#xA;Trino on ice IV: Deep dive into Iceberg internals&#xA;&#xA;---&#xA;&#xA;Concurrency Model&#xA; Some issues with the Hive model are the distinct locations where the metadata is stored and where the data files are stored. Having your data and metadata split up like this is a recipe for disaster when trying to apply updates to both services atomically.&#xA;&#xA; Iceberg metadata diagram of runtime, and file storage&#xA; A very common problem with Hive is that if a writing process failed during insertion, many times you would find the data written to file storage, but the metastore writes failed to occur. Or conversely, the metastore writes were successful, but the data failed to finish writing to file storage due to a  network or file IO failure. There’s a good  Trino Community Broadcast episode that talks about a function in Trino that exists to resolve these issues by syncing the metastore and file storage. You can watch  a simulation of this error on that episode.&#xA;&#xA; Aside from having issues due to the split state in the system, there are many  other issues that stem from the file system itself. In the case of HDFS,  depending on the specific filesystem implementation you are using, you may have different atomicity guarantees for various file systems and their operations, such as creating, deleting, and renaming files and directories. HDFS isn’t the only troublemaker here. Other than Amazon S3’s  recent announcement of strong consistency in their S3 service, most object storage systems only offer eventual consistency that may not show the latest files immediately after writes. Despite storage systems showing more progress towards offering better performance and guarantees, these systems still offer no reliable locking mechanism.&#xA;&#xA; Iceberg addresses all of these issues in a multitude of ways. One of the primary ways Iceberg introduces transactional guarantees is by storing the metadata in the same datastore as the data itself. This simplifies handling commit failures down to rolling back on one system rather than trying to coordinate a rollback across two systems like in Hive. Writers independently write their metadata and attempt to perform their operations, needing no coordination with other writers. The only time the writers coordinate is when they attempt to perform a commit of their operations. In order to do a commit, they perform a lock of the current snapshot record in a database. This concurrency model where writers eagerly do the work upfront is called optimistic concurrency control.&#xA; Currently, in Trino, this method still uses the Hive metastore to perform the lock-and-swap operation necessary to coordinate the final commits. Iceberg  creator, Ryan Blue, covers this lock-and-swap mechanism and how the metastore can be replaced with alternate locking methods. In the event that two writers attempt to commit at the same time, the writer that first acquires the lock successfully commits by swapping its snapshot as the current snapshot, while the second writer will retry to apply its changes again. The second writer should have no problem with this, assuming there are no conflicting changes between the two snapshots.&#xA;&#xA; &#xA;&#xA; This works similarly to a git workflow where the main branch is the locked resource, and two developers try to commit their changes at the same time. The first developer’s changes may conflict with the second developer’s changes. The second developer is then forced to rebase or merge the first developer’s code with their changes before commiting to the main branch again. The same logic applies to merging data files. Currently, Iceberg clients use a copy-on-write mechanism that makes a new file out of the merged data in the next snapshot. This enables accurate time traveling and preserves previous split versions of the files. At the time of writing, upserts via MERGE INTO syntax are not supported in Trino, but  this is in active development. UPDATE: Since the original writing of this post, the  MERGE syntax exists as of version 393.&#xA;&#xA; One of the great benefits of tracking each individual change that gets written to Iceberg is that you are given a view of the data at every point in time. This enables a really cool feature that I mentioned earlier called time travel.&#xA;&#xA; ## Snapshots and Time Travel&#xA;&#xA; To showcase snapshots, it’s best to go over a few examples drawing from the event table we  created in the previous blog posts. This time we’ll only be working with the Iceberg table, as this capability is not available in Hive. Snapshots allow you to have an immutable set of your data at a given time. They are automatically created on every append or removal of data. One thing to note is that for now, they do not store the state of your metadata.&#xA; Say that you have c&#xA; reated your events table and inserted the three initial rows as we did previously. Let’s look at the data we get back and see how to check the existing snapshots in Trino:&#xA;&#xA; &#xA;SELECT level, message&#xA;FROM iceberg.logging.events;&#xA;&#xA;Result:&#xA;&#xA;| level | message |&#xA;| --- | --- |&#xA;| ERROR | Double oh noes |&#xA;| WARN | Maybeh oh noes? |&#xA;| ERROR | Oh noes |&#xA;&#xA;To query the snapshots, all you need is to use the $ operator appended to the&#xA;end of the table name, and add the hidden table, snapshots:&#xA;&#xA;SELECT snapshotid, parentid, operation&#xA;FROM iceberg.logging.“events$snapshots”;&#xA;&#xA;Result:&#xA;&#xA;| snapshotid | parentid | operation |&#xA;| --- | --- | --- |&#xA;| 7620328658793169607 | | append |&#xA;| 2115743741823353537 | 7620328658793169607 | append |&#xA;&#xA;Let’s take a look at the manifest list files that are associated with each &#xA;snapshot ID. You can tell which file belongs to which snapshot based on the &#xA;snapshot ID embedded in the filename:&#xA;&#xA;SELECT manifestlist&#xA;FROM iceberg.logging.“events$snapshots”;&#xA;&#xA;Result:&#xA;&#xA;| shapshots |&#xA;| --- |&#xA;| s3a://iceberg/logging.db/events/metadata/snap-7620328658793169607-1-cc857d89-1c07-4087-bdbc-2144a814dae2.avro | &#xA;| s3a://iceberg/logging.db/events/metadata/snap-2115743741823353537-1-4cb458be-7152-4e99-8db7-b2dda52c556c.avro | &#xA;&#xA;Now, let’s insert another row to the table:&#xA;&#xA;INSERT INTO iceberg.logging.events&#xA;VALUES&#xA;(&#xA;‘INFO’,&#xA;timestamp ‘2021-04-02 00:00:11.1122222’,&#xA;‘It is all good’,&#xA;ARRAY [‘Just updating you!’]&#xA;);&#xA;&#xA;Let’s check the snapshot table again:&#xA;&#xA;SELECT snapshotid, parentid, operation&#xA;FROM iceberg.logging.“events$snapshots”;&#xA;&#xA;Result:&#xA;&#xA;| snapshotid | parentid | operation |&#xA;| --- | --- | --- |&#xA;| 7620328658793169607 | | append |&#xA;| 2115743741823353537 | 7620328658793169607 | append |&#xA;| 7030511368881343137 | 2115743741823353537 | append |&#xA;&#xA;Let’s also verify that our row was added:&#xA;&#xA;SELECT level, message&#xA;FROM iceberg.logging.events;&#xA;&#xA;Result:&#xA;&#xA;| level | message |&#xA;| --- | --- |&#xA;|ERROR|Oh noes |&#xA;|INFO |It is all good |&#xA;|ERROR|Double oh noes |&#xA;|WARN |Maybeh oh noes?|&#xA;&#xA; Since Iceberg is already tracking the list of files added and removed at each snapshot, it would make sense that you can travel back and forth between these different views into the system, right? This concept is called time traveling. You need to specify which snapshot you would like to read from and you will see the view of the data at that timestamp. In Trino, you need to use the @ operator, followed by the snapshot you wish to read from:&#xA; &#xA;&#xA;SELECT level, message&#xA;FROM iceberg.logging.“events@2115743741823353537”;&#xA;&#xA;Result:&#xA;&#xA;| level | message |&#xA;| --- | --- |&#xA;|ERROR|Double oh noes |&#xA;|WARN |Maybeh oh noes?|&#xA;|ERROR|Oh noes |&#xA;&#xA; If you determine there is some issue with your data, you can always roll back to the previous state permanently as well. In Trino we have a function called rollbacktosnapshot to move the table state to another snapshot:&#xA; &#xA;CALL system.rollbacktosnapshot(‘logging’, ‘events’, 2115743741823353537);&#xA;&#xA;Now that we have rolled back, observe what happens when we query the events&#xA;table with:&#xA;&#xA;SELECT level, message&#xA;FROM iceberg.logging.events;&#xA;&#xA;Result:&#xA;&#xA;| level | message |&#xA;| --- | --- |&#xA;|ERROR|Double oh noes |&#xA;|WARN |Maybeh oh noes?|&#xA;|ERROR|Oh noes |&#xA; &#xA; Notice the INFO row is still missing even though we query the table without specifying a snapshot id. Now just because we rolled back, doesn’t mean we’ve lost the snapshot we just rolled back from. In fact, we can roll forward, or as I like to call it,  back to the future! In Trino, you use the same function call but with a predecessor of the existing snapshot:&#xA; &#xA;CALL system.rollbacktosnapshot(‘logging’, ‘events’, 7030511368881343137)&#xA;&#xA;And now we should be able to query the table again and see the INFO row &#xA;return:&#xA;&#xA;SELECT level, message&#xA;FROM iceberg.logging.events;&#xA;&#xA;Result:&#xA;&#xA;| level | message |&#xA;| --- | --- |&#xA;|ERROR|Oh noes |&#xA;|INFO |It is all good |&#xA;|ERROR|Double oh noes |&#xA;|WARN |Maybeh oh noes?|&#xA; &#xA; As expected, the INFO row returns when you roll back to the future.&#xA; &#xA; Having snapshots not only provides you with a level of immutability that is key to the eventual consistency model, but gives you a rich set of features to version and move between different versions of your data like a git repository.&#xA; &#xA; ## Iceberg Specification&#xA; &#xA; Perhaps saving the best for last, the benefit of using Iceberg is the community that surrounds it, and the support you receive. It can be daunting to have to choose a project that replaces something so core to your architecture. While Hive has so many drawbacks, one of the things keeping many companies locked in is the fear of the unknown. How do you know which table format to choose? Are there unknown data corruption issues that I’m about to take on? What if this doesn’t scale like it promises on the label? It is worth noting that  alternative table formats are also emerging in this space  and we encourage you to investigate these for your own use cases. When sitting down with Iceberg creator, Ryan Blue,  comparing Iceberg to other table formats,  he claims the community’s greatest strength is their ability to look forward. They intentionally broke compatibility with Hive to enable them to provide a richer level of features. Unlike Hive, the Iceberg project explained their thinking in a spec.&#xA;&#xA; The strongest argument I can see for Iceberg is that it has a specification. This is something that has largely been missing from Hive and shows a real maturity in how the Iceberg community has approached the issue. On the Trino project, we think standards are important. We adhere to many of them ourselves, such as the ANSI SQL syntax, and exposing the client through a JDBC connection. By creating a standard around this, you’re no longer tied to any particular technology, not even Iceberg itself. You are adhering to a standard that will hopefully become the de facto standard over a decade or two, much like Hive did. Having the standard in clear writing invites multiple communities to the table and brings even more use  cases. Doing so improves the standards and therefore the technologies that implement them.&#xA; &#xA; The previous three blog posts of this series covered the features and massive benefits from using this novel table format. The following post will dive deeper and discuss more about how Iceberg achieves some of this functionality, with an overview into some of the internals and metadata layouts. In the meantime, feel free to try  Trino on Ice(berg).&#xA;&#xA;#trino #iceberg&#xA;&#xA;bits_&#xA;&#xA;!--emailsub--]]&gt;</description>
      <content:encoded><![CDATA[<p><img src="https://trino.io/assets/blog/trino-on-ice/trino-iceberg.png" alt=""/></p>

<p>In the last two blog posts, we’ve covered a lot of cool feature improvements of Iceberg over the Hive model. I recommend you take a look at those if you haven’t yet. We introduced concepts and issues that table formats address. This blog closes up the overview of Iceberg features by discussing the concurrency model Iceberg uses to ensure data integrity, how to use snapshots via Trino, and the <a href="https://iceberg.apache.org/spec/">Iceberg Specification</a>.</p>



<hr/>

<p>Trino on ice is a series, covering the details around how the Iceberg table format works with the Trino query engine. It’s recommended to read the posts sequentially as the examples build on previous posts in this series:</p>
<ul><li><a href="https://bitsondata.dev/trino-iceberg-i-gentle-intro">Trino on ice I: A gentle introduction to Iceberg</a></li>
<li><a href="https://bitsondata.dev/trino-iceberg-ii-table-evolution-cloud">Trino on ice II: In-place table evolution and cloud compatibility with Iceberg</a></li>
<li><a href="https://write.as/bitsondatadev/trino-iceberg-iii-concurrency-snapshots-spec">Trino on ice III: Iceberg concurrency model, snapshots, and the Iceberg spec</a></li>
<li><a href="https://bitsondata.dev/trino-iceberg-iv-deep-dive">Trino on ice IV: Deep dive into Iceberg internals</a></li></ul>

<hr/>

<h2 id="concurrency-model" id="concurrency-model">Concurrency Model</h2>

<p> Some issues with the Hive model are the distinct locations where the metadata is stored and where the data files are stored. Having your data and metadata split up like this is a recipe for disaster when trying to apply updates to both services atomically.</p>

<p> <img src="https://trino.io/assets/blog/trino-on-ice/iceberg-metadata.png" alt="Iceberg metadata diagram of runtime, and file storage"/>
 A very common problem with Hive is that if a writing process failed during insertion, many times you would find the data written to file storage, but the metastore writes failed to occur. Or conversely, the metastore writes were successful, but the data failed to finish writing to file storage due to a  network or file IO failure. There’s a good  <a href="https://trino.io/episodes/5.html">Trino Community Broadcast episode</a> that talks about a function in Trino that exists to resolve these issues by syncing the metastore and file storage. You can watch  <a href="https://www.youtube.com/watch?v=OXyJFZSsX5w&amp;t=2097s">a simulation of this error</a> on that episode.</p>

<p> Aside from having issues due to the split state in the system, there are many  other issues that stem from the file system itself. In the case of HDFS,  depending on the specific filesystem implementation you are using, you may have <a href="https://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-common/filesystem/introduction.html#Core_Expectations_of_a_Hadoop_Compatible_FileSystem">different atomicity guarantees for various file systems and their operations</a>, such as creating, deleting, and renaming files and directories. HDFS isn’t the only troublemaker here. Other than Amazon S3’s  <a href="https://aws.amazon.com/about-aws/whats-new/2020/12/amazon-s3-now-delivers-strong-read-after-write-consistency-automatically-for-all-applications/">recent announcement of strong consistency in their S3 service,</a> most object storage systems only offer <em>eventual</em> consistency that may not show the latest files immediately after writes. Despite storage systems showing more progress towards offering better performance and guarantees, these systems still offer no reliable locking mechanism.</p>

<p> Iceberg addresses all of these issues in a multitude of ways. One of the primary ways Iceberg introduces transactional guarantees is by storing the metadata in the same datastore as the data itself. This simplifies handling commit failures down to rolling back on one system rather than trying to coordinate a rollback across two systems like in Hive. Writers independently write their metadata and attempt to perform their operations, needing no coordination with other writers. The only time the writers coordinate is when they attempt to perform a commit of their operations. In order to do a commit, they perform a lock of the current snapshot record in a database. This concurrency model where writers eagerly do the work upfront is called <strong><em>optimistic concurrency control</em></strong>.
 Currently, in Trino, this method still uses the Hive metastore to perform the lock-and-swap operation necessary to coordinate the final commits. Iceberg  creator, <a href="https://www.linkedin.com/in/rdblue/">Ryan Blue</a>, <a href="https://youtu.be/-iIY2sOFBRc?t=1351">covers this lock-and-swap mechanism</a> and how the metastore can be replaced with alternate locking methods. In the event that <a href="https://iceberg.apache.org/reliability/#concurrent-write-operations">two writers attempt to commit at the same time</a>, the writer that first acquires the lock successfully commits by swapping its snapshot as the current snapshot, while the second writer will retry to apply its changes again. The second writer should have no problem with this, assuming there are no conflicting changes between the two snapshots.</p>

<p> <img src="https://trino.io/assets/blog/trino-on-ice/iceberg-files.png" alt=""/></p>

<p> This works similarly to a git workflow where the main branch is the locked resource, and two developers try to commit their changes at the same time. The first developer’s changes may conflict with the second developer’s changes. The second developer is then forced to rebase or merge the first developer’s code with their changes before commiting to the main branch again. The same logic applies to merging data files. Currently, Iceberg clients use a <a href="https://iceberg.apache.org/reliability/#concurrent-write-operations">copy-on-write mechanism</a> that makes a new file out of the merged data in the next snapshot. This enables accurate time traveling and preserves previous split versions of the files. At the time of writing, upserts via <code>MERGE INTO</code> syntax are not supported in Trino, but  <a href="https://github.com/trinodb/trino/issues/7708">this is in active development</a>. <strong><em>UPDATE:</em></strong> Since the original writing of this post, the  <a href="https://github.com/trinodb/trino/pull/7933"><code>MERGE</code> syntax exists as of version 393</a>.</p>

<p> One of the great benefits of tracking each individual change that gets written to Iceberg is that you are given a view of the data at every point in time. This enables a really cool feature that I mentioned earlier called <strong><em>time travel</em></strong>.</p>

<p> ## Snapshots and Time Travel</p>

<p> To showcase snapshots, it’s best to go over a few examples drawing from the event table we  created in the previous blog posts. This time we’ll only be working with the Iceberg table, as this capability is not available in Hive. Snapshots allow you to have an immutable set of your data at a given time. They are automatically created on every append or removal of data. One thing to note is that for now, they do not store the state of your metadata.
 Say that you have c
 reated your events table and inserted the three initial rows as we did previously. Let’s look at the data we get back and see how to check the existing snapshots in Trino:</p>

<pre><code>SELECT level, message
FROM iceberg.logging.events;
</code></pre>

<p>Result:</p>

<table>
<thead>
<tr>
<th>level</th>
<th>message</th>
</tr>
</thead>

<tbody>
<tr>
<td>ERROR</td>
<td>Double oh noes</td>
</tr>

<tr>
<td>WARN</td>
<td>Maybeh oh noes?</td>
</tr>

<tr>
<td>ERROR</td>
<td>Oh noes</td>
</tr>
</tbody>
</table>

<p>To query the snapshots, all you need is to use the $ operator appended to the
end of the table name, and add the hidden table, <code>snapshots</code>:</p>

<pre><code>SELECT snapshot_id, parent_id, operation
FROM iceberg.logging.“events$snapshots”;
</code></pre>

<p>Result:</p>

<table>
<thead>
<tr>
<th>snapshot_id</th>
<th>parent_id</th>
<th>operation</th>
</tr>
</thead>

<tbody>
<tr>
<td>7620328658793169607</td>
<td></td>
<td>append</td>
</tr>

<tr>
<td>2115743741823353537</td>
<td>7620328658793169607</td>
<td>append</td>
</tr>
</tbody>
</table>

<p>Let’s take a look at the manifest list files that are associated with each
snapshot ID. You can tell which file belongs to which snapshot based on the
snapshot ID embedded in the filename:</p>

<pre><code>SELECT manifest_list
FROM iceberg.logging.“events$snapshots”;
</code></pre>

<p>Result:</p>

<table>
<thead>
<tr>
<th>shapshots</th>
</tr>
</thead>

<tbody>
<tr>
<td>s3a://iceberg/logging.db/events/metadata/snap-7620328658793169607-1-cc857d89-1c07-4087-bdbc-2144a814dae2.avro</td>
</tr>

<tr>
<td>s3a://iceberg/logging.db/events/metadata/snap-2115743741823353537-1-4cb458be-7152-4e99-8db7-b2dda52c556c.avro</td>
</tr>
</tbody>
</table>

<p>Now, let’s insert another row to the table:</p>

<pre><code>INSERT INTO iceberg.logging.events
VALUES
(
‘INFO’,
timestamp ‘2021-04-02 00:00:11.1122222’,
‘It is all good’,
ARRAY [‘Just updating you!’]
);
</code></pre>

<p>Let’s check the snapshot table again:</p>

<pre><code>SELECT snapshot_id, parent_id, operation
FROM iceberg.logging.“events$snapshots”;
</code></pre>

<p>Result:</p>

<table>
<thead>
<tr>
<th>snapshot_id</th>
<th>parent_id</th>
<th>operation</th>
</tr>
</thead>

<tbody>
<tr>
<td>7620328658793169607</td>
<td></td>
<td>append</td>
</tr>

<tr>
<td>2115743741823353537</td>
<td>7620328658793169607</td>
<td>append</td>
</tr>

<tr>
<td>7030511368881343137</td>
<td>2115743741823353537</td>
<td>append</td>
</tr>
</tbody>
</table>

<p>Let’s also verify that our row was added:</p>

<pre><code>SELECT level, message
FROM iceberg.logging.events;
</code></pre>

<p>Result:</p>

<table>
<thead>
<tr>
<th>level</th>
<th>message</th>
</tr>
</thead>

<tbody>
<tr>
<td>ERROR</td>
<td>Oh noes</td>
</tr>

<tr>
<td>INFO</td>
<td>It is all good</td>
</tr>

<tr>
<td>ERROR</td>
<td>Double oh noes</td>
</tr>

<tr>
<td>WARN</td>
<td>Maybeh oh noes?</td>
</tr>
</tbody>
</table>

<p> Since Iceberg is already tracking the list of files added and removed at each snapshot, it would make sense that you can travel back and forth between these different views into the system, right? This concept is called time traveling. You need to specify which snapshot you would like to read from and you will see the view of the data at that timestamp. In Trino, you need to use the <code>@</code> operator, followed by the snapshot you wish to read from:</p>

<pre><code>SELECT level, message
FROM iceberg.logging.“events@2115743741823353537”;
</code></pre>

<p>Result:</p>

<table>
<thead>
<tr>
<th>level</th>
<th>message</th>
</tr>
</thead>

<tbody>
<tr>
<td>ERROR</td>
<td>Double oh noes</td>
</tr>

<tr>
<td>WARN</td>
<td>Maybeh oh noes?</td>
</tr>

<tr>
<td>ERROR</td>
<td>Oh noes</td>
</tr>
</tbody>
</table>

<p> If you determine there is some issue with your data, you can always roll back to the previous state permanently as well. In Trino we have a function called <code>rollback_to_snapshot</code> to move the table state to another snapshot:</p>

<pre><code>CALL system.rollback_to_snapshot(‘logging’, ‘events’, 2115743741823353537);
</code></pre>

<p>Now that we have rolled back, observe what happens when we query the events
table with:</p>

<pre><code>SELECT level, message
FROM iceberg.logging.events;
</code></pre>

<p>Result:</p>

<table>
<thead>
<tr>
<th>level</th>
<th>message</th>
</tr>
</thead>

<tbody>
<tr>
<td>ERROR</td>
<td>Double oh noes</td>
</tr>

<tr>
<td>WARN</td>
<td>Maybeh oh noes?</td>
</tr>

<tr>
<td>ERROR</td>
<td>Oh noes</td>
</tr>
</tbody>
</table>

<p> Notice the <code>INFO</code> row is still missing even though we query the table without specifying a snapshot id. Now just because we rolled back, doesn’t mean we’ve lost the snapshot we just rolled back from. In fact, we can roll forward, or as I like to call it,  <a href="https://en.wikipedia.org/wiki/Back_to_the_Future">back to the future</a>! In Trino, you use the same function call but with a predecessor of the existing snapshot:</p>

<pre><code>CALL system.rollback_to_snapshot(‘logging’, ‘events’, 7030511368881343137)
</code></pre>

<p>And now we should be able to query the table again and see the <code>INFO</code> row
return:</p>

<pre><code>SELECT level, message
FROM iceberg.logging.events;
</code></pre>

<p>Result:</p>

<table>
<thead>
<tr>
<th>level</th>
<th>message</th>
</tr>
</thead>

<tbody>
<tr>
<td>ERROR</td>
<td>Oh noes</td>
</tr>

<tr>
<td>INFO</td>
<td>It is all good</td>
</tr>

<tr>
<td>ERROR</td>
<td>Double oh noes</td>
</tr>

<tr>
<td>WARN</td>
<td>Maybeh oh noes?</td>
</tr>
</tbody>
</table>

<p> As expected, the INFO row returns when you roll back to the future.</p>

<p> Having snapshots not only provides you with a level of immutability that is key to the eventual consistency model, but gives you a rich set of features to version and move between different versions of your data like a git repository.</p>

<p> ## Iceberg Specification</p>

<p> Perhaps saving the best for last, the benefit of using Iceberg is the community that surrounds it, and the support you receive. It can be daunting to have to choose a project that replaces something so core to your architecture. While Hive has so many drawbacks, one of the things keeping many companies locked in is the fear of the unknown. How do you know which table format to choose? Are there unknown data corruption issues that I’m about to take on? What if this doesn’t scale like it promises on the label? It is worth noting that  <a href="https://lakefs.io/hudi-iceberg-and-delta-lake-data-lake-table-formats-compared/">alternative table formats are also emerging in this space</a>  and we encourage you to investigate these for your own use cases. When sitting down with Iceberg creator, Ryan Blue,  <a href="https://www.twitch.tv/videos/989098630">comparing Iceberg to other table formats</a>,  he claims the community’s greatest strength is their ability to look forward. They intentionally broke compatibility with Hive to enable them to provide a richer level of features. Unlike Hive, the Iceberg project explained their thinking in a spec.</p>

<p> The strongest argument I can see for Iceberg is that it has a <a href="https://iceberg.apache.org/spec/">specification</a>. This is something that has largely been missing from Hive and shows a real maturity in how the Iceberg community has approached the issue. On the Trino project, we think standards are important. We adhere to many of them ourselves, such as the ANSI SQL syntax, and exposing the client through a JDBC connection. By creating a standard around this, you’re no longer tied to any particular technology, not even Iceberg itself. You are adhering to a standard that will hopefully become the de facto standard over a decade or two, much like Hive did. Having the standard in clear writing invites multiple communities to the table and brings even more use  cases. Doing so improves the standards and therefore the technologies that implement them.</p>

<p> The previous three blog posts of this series covered the features and massive benefits from using this novel table format. The following post will dive deeper and discuss more about how Iceberg achieves some of this functionality, with an overview into some of the internals and metadata layouts. In the meantime, feel free to try  <a href="https://github.com/bitsondatadev/trino-getting-started/tree/main/iceberg/trino-iceberg-minio">Trino on Ice(berg)</a>.</p>

<p><a href="https://bitsondata.dev/tag:trino" class="hashtag"><span>#</span><span class="p-category">trino</span></a> <a href="https://bitsondata.dev/tag:iceberg" class="hashtag"><span>#</span><span class="p-category">iceberg</span></a></p>

<p><em>bits</em></p>


]]></content:encoded>
      <guid>https://bitsondata.dev/trino-iceberg-iii-concurrency-snapshots-spec</guid>
      <pubDate>Fri, 30 Jul 2021 05:00:00 +0000</pubDate>
    </item>
    <item>
      <title>Trino on ice II: In-place table evolution and cloud compatibility with Iceberg</title>
      <link>https://bitsondata.dev/trino-iceberg-ii-table-evolution-cloud?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[&#xA;&#xA;The first post covered how Iceberg is a table format and not a file format It demonstrated the benefits of hidden partitioning in Iceberg in contrast to exposed partitioning in Hive. There really is no such thing as “exposed partitioning.” I just thought that sounded better than not-hidden partitioning. If any of that wasn’t clear, I recommend either that you stop reading now, or go back to the first post before starting this one. This post discusses evolution. No, the post isn’t covering Darwinian nor Pokémon evolution, but in-place table evolution! &#xA;&#xA;!--more--&#xA;&#xA;---&#xA;&#xA;Trino on ice is a series, covering the details around how the Iceberg table format works with the Trino query engine. It’s recommended to read the posts sequentially as the examples build on previous posts in this series:&#xA;&#xA;Trino on ice I: A gentle introduction to Iceberg&#xA;Trino on ice II: In-place table evolution and cloud compatibility with Iceberg&#xA;Trino on ice III: Iceberg concurrency model, snapshots, and the Iceberg spec&#xA;Trino on ice IV: Deep dive into Iceberg internals&#xA;&#xA;---&#xA;&#xA;You may find it a little odd that I am getting excited over tables evolving &#xA;in-place, but as mentioned in the last post, if you have experience performing table evolution in Hive, you’d be as happy as Ash Ketchum when Charmander evolved into Charmeleon discovering that Iceberg supports Partition evolution and schema evolution. That is, until Charmeleon started treating Ash like a jerk after the evolution from Charmander. Hopefully, you won’t face the same issue when your tables evolve. &#xA;&#xA;Another important aspect that is covered, is how Iceberg is developed with cloud storage in mind. Hive and other data lake technologies were developed with file systems as their primary storage layer. This is still a very common layer today, but as more companies move to include object storage, table formats did not adapt to the needs of object stores. Let’s dive in!&#xA;&#xA;Partition Specification evolution&#xA;&#xA;In Iceberg, you are able to update the partition specification, shortened to partition spec in Iceberg, on a live table. You do not need to perform a table migration as you do in Hive. In Hive, partition specs don’t explicitly exist because they are tightly coupled with the creation of the Hive table. Meaning, if you ever need to change the granularity of your data partitions at any point, you need to create an entirely new table, and move all the data to the new partition granularity you desire. No pressure on choosing the right granularity or anything!&#xA;&#xA;In Iceberg, you’re not required to choose the perfect partition specification upfront, and you can have multiple partition specs in the same table, and query across the different sized partition specs. How great is that! This means, if you’re initially partitioning your data by month, and later you decide to move to a daily partitioning spec due to a growing ingest from all your new customers, you can do so with no migration, and query over the table with no issue. &#xA;&#xA;This is conveyed pretty succinctly in this graphic from the Iceberg &#xA;documentation. At the end of the year 2008, partitioning occurs at a monthly granularity and after 2009, it moves to a daily granularity. When the query to pull data from December 14th, 2008 and January 13th, 2009, the entire month of December gets scanned due to the monthly partition, but for the dates in January, only the first 13 days are scanned to answer the query.&#xA;&#xA;At the time of writing, Trino is able to perform reads from tables that have multiple partition spec changes but partition evolution write support does not yet exist. There are efforts to add this support in the near future. Edit: this has since been merged!&#xA;&#xA;Schema evolution&#xA;&#xA;Iceberg also handles schema evolution much more elegantly than Hive. In Hive, adding columns worked well enough, as data inserted before the schema change just reports null for that column. For formats that use column names, like ORC and Parquet, deletes are also straightforward for Hive, as it simply ignores fields that are no longer part of the table. For unstructured files like CSV that use the position of the column, deletes would still cause issues, as deleting one column shifts the rest of the columns. Renames for schemas pose an issue for all formats in Hive as data written prior to the rename is not modified to the new field. This effectively works the same as if you deleted the old field and added a new column with the new name. This lack of support for schema. evolution across various file types in Hive requires a lot of memorizing&#xA;the formats underneath various tables. This is very susceptible to causing user errors if someone executes one of the unsupported operations on the wrong table.&#xA;&#xA;table&#xA;thead&#xA;  tr&#xA;    th colspan=&#34;4&#34;Hive 2.2.0 schema evolution based on file type and operation./th&#xA;  /tr&#xA;/thead&#xA;tbody&#xA;  tr&#xA;    td/td&#xA;    tdAdd/td&#xA;    tdDelete/td&#xA;    tdRename/td&#xA;  /tr&#xA;  tr&#xA;    tdCSV/TSV/td&#xA;    td✅/td&#xA;    td❌/td&#xA;    td❌/td&#xA;  /tr&#xA;  tr&#xA;    tdJSON/td&#xA;    td✅/td&#xA;    td✅/td&#xA;    td❌/td&#xA;  /tr&#xA;  tr&#xA;    tdORC/Parquet/Avro/td&#xA;    td✅/td&#xA;    td✅/td&#xA;    td❌/td&#xA;  /tr&#xA;/tbody&#xA;/table&#xA;&#xA;Currently in Iceberg, schemaless position-based data formats such as CSV and TSVare not supported, though there are some discussions on adding limited support for them. This would be good from a reading standpoint, to load data from the CSV, into an Iceberg format with all the guarantees that Iceberg offers. &#xA;&#xA;While JSON doesn’t rely on positional data, it does have an explicit dependency on names. This means, that if I remove a text column from a JSON table named severity, then later I want to add a new int column called severity, I encounter an error when I try to read in the data with the string type from before when I try to deserialize the JSON files. Even worse would be if the new severity column you add has the same type as the original but a semantically different meaning. This results in old rows containing values that are unknowingly from a different domain, which can lead to wrong analytics. After all, someone who adds the new severity column might not even be aware of the old severity column, if it was quite some time ago when it was dropped.&#xA;&#xA;ORC, Parquet, and Avro do not suffer from these issues as they are columnar formats that keep a schema internal to the file itself, and each format tracks changes to the columns through IDs rather than name values or position. Iceberg uses these unique column IDs to also keep track of the columns as changes are applied.&#xA;&#xA;In general, Iceberg can only allow this small set of file formats due to the correctness guarantees it provides. In Trino, you can add, delete, or rename columns using the ALTER TABLE command. Here’s an example that continues from the table created  in the last post  that inserted three rows. The DDL statement looked like this.&#xA;&#xA;CREATE TABLE iceberg.logging.events (&#xA;  level VARCHAR,&#xA;  eventtime TIMESTAMP(6), &#xA;  message VARCHAR,&#xA;  callstack ARRAY(VARCHAR)&#xA;) WITH (&#xA;  format = &#39;ORC&#39;,&#xA;  partitioning = ARRAY[&#39;day(eventtime)&#39;]&#xA;);&#xA;&#xA;Here is an ALTER TABLE sequence that adds a new column named severity, inserts data including into the new column, renames the column, and prints the data.&#xA;&#xA;ALTER TABLE iceberg.logging.events ADD COLUMN severity INTEGER; &#xA;&#xA;INSERT INTO iceberg.logging.events VALUES &#xA;(&#xA;  &#39;INFO&#39;, &#xA;  timestamp &#xA;  &#39;2021-04-01 19:59:59.999999&#39; AT TIME ZONE &#39;America/LosAngeles&#39;, &#xA;  &#39;es muy bueno&#39;, &#xA;  ARRAY [&#39;It is all normal&#39;], &#xA;  1&#xA;);&#xA;&#xA;ALTER TABLE iceberg.logging.events RENAME COLUMN severity TO priority;&#xA;&#xA;SELECT level, message, priority&#xA;FROM iceberg.logging.events;&#xA;&#xA;Result:&#xA;&#xA;| level |  message | priority |&#xA;| --- | --- | --- |&#xA;| ERROR | Double oh noes | NULL |&#xA;| WARN | Maybeh oh noes? | NULL |&#xA;| ERROR | Oh noes | NULL |&#xA;| INFO | es muy bueno | 1 |&#xA;&#xA;ALTER TABLE iceberg.logging.events &#xA;DROP COLUMN priority;&#xA;&#xA;SHOW CREATE TABLE iceberg.logging.events;&#xA;&#xA;Result&#xA;&#xA;CREATE TABLE iceberg.logging.events (&#xA;   level varchar,&#xA;   eventtime timestamp(6),&#xA;   message varchar,&#xA;   callstack array(varchar)&#xA;)&#xA;WITH (&#xA;   format = &#39;ORC&#39;,&#xA;   partitioning = ARRAY[&#39;day(eventtime)&#39;]&#xA;)&#xA;&#xA;Notice how the priority and severity columns are both not present in the schema. As noted in the table above, Hive renames cause issues for all file formats. Yet in Iceberg, performing all these operations causes no issues with the table and underlying data.&#xA;&#xA;Cloud storage compatibility&#xA;&#xA;Not all developers consider or are aware of the performance implications of using Hive over a cloud object storage solution like S3 or Azure Blob storage. One thing to remember is that Hive was developed with the Hadoop Distributed File System (HDFS) in mind. HDFS is a filesystem and is particularly well suited to handle listing files on the filesystem, because they were stored in a contiguous manner. When Hive stores data associated with a table, it assumes there is a contiguous layout underneath it and performs list operations that are expensive on cloud storage systems.&#xA;&#xA;The common cloud storage systems are typically object stores that do not lay out the files in a contiguous manner based on paths. Therefore, it becomes very expensive to list out all the files in a particular path. Yet, these list operations are executed for every partition that could be included in a query, regardless of only a single row, in a single file out of thousands of files needing to be retrieved to answer the query. Even ignoring the performance costs for a minute, object stores may also pose issues for Hive due to eventual  consistency. Inserting and deleting can cause inconsistent results for readers, if the files you end up reading are out of date. &#xA;&#xA;Iceberg avoids all of these issues by tracking the data at the file level, &#xA;rather than the partition level. By tracking the files, Iceberg only accesses the files containing data relevant to the query, as opposed to accessing files in the same partition looking for the few files that are relevant to the query. Further, this allows Iceberg to control for the inconsistency issue in cloud-based file systems by using a locking mechanism at the file level. See the file layout below that Hive layout versus the Iceberg layout. As you can see in the next image, Iceberg makes no assumptions about the data being contiguous or not. It simply builds a persistent tree using the snapshot (S) location stored in the metadata, that points to the manifest list (ML), which points to &#xA;manifests containing partitions (P). Finally, these manifest files contain the file (F) locations and stats that can quickly be used to prune data versus &#xA;needing to do a list operation and scanning all the files.&#xA;&#xA;Referencing the picture above, if you were to run a query where the result set only contains rows from file F1, Hive would require a list operation and scanning the files, F2 and F3. In Iceberg, file metadata exists in the manifest file, P1, that would have a range on the predicate field that prunes out files F2 and F3, and only scans file F1. This example only shows a couple of files, but imagine storage that scales up to thousands of files! Listing becomes expensive on files that are not contiguously stored in memory. Having this flexibility in the logical layout is essential to increase query performance. This is especially true on cloud object stores.&#xA;&#xA;If you want to play around with Iceberg using Trino, check out the &#xA;Trino Iceberg docs. To avoid issues like the eventual consistency issue, as well as other problems of trying to sync operations across systems, Iceberg provides optimistic concurrency support, which is covered in more detail in&#xA;the next post. &#xA;&#xA;#trino #iceberg&#xA;&#xA;bits_&#xA;&#xA;!--emailsub--]]&gt;</description>
      <content:encoded><![CDATA[<p><img src="https://trino.io/assets/blog/trino-on-ice/trino-iceberg.png" alt=""/></p>

<p><a href="https://bitsondata.dev/trino-on-ice-i-a-gentle-introduction-to-iceberg">The first post</a> covered how Iceberg is a table format and not a file format It demonstrated the benefits of hidden partitioning in Iceberg in contrast to exposed partitioning in Hive. There really is no such thing as “exposed partitioning.” I just thought that sounded better than not-hidden partitioning. If any of that wasn’t clear, I recommend either that you stop reading now, or go back to the first post before starting this one. This post discusses evolution. No, the post isn’t covering Darwinian nor Pokémon evolution, but in-place table evolution!</p>



<hr/>

<p>Trino on ice is a series, covering the details around how the Iceberg table format works with the Trino query engine. It’s recommended to read the posts sequentially as the examples build on previous posts in this series:</p>
<ul><li><a href="https://bitsondata.dev/trino-iceberg-i-gentle-intro">Trino on ice I: A gentle introduction to Iceberg</a></li>
<li><a href="https://bitsondata.dev/trino-iceberg-ii-table-evolution-cloud">Trino on ice II: In-place table evolution and cloud compatibility with Iceberg</a></li>
<li><a href="https://write.as/bitsondatadev/trino-iceberg-iii-concurrency-snapshots-spec">Trino on ice III: Iceberg concurrency model, snapshots, and the Iceberg spec</a></li>
<li><a href="https://bitsondata.dev/trino-iceberg-iv-deep-dive">Trino on ice IV: Deep dive into Iceberg internals</a></li></ul>

<hr/>

<p><img src="https://trino.io/assets/blog/trino-on-ice/evolution.gif" alt=""/></p>

<p>You may find it a little odd that I am getting excited over tables evolving
in-place, but as mentioned in the last post, if you have experience performing table evolution in Hive, you’d be as happy as Ash Ketchum when Charmander evolved into Charmeleon discovering that Iceberg supports Partition evolution and schema evolution. That is, until Charmeleon started treating Ash like a jerk after the evolution from Charmander. Hopefully, you won’t face the same issue when your tables evolve.</p>

<p>Another important aspect that is covered, is how Iceberg is developed with cloud storage in mind. Hive and other data lake technologies were developed with file systems as their primary storage layer. This is still a very common layer today, but as more companies move to include object storage, table formats did not adapt to the needs of object stores. Let’s dive in!</p>

<h2 id="partition-specification-evolution" id="partition-specification-evolution">Partition Specification evolution</h2>

<p>In Iceberg, you are able to update the partition specification, shortened to partition spec in Iceberg, on a live table. You do not need to perform a table migration as you do in Hive. In Hive, partition specs don’t explicitly exist because they are tightly coupled with the creation of the Hive table. Meaning, if you ever need to change the granularity of your data partitions at any point, you need to create an entirely new table, and move all the data to the new partition granularity you desire. No pressure on choosing the right granularity or anything!</p>

<p>In Iceberg, you’re not required to choose the perfect partition specification upfront, and you can have multiple partition specs in the same table, and query across the different sized partition specs. How great is that! This means, if you’re initially partitioning your data by month, and later you decide to move to a daily partitioning spec due to a growing ingest from all your new customers, you can do so with no migration, and query over the table with no issue.</p>

<p>This is conveyed pretty succinctly in this graphic from the Iceberg
documentation. At the end of the year 2008, partitioning occurs at a monthly granularity and after 2009, it moves to a daily granularity. When the query to pull data from December 14th, 2008 and January 13th, 2009, the entire month of December gets scanned due to the monthly partition, but for the dates in January, only the first 13 days are scanned to answer the query.</p>

<p><img src="https://trino.io/assets/blog/trino-on-ice/partition-spec-evolution.png" alt=""/></p>

<p>At the time of writing, Trino is able to perform reads from tables that have multiple partition spec changes but partition evolution write support does not yet exist. <a href="https://github.com/trinodb/trino/issues/7580">There are efforts to add this support in the near future</a>. Edit: this has since been merged!</p>

<h2 id="schema-evolution" id="schema-evolution">Schema evolution</h2>

<p>Iceberg also handles schema evolution much more elegantly than Hive. In Hive, adding columns worked well enough, as data inserted before the schema change just reports null for that column. For formats that use column names, like ORC and Parquet, deletes are also straightforward for Hive, as it simply ignores fields that are no longer part of the table. For unstructured files like CSV that use the position of the column, deletes would still cause issues, as deleting one column shifts the rest of the columns. Renames for schemas pose an issue for all formats in Hive as data written prior to the rename is not modified to the new field. This effectively works the same as if you deleted the old field and added a new column with the new name. This lack of support for schema. evolution across various file types in Hive requires a lot of memorizing
the formats underneath various tables. This is very susceptible to causing user errors if someone executes one of the unsupported operations on the wrong table.</p>

<table>
<thead>
  <tr>
    <th colspan="4">Hive 2.2.0 schema evolution based on file type and operation.</th>
  </tr>
</thead>
<tbody>
  <tr>
    <td></td>
    <td>Add</td>
    <td>Delete</td>
    <td>Rename</td>
  </tr>
  <tr>
    <td>CSV/TSV</td>
    <td>✅</td>
    <td>❌</td>
    <td>❌</td>
  </tr>
  <tr>
    <td>JSON</td>
    <td>✅</td>
    <td>✅</td>
    <td>❌</td>
  </tr>
  <tr>
    <td>ORC/Parquet/Avro</td>
    <td>✅</td>
    <td>✅</td>
    <td>❌</td>
  </tr>
</tbody>
</table>

<p>Currently in Iceberg, schemaless position-based data formats such as CSV and TSVare not supported, though there are <a href="https://github.com/apache/iceberg/issues/118">some discussions on adding limited support for them</a>. This would be good from a reading standpoint, to load data from the CSV, into an Iceberg format with all the guarantees that Iceberg offers.</p>

<p>While JSON doesn’t rely on positional data, it does have an explicit dependency on names. This means, that if I remove a text column from a JSON table named <code>severity</code>, then later I want to add a new int column called <code>severity</code>, I encounter an error when I try to read in the data with the string type from before when I try to deserialize the JSON files. Even worse would be if the new <code>severity</code> column you add has the same type as the original but a semantically different meaning. This results in old rows containing values that are unknowingly from a different domain, which can lead to wrong analytics. After all, someone who adds the new <code>severity</code> column might not even be aware of the old <code>severity</code> column, if it was quite some time ago when it was dropped.</p>

<p>ORC, Parquet, and Avro do not suffer from these issues as they are columnar formats that keep a schema internal to the file itself, and each format tracks changes to the columns through IDs rather than name values or position. Iceberg uses these unique column IDs to also keep track of the columns as changes are applied.</p>

<p>In general, Iceberg can only allow this small set of file formats due to the <a href="https://iceberg.apache.org/evolution/#correctness">correctness guarantees</a> it provides. In Trino, you can add, delete, or rename columns using the <code>ALTER TABLE</code> command. Here’s an example that continues from the table created  in the last post  that inserted three rows. The DDL statement looked like this.</p>

<pre><code>CREATE TABLE iceberg.logging.events (
  level VARCHAR,
  event_time TIMESTAMP(6), 
  message VARCHAR,
  call_stack ARRAY(VARCHAR)
) WITH (
  format = &#39;ORC&#39;,
  partitioning = ARRAY[&#39;day(event_time)&#39;]
);
</code></pre>

<p>Here is an <code>ALTER TABLE</code> sequence that adds a new column named <code>severity</code>, inserts data including into the new column, renames the column, and prints the data.</p>

<pre><code>ALTER TABLE iceberg.logging.events ADD COLUMN severity INTEGER; 

INSERT INTO iceberg.logging.events VALUES 
(
  &#39;INFO&#39;, 
  timestamp 
  &#39;2021-04-01 19:59:59.999999&#39; AT TIME ZONE &#39;America/Los_Angeles&#39;, 
  &#39;es muy bueno&#39;, 
  ARRAY [&#39;It is all normal&#39;], 
  1
);

ALTER TABLE iceberg.logging.events RENAME COLUMN severity TO priority;

SELECT level, message, priority
FROM iceberg.logging.events;
</code></pre>

<p>Result:</p>

<table>
<thead>
<tr>
<th>level</th>
<th>message</th>
<th>priority</th>
</tr>
</thead>

<tbody>
<tr>
<td>ERROR</td>
<td>Double oh noes</td>
<td>NULL</td>
</tr>

<tr>
<td>WARN</td>
<td>Maybeh oh noes?</td>
<td>NULL</td>
</tr>

<tr>
<td>ERROR</td>
<td>Oh noes</td>
<td>NULL</td>
</tr>

<tr>
<td>INFO</td>
<td>es muy bueno</td>
<td>1</td>
</tr>
</tbody>
</table>

<pre><code>ALTER TABLE iceberg.logging.events 
DROP COLUMN priority;

SHOW CREATE TABLE iceberg.logging.events;
</code></pre>

<p>Result</p>

<pre><code>CREATE TABLE iceberg.logging.events (
   level varchar,
   event_time timestamp(6),
   message varchar,
   call_stack array(varchar)
)
WITH (
   format = &#39;ORC&#39;,
   partitioning = ARRAY[&#39;day(event_time)&#39;]
)
</code></pre>

<p>Notice how the priority and severity columns are both not present in the schema. As noted in the table above, Hive renames cause issues for all file formats. Yet in Iceberg, performing all these operations causes no issues with the table and underlying data.</p>

<h2 id="cloud-storage-compatibility" id="cloud-storage-compatibility">Cloud storage compatibility</h2>

<p>Not all developers consider or are aware of the performance implications of using Hive over a cloud object storage solution like S3 or Azure Blob storage. One thing to remember is that Hive was developed with the Hadoop Distributed File System (HDFS) in mind. HDFS is a filesystem and is particularly well suited to handle listing files on the filesystem, because they were stored in a contiguous manner. When Hive stores data associated with a table, it assumes there is a contiguous layout underneath it and performs list operations that are expensive on cloud storage systems.</p>

<p>The common cloud storage systems are typically object stores that do not lay out the files in a contiguous manner based on paths. Therefore, it becomes very expensive to list out all the files in a particular path. Yet, these list operations are executed for every partition that could be included in a query, regardless of only a single row, in a single file out of thousands of files needing to be retrieved to answer the query. Even ignoring the performance costs for a minute, object stores may also pose issues for Hive due to eventual  consistency. Inserting and deleting can cause inconsistent results for readers, if the files you end up reading are out of date.</p>

<p>Iceberg avoids all of these issues by tracking the data at the file level,
rather than the partition level. By tracking the files, Iceberg only accesses the files containing data relevant to the query, as opposed to accessing files in the same partition looking for the few files that are relevant to the query. Further, this allows Iceberg to control for the inconsistency issue in cloud-based file systems by using a locking mechanism at the file level. See the file layout below that Hive layout versus the Iceberg layout. As you can see in the next image, Iceberg makes no assumptions about the data being contiguous or not. It simply builds a persistent tree using the snapshot (S) location stored in the metadata, that points to the manifest list (ML), which points to
manifests containing partitions (P). Finally, these manifest files contain the file (F) locations and stats that can quickly be used to prune data versus
needing to do a list operation and scanning all the files.</p>

<p><img src="https://trino.io/assets/blog/trino-on-ice/cloud-file-layout.png" alt=""/></p>

<p>Referencing the picture above, if you were to run a query where the result set only contains rows from file F1, Hive would require a list operation and scanning the files, F2 and F3. In Iceberg, file metadata exists in the manifest file, P1, that would have a range on the predicate field that prunes out files F2 and F3, and only scans file F1. This example only shows a couple of files, but imagine storage that scales up to thousands of files! Listing becomes expensive on files that are not contiguously stored in memory. Having this flexibility in the logical layout is essential to increase query performance. This is especially true on cloud object stores.</p>

<p>If you want to play around with Iceberg using Trino, check out the
<a href="https://trino.io/docs/current/connector/iceberg.html">Trino Iceberg docs</a>. To avoid issues like the eventual consistency issue, as well as other problems of trying to sync operations across systems, Iceberg provides optimistic concurrency support, which is covered in more detail in
<a href="https://bitsondata.dev/iceberg-concurrency-snapshots-spec">the next post</a>.</p>

<p><a href="https://bitsondata.dev/tag:trino" class="hashtag"><span>#</span><span class="p-category">trino</span></a> <a href="https://bitsondata.dev/tag:iceberg" class="hashtag"><span>#</span><span class="p-category">iceberg</span></a></p>

<p><em>bits</em></p>


]]></content:encoded>
      <guid>https://bitsondata.dev/trino-iceberg-ii-table-evolution-cloud</guid>
      <pubDate>Mon, 12 Jul 2021 05:00:00 +0000</pubDate>
    </item>
    <item>
      <title>Trino on ice I: A gentle introduction To Iceberg</title>
      <link>https://bitsondata.dev/trino-iceberg-i-gentle-intro?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[&#xA;&#xA;Back in the Gentle introduction to the Hive connector blog post, I discussed a commonly misunderstood architecture and uses of the Trino Hive connector. In short, while some may think the name indicates Trino makes a call to a running Hive instance, the Hive connector does not use the Hive runtime to answer queries. Instead, the connector is named Hive connector because it relies on Hive conventions and implementation details from the Hadoop ecosystem - the invisible Hive specification.&#xA;&#xA;!--more--&#xA;&#xA;---&#xA;&#xA;Trino on ice is a series, covering the details around how the Iceberg table format works with the Trino query engine. It’s recommended to read the posts sequentially as the examples build on previous posts in this series:&#xA;&#xA;Trino on ice I: A gentle introduction to Iceberg&#xA;Trino on ice II: In-place table evolution and cloud compatibility with Iceberg&#xA;Trino on ice III: Iceberg concurrency model, snapshots, and the Iceberg spec&#xA;Trino on ice IV: Deep dive into Iceberg internals&#xA;&#xA;---&#xA;&#xA;I call this specification invisible because it doesn’t exist. It lives in the Hive code and the minds of those who developed it. This is makes it very difficult for anybody else who has to integrate with any distributed object storage that uses Hive, since they had to rely on reverse engineering and keeping up with the changes. The way you interact with Hive changes based on which version of Hive or Hadoop you are running. It also varies if you are in the cloud or over an object store. Spark has even modified the Hive spec in some ways to fit the Hive model to their use cases. It’s a big mess that data engineers have put up with for years. Yet despite the confusion and lack of organization due to Hive’s number of unwritten assumptions, the Hive connector is the most popular connector in use for Trino. Virtually every big data query engine uses the Hive model today in some form. As a result it is used by numerous companies to store and access data in their data lakes.&#xA;&#xA;So how did something with no specification become so ubiquitous in data lakes? Hive was first in the large object storage and big data world as part of Hadoop. Hadoop became popular from good marketing for Hadoop to solve the problems of dealing with the increase in data with the Web 2.0 boom . Of course, Hive didn’t get everything wrong. In fact, without Hive, and the fact that it is open source, there may not have been a unified specification at all. Despite the many hours data engineers have spent bashing their heads against the wall with all the unintended consequences of Hive, it still served a very useful purpose.&#xA;&#xA;So why did I just rant about Hive for so long if I’m here to tell you about Apache Iceberg? It’s impossible for a teenager growing up today to truly appreciate music streaming services without knowing what it was like to have an iPod with limited storage, or listening to a scratched burnt CD that skips, or flipping your tape or record to side-B. The same way anyone born before the turn of the millennium really appreciates streaming services, so you too will appreciate Iceberg once you’ve learned the intricacies of managing a data lake built on Hive and Hadoop.&#xA;&#xA;If you haven’t used Hive before, this blog post outlines just a few pain points that come from this data warehousing software to give you proper context. If you have already lived through these headaches, this post acts as a guide to Iceberg from Hive. This post is the first in a series of blog posts discussing Apache Iceberg in great detail, through the lens of the Trino query engine user. If you’re not aware of Trino (formerly PrestoSQL) yet, it is the project that houses the founding Presto community after the founders of Presto left Facebook. This and the next couple of posts discuss the Iceberg specification and all the features Iceberg has to offer, many times in comparison with Hive.&#xA;&#xA;Before jumping into the comparisons, what is Iceberg exactly? The first thing to understand is that Iceberg is not a file format, but a table format. It may not be clear what this means by just stating that, but the function of a table format becomes clearer as the improvements Iceberg brings from the Hive table standard materialize. Iceberg doesn’t replace file formats like ORC and Parquet, but is the layer between the query engine and the data. Iceberg maps and indexes the files in order to provide a higher level abstraction that handles the relational table format for data lakes. You will understand more about table formats through examples in this series.&#xA;&#xA;Hidden Partitions&#xA;&#xA;Hive Partitions&#xA;&#xA;Since most developers and users interact with the table format via the query language, a noticeable difference is the flexibility you have while creating a partitioned table. Assume you are trying to create a table for tracking events occurring in our system. You run both sets of SQL commands from Trino, just using the Hive and Iceberg connectors which are designated by the catalog name (i.e. the catalog name starting with hive. uses the Hive connector, while the iceberg. table uses the Iceberg connector). To begin with, the first DDL statement attempts to create an events table in the logging schema in the hive catalog, which is configured to use the Hive connector. Trino also creates a partition on the events table using the eventtime field which is a TIMESTAMP field.&#xA;&#xA;CREATE TABLE hive.logging.events (&#xA;  level VARCHAR,&#xA;  eventtime TIMESTAMP,&#xA;  message VARCHAR,&#xA;  callstack ARRAY(VARCHAR)&#xA;) WITH (&#xA;  format = &#39;ORC&#39;,&#xA;  partitionedby = ARRAY[&#39;eventtime&#39;]&#xA;);&#xA;&#xA;Running this in Trino using the Hive connector produces the following error message.&#xA;&#xA;Partition keys must be the last columns in the table and in the same order as the table properties: [eventtime]&#xA;&#xA;The Hive DDL is very dependent on ordering for columns and specifically partition columns. Partition fields must be located in the final column positions and in the order of partitioning in the DDL statement. The next statement attempts to create the same table, but now with the eventtime field moved to the last column position.&#xA;&#xA;CREATE TABLE hive.logging.events (&#xA;  level VARCHAR,&#xA;  message VARCHAR,&#xA;  callstack ARRAY(VARCHAR),&#xA;  eventtime TIMESTAMP&#xA;) WITH (&#xA;  format = &#39;ORC&#39;,&#xA;  partitionedby = ARRAY[&#39;eventtime&#39;]&#xA;);&#xA;&#xA;This time, the DDL command works successfully, but you likely don’t want to partition your data on the plain timestamp. This results in a separate file for each distinct timestamp value in your table (likely almost a file for each event). In Hive, there’s no way to indicate the time granularity at which you want to partition natively. The method to support this scenario with Hive is to create a new VARCHAR column, eventtimeday that is dependent on the eventtime column to create the date partition value.&#xA;&#xA;CREATE TABLE hive.logging.events (&#xA;  level VARCHAR,&#xA;  eventtime TIMESTAMP,&#xA;  message VARCHAR,&#xA;  callstack ARRAY(VARCHAR),&#xA;  eventtimeday VARCHAR&#xA;) WITH (&#xA;  format = &#39;ORC&#39;,&#xA;  partitionedby = ARRAY[&#39;eventtimeday&#39;]&#xA;);&#xA;&#xA;This method wastes space by adding a new column to your table. Even worse, it puts the burden of knowledge on the user to include this new column for writing data. It is then necessary to use that separate column for any read access to take advantage of the performance gains from the partitioning.&#xA;&#xA;INSERT INTO hive.logging.events&#xA;VALUES&#xA;(&#xA;  &#39;ERROR&#39;,&#xA;  timestamp &#39;2021-04-01 12:00:00.000001&#39;,&#xA;  &#39;Oh noes&#39;, &#xA;  ARRAY [&#39;Exception in thread &#34;main&#34; java.lang.NullPointerException&#39;], &#xA;  &#39;2021-04-01&#39;&#xA;),&#xA;(&#xA;  &#39;ERROR&#39;,&#xA;  timestamp &#39;2021-04-02 15:55:55.555555&#39;,&#xA;  &#39;Double oh noes&#39;,&#xA;  ARRAY [&#39;Exception in thread &#34;main&#34; java.lang.NullPointerException&#39;],&#xA;  &#39;2021-04-02&#39;&#xA;),&#xA;(&#xA;  &#39;WARN&#39;, &#xA;  timestamp &#39;2021-04-02 00:00:11.1122222&#39;,&#xA;  &#39;Maybeh oh noes?&#39;,&#xA;  ARRAY [&#39;Bad things could be happening??&#39;], &#xA;  &#39;2021-04-02&#39;&#xA;);&#xA;&#xA;Notice that the last partition value &#39;2021-04-01&#39; has to match the TIMESTAMP date during insertion. There is no validation in Hive to make sure this is happening because it only requires a VARCHAR and knows to partition based on different values.&#xA;&#xA;On the other hand, If a user runs the following query:&#xA;&#xA;SELECT &#xA;FROM hive.logging.events&#xA;WHERE eventtime &lt; timestamp &#39;2021-04-02&#39;;&#xA;&#xA;they get the correct results back, but have to scan all the data in the table:&#xA;&#xA;table&#xA;trthlevel/ththeventtime/ththmessage/ththcallstack/th/tr&#xA;trtdERROR/tdtd2021-04-01 12:00:00/tdtdOh noes/tdtdException in thread &#34;main&#34; java.lang.NullPointerException/td/tr&#xA;/table&#xA;&#xA;This happens because the user forgot to include the eventtimeday &lt; &#39;2021-04-02&#39; predicate in the WHERE clause. This eliminates all the benefits that led us to create the partition in the first place and yet frequently this is missed by the users of these tables.&#xA;&#xA;SELECT &#xA;FROM hive.logging.events&#xA;WHERE eventtime &lt; timestamp &#39;2021-04-02&#39; &#xA;AND eventtimeday &lt; &#39;2021-04-02&#39;;&#xA;&#xA;Result:&#xA;&#xA;table&#xA;trthlevel/ththeventtime/ththmessage/ththcallstack/th/tr&#xA;trtdERROR/tdtd2021-04-01 12:00:00/tdtdOh noes/tdtdException in thread &#34;main&#34; java.lang.NullPointerException/td/tr&#xA;/table&#xA;&#xA;Iceberg Partitions&#xA;&#xA;The following DDL statement illustrates how these issues are handled in Iceberg via the Trino Iceberg connector.&#xA;&#xA;CREATE TABLE iceberg.logging.events (&#xA;  level VARCHAR,&#xA;  eventtime TIMESTAMP(6),&#xA;  message VARCHAR,&#xA;  callstack ARRAY(VARCHAR)&#xA;) WITH (&#xA;  partitioning = ARRAY[&#39;day(eventtime)&#39;]&#xA;);&#xA;&#xA;Taking note of a few things. First, notice the partition on the eventtime column that is defined without having to move it to the last position. There is also no need to create a separate field to handle the daily partition on the eventtime field. The partition specification is maintained internally by Iceberg, and neither the user nor the reader of this table needs to know anything about the partition specification to take advantage of it. This concept is called hidden partitioning , where only the table creator/maintainer has to know the partitioning specification. Here is what the insert statements look like now:&#xA;&#xA;INSERT INTO iceberg.logging.events&#xA;VALUES&#xA;(&#xA;  &#39;ERROR&#39;,&#xA;  timestamp &#39;2021-04-01 12:00:00.000001&#39;,&#xA;  &#39;Oh noes&#39;, &#xA;  ARRAY [&#39;Exception in thread &#34;main&#34; java.lang.NullPointerException&#39;]&#xA;),&#xA;(&#xA;  &#39;ERROR&#39;,&#xA;  timestamp &#39;2021-04-02 15:55:55.555555&#39;,&#xA;  &#39;Double oh noes&#39;,&#xA;  ARRAY [&#39;Exception in thread &#34;main&#34; java.lang.NullPointerException&#39;]),&#xA;(&#xA;  &#39;WARN&#39;, &#xA;  timestamp &#39;2021-04-02 00:00:11.1122222&#39;,&#xA;  &#39;Maybeh oh noes?&#39;,&#xA;  ARRAY [&#39;Bad things could be happening??&#39;]&#xA;);&#xA;&#xA;The VARCHAR dates are no longer needed. The eventtime field is internally converted to the proper partition value to partition each row. Also, notice that the same query that ran in Hive returns the same results. The big difference is that it doesn’t require any extra clause to indicate to filter partition as well as filter the results.&#xA;&#xA;SELECT *&#xA;FROM iceberg.logging.events&#xA;WHERE eventtime &lt; timestamp &#39;2021-04-02&#39;;&#xA;&#xA;Result:&#xA;&#xA;table&#xA;trthlevel/ththeventtime/ththmessage/ththcallstack/th/tr&#xA;trtdERROR/tdtd2021-04-01 12:00:00/tdtdOh noes/tdtdException in thread &#34;main&#34; java.lang.NullPointerException/td/tr&#xA;/table&#xA;&#xA;So hopefully that gives you a glimpse into what a table format and specification are, and why Iceberg is such a wonderful improvement over the existing and outdated method of storing your data in your data lake. While this post covers a lot of aspects of Iceberg’s capabilities, this is just the tip of the Iceberg…&#xA;&#xA;If you want to play around with Iceberg using Trino, check out the Trino Iceberg docs. The next post covers how table evolution works in Iceberg, as well as, how Iceberg is an improved storage format for cloud storage.&#xA;&#xA;#trino #iceberg&#xA;&#xA;bits&#xA;&#xA;!--emailsub--]]&gt;</description>
      <content:encoded><![CDATA[<p><img src="https://trino.io/assets/blog/trino-on-ice/trino-iceberg.png" alt=""/></p>

<p>Back in the <a href="https://trino.io/blog/2020/10/20/intro-to-hive-connector">Gentle introduction to the Hive connector</a> blog post, I discussed a commonly misunderstood architecture and uses of the Trino Hive connector. In short, while some may think the name indicates Trino makes a call to a running Hive instance, the Hive connector does not use the Hive runtime to answer queries. Instead, the connector is named Hive connector because it relies on Hive conventions and implementation details from the Hadoop ecosystem – the invisible Hive specification.</p>



<hr/>

<p>Trino on ice is a series, covering the details around how the Iceberg table format works with the Trino query engine. It’s recommended to read the posts sequentially as the examples build on previous posts in this series:</p>
<ul><li><a href="https://bitsondata.dev/trino-iceberg-i-gentle-intro">Trino on ice I: A gentle introduction to Iceberg</a></li>
<li><a href="https://bitsondata.dev/trino-iceberg-ii-table-evolution-cloud">Trino on ice II: In-place table evolution and cloud compatibility with Iceberg</a></li>
<li><a href="https://write.as/bitsondatadev/trino-iceberg-iii-concurrency-snapshots-spec">Trino on ice III: Iceberg concurrency model, snapshots, and the Iceberg spec</a></li>
<li><a href="https://bitsondata.dev/trino-iceberg-iv-deep-dive">Trino on ice IV: Deep dive into Iceberg internals</a></li></ul>

<hr/>

<p>I call this specification invisible because it doesn’t exist. It lives in the Hive code and the minds of those who developed it. This is makes it very difficult for anybody else who has to integrate with any distributed object storage that uses Hive, since they had to rely on reverse engineering and keeping up with the changes. The way you interact with Hive changes based on <a href="https://medium.com/hashmapinc/four-steps-for-migrating-from-hive-2-x-to-3-x-e85a8363a18">which version of Hive or Hadoop</a> you are running. It also varies if you are in the cloud or over an object store. Spark has even <a href="https://spark.apache.org/docs/2.4.4/sql-migration-guide-hive-compatibility.html">modified the Hive spec</a> in some ways to fit the Hive model to their use cases. It’s a big mess that data engineers have put up with for years. Yet despite the confusion and lack of organization due to Hive’s number of unwritten assumptions, the Hive connector is the most popular connector in use for Trino. Virtually every big data query engine uses the Hive model today in some form. As a result it is used by numerous companies to store and access data in their data lakes.</p>

<p>So how did something with no specification become so ubiquitous in data lakes? Hive was first in the large object storage and big data world as part of Hadoop. Hadoop became popular from good marketing for Hadoop to solve the problems of dealing with the increase in data with the Web 2.0 boom . Of course, Hive didn’t get everything wrong. In fact, without Hive, and the fact that it is open source, there may not have been a unified specification at all. Despite the many hours data engineers have spent bashing their heads against the wall with all the unintended consequences of Hive, it still served a very useful purpose.</p>

<p>So why did I just rant about Hive for so long if I’m here to tell you about <a href="https://iceberg.apache.org/">Apache Iceberg</a>? It’s impossible for a teenager growing up today to truly appreciate music streaming services without knowing what it was like to have an iPod with limited storage, or listening to a scratched burnt CD that skips, or flipping your tape or record to side-B. The same way anyone born before the turn of the millennium really appreciates streaming services, so you too will appreciate Iceberg once you’ve learned the intricacies of managing a data lake built on Hive and Hadoop.</p>

<p>If you haven’t used Hive before, this blog post outlines just a few pain points that come from this data warehousing software to give you proper context. If you have already lived through these headaches, this post acts as a guide to Iceberg from Hive. This post is the first in a series of blog posts discussing Apache Iceberg in great detail, through the lens of the Trino query engine user. If you’re not aware of Trino (formerly PrestoSQL) yet, it is the project that houses the founding Presto community after the <a href="https://trino.io/blog/2020/12/27/announcing-trino">founders of Presto left Facebook</a>. This and the next couple of posts discuss the Iceberg specification and all the features Iceberg has to offer, many times in comparison with Hive.</p>

<p>Before jumping into the comparisons, what is Iceberg exactly? The first thing to understand is that Iceberg is not a file format, but a table format. It may not be clear what this means by just stating that, but the function of a table format becomes clearer as the improvements Iceberg brings from the Hive table standard materialize. Iceberg doesn’t replace file formats like ORC and Parquet, but is the layer between the query engine and the data. Iceberg maps and indexes the files in order to provide a higher level abstraction that handles the relational table format for data lakes. You will understand more about table formats through examples in this series.</p>

<h2 id="hidden-partitions" id="hidden-partitions">Hidden Partitions</h2>

<h3 id="hive-partitions" id="hive-partitions">Hive Partitions</h3>

<p>Since most developers and users interact with the table format via the query language, a noticeable difference is the flexibility you have while creating a partitioned table. Assume you are trying to create a table for tracking events occurring in our system. You run both sets of SQL commands from Trino, just using the Hive and Iceberg connectors which are designated by the catalog name (i.e. the catalog name starting with <code>hive.</code> uses the Hive connector, while the <code>iceberg.</code> table uses the Iceberg connector). To begin with, the first DDL statement attempts to create an <code>events</code> table in the <code>logging</code> schema in the <code>hive</code> catalog, which is configured to use the Hive connector. Trino also creates a partition on the <code>events</code> table using the <code>event_time</code> field which is a <code>TIMESTAMP</code> field.</p>

<pre><code>CREATE TABLE hive.logging.events (
  level VARCHAR,
  event_time TIMESTAMP,
  message VARCHAR,
  call_stack ARRAY(VARCHAR)
) WITH (
  format = &#39;ORC&#39;,
  partitioned_by = ARRAY[&#39;event_time&#39;]
);
</code></pre>

<p>Running this in Trino using the Hive connector produces the following error message.</p>

<pre><code>Partition keys must be the last columns in the table and in the same order as the table properties: [event_time]
</code></pre>

<p>The Hive DDL is very dependent on ordering for columns and specifically partition columns. Partition fields must be located in the final column positions and in the order of partitioning in the DDL statement. The next statement attempts to create the same table, but now with the <code>event_time</code> field moved to the last column position.</p>

<pre><code>CREATE TABLE hive.logging.events (
  level VARCHAR,
  message VARCHAR,
  call_stack ARRAY(VARCHAR),
  event_time TIMESTAMP
) WITH (
  format = &#39;ORC&#39;,
  partitioned_by = ARRAY[&#39;event_time&#39;]
);
</code></pre>

<p>This time, the DDL command works successfully, but you likely don’t want to partition your data on the plain timestamp. This results in a separate file for each distinct timestamp value in your table (likely almost a file for each event). In Hive, there’s no way to indicate the time granularity at which you want to partition natively. The method to support this scenario with Hive is to create a new <code>VARCHAR</code> column, <code>event_time_day</code> that is dependent on the <code>event_time</code> column to create the date partition value.</p>

<pre><code>CREATE TABLE hive.logging.events (
  level VARCHAR,
  event_time TIMESTAMP,
  message VARCHAR,
  call_stack ARRAY(VARCHAR),
  event_time_day VARCHAR
) WITH (
  format = &#39;ORC&#39;,
  partitioned_by = ARRAY[&#39;event_time_day&#39;]
);
</code></pre>

<p>This method wastes space by adding a new column to your table. Even worse, it puts the burden of knowledge on the user to include this new column for writing data. It is then necessary to use that separate column for any read access to take advantage of the performance gains from the partitioning.</p>

<pre><code>INSERT INTO hive.logging.events
VALUES
(
  &#39;ERROR&#39;,
  timestamp &#39;2021-04-01 12:00:00.000001&#39;,
  &#39;Oh noes&#39;, 
  ARRAY [&#39;Exception in thread &#34;main&#34; java.lang.NullPointerException&#39;], 
  &#39;2021-04-01&#39;
),
(
  &#39;ERROR&#39;,
  timestamp &#39;2021-04-02 15:55:55.555555&#39;,
  &#39;Double oh noes&#39;,
  ARRAY [&#39;Exception in thread &#34;main&#34; java.lang.NullPointerException&#39;],
  &#39;2021-04-02&#39;
),
(
  &#39;WARN&#39;, 
  timestamp &#39;2021-04-02 00:00:11.1122222&#39;,
  &#39;Maybeh oh noes?&#39;,
  ARRAY [&#39;Bad things could be happening??&#39;], 
  &#39;2021-04-02&#39;
);
</code></pre>

<p>Notice that the last partition value <code>&#39;2021-04-01&#39;</code> has to match the <code>TIMESTAMP</code> date during insertion. There is no validation in Hive to make sure this is happening because it only requires a <code>VARCHAR</code> and knows to partition based on different values.</p>

<p>On the other hand, If a user runs the following query:</p>

<pre><code>SELECT *
FROM hive.logging.events
WHERE event_time &lt; timestamp &#39;2021-04-02&#39;;
</code></pre>

<p>they get the correct results back, but have to scan all the data in the table:</p>

<table>
<tr><th>level</th><th>event_time</th><th>message</th><th>call_stack</th></tr>
<tr><td>ERROR</td><td>2021-04-01 12:00:00</td><td>Oh noes</td><td>Exception in thread &#34;main&#34; java.lang.NullPointerException</td></tr>
</table>

<p>This happens because the user forgot to include the <code>event_time_day &lt; &#39;2021-04-02&#39;</code> predicate in the <code>WHERE</code> clause. This eliminates all the benefits that led us to create the partition in the first place and yet frequently this is missed by the users of these tables.</p>

<pre><code>SELECT *
FROM hive.logging.events
WHERE event_time &lt; timestamp &#39;2021-04-02&#39; 
AND event_time_day &lt; &#39;2021-04-02&#39;;
</code></pre>

<p>Result:</p>

<table>
<tr><th>level</th><th>event_time</th><th>message</th><th>call_stack</th></tr>
<tr><td>ERROR</td><td>2021-04-01 12:00:00</td><td>Oh noes</td><td>Exception in thread &#34;main&#34; java.lang.NullPointerException</td></tr>
</table>

<h3 id="iceberg-partitions" id="iceberg-partitions">Iceberg Partitions</h3>

<p>The following DDL statement illustrates how these issues are handled in Iceberg via the Trino Iceberg connector.</p>

<pre><code>CREATE TABLE iceberg.logging.events (
  level VARCHAR,
  event_time TIMESTAMP(6),
  message VARCHAR,
  call_stack ARRAY(VARCHAR)
) WITH (
  partitioning = ARRAY[&#39;day(event_time)&#39;]
);
</code></pre>

<p>Taking note of a few things. First, notice the partition on the <code>event_time</code> column that is defined without having to move it to the last position. There is also no need to create a separate field to handle the daily partition on the <code>event_time</code> field. The <em><strong>partition specification</strong></em> is maintained internally by Iceberg, and neither the user nor the reader of this table needs to know anything about the partition specification to take advantage of it. This concept is called <em><strong>hidden partitioning</strong></em> , where only the table creator/maintainer has to know the <em><strong>partitioning specification</strong></em>. Here is what the insert statements look like now:</p>

<pre><code>INSERT INTO iceberg.logging.events
VALUES
(
  &#39;ERROR&#39;,
  timestamp &#39;2021-04-01 12:00:00.000001&#39;,
  &#39;Oh noes&#39;, 
  ARRAY [&#39;Exception in thread &#34;main&#34; java.lang.NullPointerException&#39;]
),
(
  &#39;ERROR&#39;,
  timestamp &#39;2021-04-02 15:55:55.555555&#39;,
  &#39;Double oh noes&#39;,
  ARRAY [&#39;Exception in thread &#34;main&#34; java.lang.NullPointerException&#39;]),
(
  &#39;WARN&#39;, 
  timestamp &#39;2021-04-02 00:00:11.1122222&#39;,
  &#39;Maybeh oh noes?&#39;,
  ARRAY [&#39;Bad things could be happening??&#39;]
);
</code></pre>

<p>The <code>VARCHAR</code> dates are no longer needed. The <code>event_time</code> field is internally converted to the proper partition value to partition each row. Also, notice that the same query that ran in Hive returns the same results. The big difference is that it doesn’t require any extra clause to indicate to filter partition as well as filter the results.</p>

<pre><code>SELECT *
FROM iceberg.logging.events
WHERE event_time &lt; timestamp &#39;2021-04-02&#39;;
</code></pre>

<p>Result:</p>

<table>
<tr><th>level</th><th>event_time</th><th>message</th><th>call_stack</th></tr>
<tr><td>ERROR</td><td>2021-04-01 12:00:00</td><td>Oh noes</td><td>Exception in thread &#34;main&#34; java.lang.NullPointerException</td></tr>
</table>

<p>So hopefully that gives you a glimpse into what a table format and specification are, and why Iceberg is such a wonderful improvement over the existing and outdated method of storing your data in your data lake. While this post covers a lot of aspects of Iceberg’s capabilities, this is just the tip of the Iceberg…</p>

<p><img src="https://trino.io/assets/blog/trino-on-ice/see_myself_out.gif" alt=""/></p>

<p>If you want to play around with Iceberg using Trino, check out the <a href="https://trino.io/docs/current/connector/iceberg.html">Trino Iceberg docs</a>. The <a href="https://bitsondata.dev/in-place-table-evolution-and-cloud-compatibility-with-iceberg">next post</a> covers how table evolution works in Iceberg, as well as, how Iceberg is an improved storage format for cloud storage.</p>

<p><a href="https://bitsondata.dev/tag:trino" class="hashtag"><span>#</span><span class="p-category">trino</span></a> <a href="https://bitsondata.dev/tag:iceberg" class="hashtag"><span>#</span><span class="p-category">iceberg</span></a></p>

<p><em>bits</em></p>


]]></content:encoded>
      <guid>https://bitsondata.dev/trino-iceberg-i-gentle-intro</guid>
      <pubDate>Mon, 03 May 2021 05:00:00 +0000</pubDate>
    </item>
    <item>
      <title>A gentle introduction to the Hive connector</title>
      <link>https://bitsondata.dev/a-gentle-introduction-to-the-hive-connector?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[TL;DR: The Hive connector is what you use in Trino for reading data from object storage that is organized according to the rules laid out by Hive, without using the Hive runtime code.&#xA;&#xA;!--more--&#xA;&#xA;Originally Posted on https://trino.io/blog/2020/10/20/intro-to-hive-connector.html&#xA;&#xA;One of the most confusing aspects when starting Trino is the Hive connector. Typically, you seek out the use of Trino when you experience an intensely slow query turnaround from your existing Hadoop, Spark, or Hive infrastructure. In fact, the genesis of Trino came about due to these slow Hive query conditions at Facebook back in 2012.&#xA;&#xA;So when you learn that Trino has a Hive connector, it can be rather confusing since you moved to Trino to circumvent the slowness of your current Hive cluster. Another common source of confusion is when you want to query your data from your cloud object storage, such as AWS S3, MinIO, and Google Cloud Storage. This too uses the Hive connector. If that confuses you, don’t worry, you are not alone. This blog aims to explain this commonly confusing nomenclature.&#xA;&#xA;Hive architecture&#xA;&#xA;To understand the origins and inner workings of Trino’s Hive connector, you first need to know a few high-level components of the Hive architecture.&#xA;&#xA;You can simplify the Hive architecture to four components:&#xA;&#xA;The runtime contains the logic of the query engine that translates the SQL -esque Hive Query Language(HQL) into MapReduce jobs that run over files stored in the filesystem.&#xA;&#xA;The storage component is simply that, it stores files in various formats and index structures to recall these files. The file formats can be anything as simple as JSON and CSV, to more complex files such as columnar formats like ORC and Parquet. Traditionally, Hive runs on top of the Hadoop Distributed Filesystem (HDFS). As cloud-based options became more prevalent, object storage like Amazon S3, Azure Blob Storage, Google Cloud Storage, and others needed to be leveraged as well and replaced HDFS as the storage component.&#xA;&#xA;In order for Hive to process these files, it must have a mapping from SQL tables in the runtime to files and directories in the storage component. To accomplish this, Hive uses the Hive Metastore Service (HMS), often shortened to the metastore to manage the metadata about the files such as table columns, file locations, file formats, etc…&#xA;&#xA;The last component not included in the image is Hive’s data organization specification. The documentation of this element only exists in the code in Hive and has been reverse engineered to be used by other systems like Trino to remain compatible with other systems.&#xA;&#xA;Trino reuses all of these components except for the runtime. This is the same approach most compute engine takes when dealing with data in object stores, specifically, Trino, Spark, Drill, and Impala. When you think of the Hive connector, you should think about a connector that is capable of reading data organized by the unwritten Hive specification.&#xA;&#xA;Trino runtime replaces Hive runtime&#xA;&#xA;In the early days of big data systems, many expected query turnaround to take a long time due to the high volume of unstructured data in ETL workloads. The primary goal in early iterations of these systems was simply throughput over large volumes of data while maintaining fault-tolerance. Now, more businesses want to run fast interactive queries over their big data instead of running jobs that take hours and produce possibly undesirable results. Many companies have petabytes of data and metadata in their data warehouse. Data in storage is cumbersome to move and the data in the metastore takes a long time to repopulate in other formats. Since only the runtime that executed Hive queries needs replacement, the Trino engine utilizes the existing metastore metadata and files residing in storage, and the Trino runtime effectively replaces the Hive runtime responsible for analyzing the data.&#xA;&#xA;Trino Architecture&#xA;&#xA;The Hive connector nomenclature&#xA;&#xA;Notice, that the only change in the Trino architecture is the runtime. The HMS still exists along with the storage. This is not by accident. This design exists to address a common problem faced by many companies. It simplifies the migration from using Hive to using Trino. Regardless of the storage component used the runtime makes use of the HMS and that is the reason this connector is the Hive connector.&#xA;&#xA;Where the confusion tends to come from, is when you search for a connector from the context of the storage systems you want to query. You may not even be aware the metastore is a necessity or even exists. Typically, you look for an S3 connector, a GCS connector or a MinIO connector. All you need is the Hive connector and the HMS to manage the metadata of the objects in your storage.&#xA;&#xA;The Hive Metastore Service&#xA;&#xA;The HMS is the only Hive process used in the entire Trino ecosystem when using the Hive connector. The HMS is actually a simple service with a binary API using the Thrift protocol. This service makes updates to the metadata, stored in an RDBMS such as PostgreSQL, MySQL, or MariaDB. There are other compatible replacements of the HMS such as AWS Glue, a drop-in substitution for the HMS.&#xA;&#xA;https://github.com/bitsondatadev/trino-getting-started/tree/main/hive/trino-minio&#xA;&#xA;Getting started with the Hive Connector on Trino&#xA;&#xA;To drive this point home, I created a tutorial that showcases using Trino and looking at the metadata it produces. In the following scenario, the docker environment contains four docker containers:&#xA;&#xA;trino - the runtime in this scenario that replaces Hive.&#xA;minio - the storage is an open-source cloud object storage.&#xA;hive-metastore - the metastore service instance.&#xA;mariadb - the database that the metastore uses to store the metadata.&#xA;&#xA;You can play around with the system and optionally view the configurations. The scenario asks you to run a query to populate data in MinIO and then see the resulting metadata populated in MariaDB by the HMS. The next step asks you to run queries over the mariadb database which holds the generated metadata from the metastore.&#xA;&#xA;If you have any questions or run into any issues with the example, you can find us on slack on the #dev or #general channels.&#xA;&#xA;Have fun!&#xA;&#xA;https://trino.io/assets/blog/intro-to-hive-connector/intro-to-hive.jpeg&#xA;&#xA;https://github.com/bitsondatadev/trino-getting-started/tree/main/hive/trino-minio&#xA;&#xA;#trino #hive&#xA;&#xA;bits&#xA;&#xA;!--emailsub--]]&gt;</description>
      <content:encoded><![CDATA[<p>TL;DR: The Hive connector is what you use in Trino for reading data from object storage that is organized according to the rules laid out by Hive, without using the Hive runtime code.</p>

<p><img src="https://trino.io/assets/blog/intro-to-hive-connector/hive.png" alt=""/></p>



<p>Originally Posted on <a href="https://trino.io/blog/2020/10/20/intro-to-hive-connector.html">https://trino.io/blog/2020/10/20/intro-to-hive-connector.html</a></p>

<p>One of the most confusing aspects when starting Trino is the Hive connector. Typically, you seek out the use of Trino when you experience an intensely slow query turnaround from your existing Hadoop, Spark, or Hive infrastructure. In fact, the genesis of Trino came about due to these slow Hive query conditions at Facebook back in 2012.</p>

<p>So when you learn that Trino has a Hive connector, it can be rather confusing since you moved to Trino to circumvent the slowness of your current Hive cluster. Another common source of confusion is when you want to query your data from your cloud object storage, such as AWS S3, MinIO, and Google Cloud Storage. This too uses the Hive connector. If that confuses you, don’t worry, you are not alone. This blog aims to explain this commonly confusing nomenclature.</p>

<h3 id="hive-architecture" id="hive-architecture">Hive architecture</h3>

<p>To understand the origins and inner workings of Trino’s Hive connector, you first need to know a few high-level components of the Hive architecture.</p>

<p><img src="https://trino.io/assets/blog/intro-to-hive-connector/hive.png" alt=""/></p>

<p>You can simplify the Hive architecture to four components:</p>

<p><em>The runtime</em> contains the logic of the query engine that translates the SQL -esque Hive Query Language(HQL) into MapReduce jobs that run over files stored in the filesystem.</p>

<p><em>The storage</em> component is simply that, it stores files in various formats and index structures to recall these files. The file formats can be anything as simple as JSON and CSV, to more complex files such as columnar formats like ORC and Parquet. Traditionally, Hive runs on top of the Hadoop Distributed Filesystem (HDFS). As cloud-based options became more prevalent, object storage like Amazon S3, Azure Blob Storage, Google Cloud Storage, and others needed to be leveraged as well and replaced HDFS as the storage component.</p>

<p>In order for Hive to process these files, it must have a mapping from SQL tables in <em>the runtime</em> to files and directories in <em>the storage</em> component. To accomplish this, Hive uses the Hive Metastore Service (HMS), often shortened to <em>the metastore</em> to manage the metadata about the files such as table columns, file locations, file formats, etc…</p>

<p>The last component not included in the image is Hive’s <em>data organization specification</em>. The documentation of this element only exists in the code in Hive and has been reverse engineered to be used by other systems like Trino to remain compatible with other systems.</p>

<p>Trino reuses all of these components except for <em>the runtime</em>. This is the same approach most compute engine takes when dealing with data in object stores, specifically, Trino, Spark, Drill, and Impala. When you think of the Hive connector, you should think about a connector that is capable of reading data organized by the unwritten Hive specification.</p>

<h3 id="trino-runtime-replaces-hive-runtime" id="trino-runtime-replaces-hive-runtime">Trino runtime replaces Hive runtime</h3>

<p>In the early days of big data systems, many expected query turnaround to take a long time due to the high volume of unstructured data in ETL workloads. The primary goal in early iterations of these systems was simply throughput over large volumes of data while maintaining fault-tolerance. Now, more businesses want to run fast interactive queries over their big data instead of running jobs that take hours and produce possibly undesirable results. Many companies have petabytes of data and metadata in their data warehouse. Data in storage is cumbersome to move and the data in the metastore takes a long time to repopulate in other formats. Since only the runtime that executed Hive queries needs replacement, the Trino engine utilizes the existing metastore metadata and files residing in storage, and the Trino runtime effectively replaces the Hive runtime responsible for analyzing the data.</p>

<h3 id="trino-architecture" id="trino-architecture">Trino Architecture</h3>

<p><img src="https://trino.io/assets/blog/intro-to-hive-connector/trino.png" alt=""/></p>

<h3 id="the-hive-connector-nomenclature" id="the-hive-connector-nomenclature">The Hive connector nomenclature</h3>

<p>Notice, that the only change in the Trino architecture is <em>the runtime</em>. The HMS still exists along with <em>the storage</em>. This is not by accident. This design exists to address a common problem faced by many companies. It simplifies the migration from using Hive to using Trino. Regardless of <em>the storage</em> component used <em>the runtime</em> makes use of the HMS and that is the reason this connector is the Hive connector.</p>

<p>Where the confusion tends to come from, is when you search for a connector from the context of the storage systems you want to query. You may not even be aware <em>the metastore</em> is a necessity or even exists. Typically, you look for an S3 connector, a GCS connector or a MinIO connector. All you need is the Hive connector and the HMS to manage the metadata of the objects in your storage.</p>

<h3 id="the-hive-metastore-service" id="the-hive-metastore-service">The Hive Metastore Service</h3>

<p>The HMS is the only Hive process used in the entire Trino ecosystem when using the Hive connector. The HMS is actually a simple service with a binary API using <strong><a href="https://thrift.apache.org/">the Thrift protocol</a></strong>. This service makes updates to the metadata, stored in an RDBMS such as PostgreSQL, MySQL, or MariaDB. There are other compatible replacements of the HMS such as AWS Glue, a drop-in substitution for the HMS.</p>

<p><a href="https://github.com/bitsondatadev/trino-getting-started/tree/main/hive/trino-minio">https://github.com/bitsondatadev/trino-getting-started/tree/main/hive/trino-minio</a></p>

<h3 id="getting-started-with-the-hive-connector-on-trino" id="getting-started-with-the-hive-connector-on-trino">Getting started with the Hive Connector on Trino</h3>

<p>To drive this point home, I <a href="https://github.com/bitsondatadev/trino-getting-started/tree/main/hive/trino-minio">created a tutorial that showcases using Trino and looking at the metadata it produces</a>. In the following scenario, the docker environment contains four docker containers:</p>
<ul><li><code>trino</code> - <em>the runtime</em> in this scenario that replaces Hive.</li>
<li><code>minio</code> - <em>the storage</em> is an open-source cloud object storage.</li>
<li><code>hive-metastore</code> - <em>the metastore</em> service instance.</li>
<li><code>mariadb</code> - the database that <em>the metastore</em> uses to store the metadata.</li></ul>

<p>You can play around with the system and optionally view the configurations. The scenario asks you to run a query to populate data in MinIO and then see the resulting metadata populated in MariaDB by the HMS. The next step asks you to run queries over the <code>mariadb</code> database which holds the generated metadata from <em>the metastore</em>.</p>

<p>If you have any questions or run into any issues with the example, you can find us on <a href="https://trino.io/slack.html">slack</a> on the <a href="https://bitsondata.dev/tag:dev" class="hashtag"><span>#</span><span class="p-category">dev</span></a> or <a href="https://bitsondata.dev/tag:general" class="hashtag"><span>#</span><span class="p-category">general</span></a> channels.</p>

<p>Have fun!</p>

<p>![<a href="https://trino.io/assets/blog/intro-to-hive-connector/intro-to-hive.jpeg](">https://trino.io/assets/blog/intro-to-hive-connector/intro-to-hive.jpeg](</a>)</p>

<p><a href="https://github.com/bitsondatadev/trino-getting-started/tree/main/hive/trino-minio">https://github.com/bitsondatadev/trino-getting-started/tree/main/hive/trino-minio</a></p>

<p><a href="https://bitsondata.dev/tag:trino" class="hashtag"><span>#</span><span class="p-category">trino</span></a> <a href="https://bitsondata.dev/tag:hive" class="hashtag"><span>#</span><span class="p-category">hive</span></a></p>

<p><em>bits</em></p>


]]></content:encoded>
      <guid>https://bitsondata.dev/a-gentle-introduction-to-the-hive-connector</guid>
      <pubDate>Wed, 21 Oct 2020 17:00:00 +0000</pubDate>
    </item>
  </channel>
</rss>