<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rodion Shlomo Solomonyk</title>
    <description>The latest articles on DEV Community by Rodion Shlomo Solomonyk (@coddicat).</description>
    <link>https://dev.to/coddicat</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1557174%2Fe29d8b33-5411-410c-a0c9-be2919f2ad9f.jpg</url>
      <title>DEV Community: Rodion Shlomo Solomonyk</title>
      <link>https://dev.to/coddicat</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/coddicat"/>
    <language>en</language>
    <item>
      <title>Developers Need Practical Kubernetes Experience</title>
      <dc:creator>Rodion Shlomo Solomonyk</dc:creator>
      <pubDate>Mon, 03 Jun 2024 00:50:06 +0000</pubDate>
      <link>https://dev.to/coddicat/developers-need-practical-kubernetes-experience-24oh</link>
      <guid>https://dev.to/coddicat/developers-need-practical-kubernetes-experience-24oh</guid>
      <description>&lt;p&gt;In today’s swiftly evolving tech landscape, programmers must constantly stay abreast of the latest technological advancements and methodologies. Mastery in developing and deploying distributed systems is no longer optional; it’s essential. Understanding the entire lifecycle — from development and CI/CD to Kubernetes cluster configuration, deployment, monitoring, and alerting — is critical for any developer aiming to thrive in this environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Importance of Kubernetes in Distributed Systems
&lt;/h2&gt;

&lt;p&gt;Distributed systems, by nature, require robust management of multiple interdependent components across different servers. Kubernetes excels in this environment by automating deployment, scaling, and operations of application containers across clusters of hosts. This makes it an invaluable tool for developers looking to streamline their applications’ operations and ensure reliability and scalability.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Gap Between DevOps and Developers
&lt;/h2&gt;

&lt;p&gt;Large organizations often have dedicated DevOps teams tasked with fine-tuning Kubernetes environments, which inadvertently might limit direct involvement from developers in these processes. This segregation can create a gap in understanding and hands-on experience for many developers who are otherwise involved in the broader development lifecycle.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Power of Practical Application
&lt;/h2&gt;

&lt;p&gt;While theoretical knowledge gained from courses and books is invaluable, it pales in comparison to the insights and understanding developed through practical application. This belief drives me to initiate personal projects to explore new technologies and tools actively. For instance, to deepen my Kubernetes knowledge, I developed &lt;a href="https://cicadakillerwasp.com"&gt;CicadaKillerWasp.com&lt;/a&gt;, a simple interactive quest that leads users through a series of steps culminating in a reward.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kubernetes: More Than Just Tooling
&lt;/h2&gt;

&lt;p&gt;Understanding Kubernetes involves more than just learning how to use its tools. It requires a deep understanding of the principles behind containerization, orchestration, and microservices architectures. Here are some deeper insights into the components and tools that every Kubernetes practitioner should know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Containerization with Docker:&lt;/strong&gt; Before diving into Kubernetes, one must understand containerization with Docker. Containers package up the code and all its dependencies so the application runs quickly and reliably from one computing environment to another.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Microservices Architecture:&lt;/strong&gt; Kubernetes is particularly effective in a microservices architecture where small, independent services communicate over well-defined APIs. Understanding this architecture is crucial as it affects how applications are developed and deployed within Kubernetes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stateful vs Stateless Applications:&lt;/strong&gt; Kubernetes handles stateless applications — those not saving data to a server or disk — differently from stateful ones, which maintain a continuous state. Learning to manage these different types of applications within Kubernetes is essential for effective orchestration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring and Logging:&lt;/strong&gt; Tools like Prometheus and Grafana are crucial for monitoring the health of applications running on Kubernetes. They provide insights into applications and infrastructure which can be vital for troubleshooting and optimizing performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Practices:&lt;/strong&gt; Security within Kubernetes involves more than just managing permissions. It includes securing container images, managing sensitive data through secrets, and protecting traffic with network policies.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Choosing the Right Deployment Environment
&lt;/h2&gt;

&lt;p&gt;For deploying this project, I opted for DigitalOcean over AWS due to its cost-effectiveness and simplicity. Unlike AWS, which often involves complex permissions settings, DigitalOcean offers a straightforward setup process that allowed me to focus more on the project’s specifics without getting bogged down by infrastructural complexities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Juggling Time: Project and Personal Life
&lt;/h2&gt;

&lt;p&gt;The development of CicadaKillerWasp.com took about two months, fitting around my full-time job. Nights and weekends became my primary working times — thankfully, my supportive wife made this possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deep Dive into Essential Kubernetes Tools and Components
&lt;/h2&gt;

&lt;p&gt;Kubernetes is not only a platform for managing containerized applications but also an ecosystem rich with tools that enhance and simplify various aspects of application and infrastructure management. Here’s an in-depth look at some of the key tools and components essential for anyone working with Kubernetes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Lens and K9s&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lens:&lt;/strong&gt; Often described as the Kubernetes IDE, Lens provides a comprehensive, user-friendly GUI that allows developers to manage Kubernetes clusters. It integrates with other essential tools like Prometheus for real-time monitoring and has capabilities for viewing logs, managing resources, and accessing a terminal within Kubernetes pods.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;K9s:&lt;/strong&gt; This tool offers a terminal-based UI to interact with your Kubernetes clusters. It focuses on simplicity and productivity, providing a real-time view of cluster activity and resource usage. It is ideal for those who prefer a command-line approach.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Helm Charts&lt;/strong&gt;&lt;br&gt;
Helm is the package manager for Kubernetes. It allows users to define, install, and upgrade even the most complex Kubernetes application. Charts, Helm’s packaging format, provide a set of files that describe a related set of Kubernetes resources. Helm simplifies the deployment and management of applications on Kubernetes and is indispensable for managing releases and rollbacks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Redis&lt;/strong&gt;&lt;br&gt;
Redis, a powerful in-memory data structure store, is used as a database, cache, and message broker. In Kubernetes, Redis can be used to handle sessions, cache data, and perform real-time analysis. Its performance is critical in distributed systems where quick data access and high availability are required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Prometheus and the Kube-Prometheus Stack&lt;/strong&gt;&lt;br&gt;
Prometheus is an open-source monitoring system with a dimensional data model, flexible query language, and real-time alerting. The Kube-Prometheus stack leverages Prometheus’s capabilities and adds a collection of resources to provide easy to operate end-to-end Kubernetes cluster monitoring. It includes Grafana dashboards for a rich visualization of the metrics collected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Ingress-Nginx&lt;/strong&gt;&lt;br&gt;
Ingress-Nginx is an Ingress controller for Kubernetes using NGINX as a reverse proxy and load balancer. It provides an external access point to your services, routing traffic to internal Kubernetes endpoints, and is crucial for managing access to applications running within a cluster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Grafana/Loki Stack&lt;/strong&gt;&lt;br&gt;
Grafana is an open-source platform for monitoring and observability. It is often paired with Loki, a log aggregation system inspired by Prometheus. Together, they provide capabilities to query, visualize, alert on, and understand your metrics and log data from Kubernetes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Cert-Manager&lt;/strong&gt;&lt;br&gt;
Cert-Manager is a native Kubernetes certificate management controller. It can help automate certificate management in cloud-native environments, providing a way to acquire certificates from a variety of issuing sources and ensuring certificates are valid and up to date.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. Messaging Systems (NATS, Kafka, RabbitMQ)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NATS:&lt;/strong&gt; A lightweight, high-performance messaging system for microservices architectures, ideal for scenarios requiring high speed and scalability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kafka:&lt;/strong&gt; A distributed streaming platform that can handle trillions of events a day. Initially conceived as a messaging queue, Kafka is based on an abstraction of a distributed commit log.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RabbitMQ:&lt;/strong&gt; Widely used open-source message broker software that supports multiple messaging protocols.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;9. GitHub Workflow Actions&lt;/strong&gt;&lt;br&gt;
GitHub Actions make it easy to automate all your software workflows with CI/CD. Build, test, and deploy your code right from GitHub. They are particularly useful in Kubernetes environments for automating deployment and management tasks directly from a GitHub repository.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;10. ConfigMaps and Secrets&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ConfigMaps:&lt;/strong&gt; Allow you to decouple configuration artifacts from image content to keep containerized applications portable. They store non-confidential data in key-value pairs and can be consumed by pods or provide configuration data for system components.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secrets:&lt;/strong&gt; Manage the storage and handling of sensitive information, such as passwords, OAuth tokens, and SSH keys. Using Kubernetes secrets is essential for maintaining the security of your applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Setting up the entire Kubernetes infrastructure from scratch — defining the deployment pipelines, setting up services, and ensuring proper load balancing and fault tolerance. This practical experience was invaluable, proving that real-world application is the best teacher. The project also incorporated essential Kubernetes tools and practices such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Using Cron Jobs for Automation:&lt;/strong&gt; Automation within Kubernetes, such as scheduling backups or maintenance jobs, can be managed through Cron Jobs, which execute tasks at scheduled times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Managing Data with Persistent Volumes:&lt;/strong&gt; Understanding how Kubernetes handles data storage through Persistent Volume Claims (PVCs) is crucial for applications that require data persistence.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These tools and components are fundamental for effectively managing and operating Kubernetes environments. They not only enhance productivity and efficiency but also ensure robust, scalable, and secure application deployments. For developers and DevOps professionals alike, familiarity with these tools will provide a strong foundation in modern cloud-native technology landscapes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Concluding Thoughts
&lt;/h2&gt;

&lt;p&gt;My experience with &lt;a href="https://cicadakillerwasp.com"&gt;CicadaKillerWasp.com&lt;/a&gt; reinforced the idea that mastering Kubernetes is not just about handling its complexity but also about leveraging its full potential to build better, more resilient applications. For developers willing to dive deep and embrace the hands-on experience, Kubernetes offers a path to significantly enhance their capabilities and contribute more effectively to their projects and teams. In the realm of software development, where change is the only constant, embracing technologies like Kubernetes is not just an option — it’s a necessity.&lt;/p&gt;

</description>
      <category>kubernates</category>
      <category>devops</category>
      <category>programming</category>
      <category>microservices</category>
    </item>
    <item>
      <title>c# Debounce and Throttle</title>
      <dc:creator>Rodion Shlomo Solomonyk</dc:creator>
      <pubDate>Sun, 02 Jun 2024 18:35:32 +0000</pubDate>
      <link>https://dev.to/coddicat/c-debounce-and-throttle-3a1g</link>
      <guid>https://dev.to/coddicat/c-debounce-and-throttle-3a1g</guid>
      <description>&lt;p&gt;In software development, managing the frequency of function executions is crucial to ensure efficiency, avoid redundant operations, and prevent system overloads. Whether you're working on a distributed system or a local application, implementing debounce and throttle mechanisms can significantly improve performance and reliability. In this article, we introduce two powerful .NET libraries designed for these purposes: &lt;a href="https://www.nuget.org/packages/DistributedDebounceThrottle"&gt;DistributedDebounceThrottle&lt;/a&gt; and &lt;a href="https://www.nuget.org/packages/DebounceThrottle"&gt;DebounceThrottle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/coddicat/DistributedDebounceThrottle"&gt;DistributedDebounceThrottle Repo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/coddicat/DebounceThrottle"&gt;DebounceThrottle Repo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://www.nuget.org/packages/DistributedDebounceThrottle"&gt;DistributedDebounceThrottle&lt;/a&gt;: A Distributed Solution
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;DistributedDebounceThrottle&lt;/strong&gt; is a .NET library designed to facilitate debounce and throttle mechanisms in distributed system environments. Leveraging &lt;strong&gt;Redis&lt;/strong&gt; for state management and distributed locking, this library ensures that function executions are properly debounced or throttled across multiple instances, preventing excessive or unintended operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Debounce:&lt;/strong&gt; Ensures a function is executed only once after a specified interval since the last call, minimizing redundant operations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Throttle:&lt;/strong&gt; Limits the execution frequency of a function, ensuring it's not executed more than once within a specified timeframe.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distributed Locks:&lt;/strong&gt; Implements the &lt;strong&gt;RedLock&lt;/strong&gt; algorithm for distributed locking to coordinate debounce and throttle logic across distributed systems.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redis Integration:&lt;/strong&gt; Utilizes Redis for managing timestamps and locks, offering a scalable solution for state synchronization.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;Install DistributedDebounceThrottle via NuGet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package DistributedDebounceThrottle
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;To integrate &lt;strong&gt;DistributedDebounceThrottle&lt;/strong&gt; in your application, ensure you have a &lt;strong&gt;Redis&lt;/strong&gt; instance ready for connection. Here's how to get started:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Configure Services:&lt;/strong&gt;
In your application's startup configuration, register &lt;strong&gt;DistributedDebounceThrottle&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Using an existing IConnectionMultiplexer instance:&lt;/span&gt;
    &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDistributedDebounceThrottle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Or, initiating a new IConnectionMultiplexer with a connection string:&lt;/span&gt;
    &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDistributedDebounceThrottle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;redisConnectionString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Inject and Use IDebounceThrottle:&lt;/strong&gt;
Inject &lt;strong&gt;IDebounceThrottle&lt;/strong&gt; to access debounce and throttle dispatchers:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SampleService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IDispatcher&lt;/span&gt; &lt;span class="n"&gt;_throttler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;SampleService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IDebounceThrottle&lt;/span&gt; &lt;span class="n"&gt;debounceThrottle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_throttler&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;debounceThrottle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ThrottleDispatcher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"uniqueDispatcherId"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSeconds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;ExecuteThrottledOperation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_throttler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DispatchAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Operation logic here.&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;p&gt;Customize your debounce and throttle settings via &lt;strong&gt;DebounceThrottleSettings&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RedisKeysPrefix:&lt;/strong&gt; A prefix for all Redis keys (default "debounce-throttle:").&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RedLockExpiryTime:&lt;/strong&gt; The expiry time for the distributed locks (default TimeSpan.FromSeconds(10)).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://www.nuget.org/packages/DebounceThrottle"&gt;DebounceThrottle&lt;/a&gt;: A Local Solution
&lt;/h2&gt;

&lt;p&gt;For developers who don't need distributed functionalities and are looking for a local solution, &lt;strong&gt;DebounceThrottle&lt;/strong&gt; provides simple yet effective debounce and throttle dispatchers. This library supports asynchronous actions and handles exceptions, ensuring that all Task results from dispatcher calls are consistent with the result of a single invocation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debounce Demo
&lt;/h2&gt;

&lt;p&gt;The following example shows how to display entered text after stopping pressing keys for 1000 milliseconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;debounceDispatcher&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DebounceDispatcher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;//trigger when to stop and exit&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ConsoleKey&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Escape&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="n"&gt;str&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;KeyChar&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;//every keypress iteration call dispatcher but the Action will be invoked only after stopping pressing and waiting 1000 milliseconds&lt;/span&gt;
            &lt;span class="n"&gt;debounceDispatcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Debounce&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; - &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UtcNow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hh:mm:ss.fff"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="n"&gt;str&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Throttle Demo
&lt;/h2&gt;

&lt;p&gt;The following example shows how to call an action every 100 milliseconds but invoke it only once in 1000 milliseconds (after the last invoking completed):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;//trigger when to stop and exit&lt;/span&gt;
        &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;stop&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;throttleDispatcher&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ThrottleDispatcher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;//every iteration call dispatcher but the Action will be invoked only once in 1500 milliseconds (500 action work time + 1000 interval)&lt;/span&gt;
            &lt;span class="n"&gt;throttleDispatcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ThrottleAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UtcNow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hh:mm:ss.fff"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;});&lt;/span&gt;

            &lt;span class="c1"&gt;//iteration every 100 milliseconds&lt;/span&gt;
            &lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//wait trigger to stop and exit&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Whether you need a distributed solution with Redis integration or a simple local library, both &lt;strong&gt;DistributedDebounceThrottle&lt;/strong&gt; and &lt;strong&gt;DebounceThrottle&lt;/strong&gt; provide robust debounce and throttle mechanisms for .NET applications. By integrating these libraries, you can enhance your application's performance and reliability, effectively managing function executions to prevent system overloads and redundant operations.&lt;/p&gt;

&lt;p&gt;Links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/coddicat/DistributedDebounceThrottle"&gt;DistributedDebounceThrottle Repo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/coddicat/DebounceThrottle"&gt;DebounceThrottle Repo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>csharp</category>
      <category>redis</category>
      <category>distributedsystems</category>
      <category>webdev</category>
    </item>
    <item>
      <title>C# Zip Archive Entry</title>
      <dc:creator>Rodion Shlomo Solomonyk</dc:creator>
      <pubDate>Fri, 31 May 2024 23:40:38 +0000</pubDate>
      <link>https://dev.to/coddicat/c-zip-archive-entry-2339</link>
      <guid>https://dev.to/coddicat/c-zip-archive-entry-2339</guid>
      <description>&lt;p&gt;Links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/coddicat/FastZipEntry"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nuget.org/packages/FastZipEntry"&gt;NuGet&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;While the .NET ecosystem provides robust libraries for working with ZIP files, developers often encounter limitations due to internal or sealed classes that prevent straightforward extension or modification. In this article, I will discuss why and how I created &lt;a href="https://www.nuget.org/packages/FastZipEntry"&gt;FastZipEntry&lt;/a&gt;, a .NET library designed to efficiently retrieve specific entries from a ZIP archive &lt;strong&gt;without extracting the entire archive or iterating through all entries&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Motivation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Limitations in the Existing Libraries
&lt;/h3&gt;

&lt;p&gt;The existing ZIP handling libraries in .NET, specifically those provided by Microsoft, are powerful but sometimes restrictive. Many of the useful classes and methods are marked as internal or sealed, which means they cannot be extended or modified outside of their original scope. This restriction poses a significant challenge when you need to tweak or extend the functionality to suit specific needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Need for Efficient Entry Retrieval
&lt;/h3&gt;

&lt;p&gt;In many scenarios, you might need to access a specific entry in a ZIP archive without extracting the entire archive or loading all entries into memory. This is particularly important for large archives where performance and memory consumption become critical concerns. Unfortunately, the default System.IO.Compression.ZipArchive does not provide a straightforward way to achieve this.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: &lt;a href="https://www.nuget.org/packages/FastZipEntry"&gt;FastZipEntry&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;To overcome these limitations, I decided to create &lt;strong&gt;FastZipEntry&lt;/strong&gt; NuGet , a library that allows efficient retrieval of specific entries from a ZIP archive. This library leverages modified code from Microsoft's &lt;em&gt;System.IO.Compression&lt;/em&gt; but extends its functionality to meet the needs outlined above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Efficient Retrieval&lt;/strong&gt;: Locate and retrieve specific ZIP entries by name without extracting the entire archive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decompression&lt;/strong&gt;: Includes support and access to the &lt;strong&gt;Deflate64&lt;/strong&gt; algorithm for decompression, also sourced from Microsoft’s codebase.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Implementation Details
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Leveraging Microsoft’s Source Code
&lt;/h3&gt;

&lt;p&gt;The core of &lt;em&gt;FastZipEntry&lt;/em&gt; is based on the source code from Microsoft's &lt;em&gt;System.IO.Compression&lt;/em&gt; library, which is available under the MIT license. Sadly, due to the internal and sealed nature of many classes, I had to copy and modify the necessary code to enable the required functionality. This approach, while not ideal, was necessary to provide the flexibility and performance benefits that &lt;strong&gt;FastZipEntry&lt;/strong&gt; offers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Deflate64 Support
&lt;/h3&gt;

&lt;p&gt;In addition to the core functionality, I also integrated the &lt;strong&gt;Deflate64&lt;/strong&gt; algorithm for decompression. This algorithm, taken from Microsoft’s source code, is essential for handling archives that use this specific compression method. By including this in &lt;strong&gt;FastZipEntry&lt;/strong&gt;, I ensured that the library can handle a wider range of ZIP archives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage Example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.IO&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;FastZipEntry&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Open a ZIP file and create a ZipEntryAccess instance&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;FileStream&lt;/span&gt; &lt;span class="n"&gt;zipFileStream&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;FileStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"path/to/your.zip"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileAccess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;ZipEntryAccess&lt;/span&gt; &lt;span class="n"&gt;zipEntryAccess&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ZipEntryAccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;zipFileStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Encoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UTF8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Retrieve a specific entry from the ZIP file&lt;/span&gt;
&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;entryName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"desired_entry.txt"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;ZipEntry&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;zipEntryAccess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RetrieveZipEntry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entryName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StringComparison&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrdinalIgnoreCase&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Use the entry (e.g., decompress it)&lt;/span&gt;
    &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Stream&lt;/span&gt; &lt;span class="n"&gt;entryStream&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;FileStream&lt;/span&gt; &lt;span class="n"&gt;outputStream&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;FileStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"path/to/extracted/desired_entry.txt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileMode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileAccess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;entryStream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CopyTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputStream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Entry not found."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I hope &lt;strong&gt;FastZipEntry&lt;/strong&gt; proves useful in your projects, and I welcome any contributions or feedback.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can find and install the &lt;a href="https://www.nuget.org/packages/FastZipEntry"&gt;FastZipEntry&lt;/a&gt; package from the NuGet repository.&lt;/li&gt;
&lt;li&gt;You can also check out the source code in &lt;a href="https://github.com/coddicat/FastZipEntry"&gt;GitHub repository&lt;/a&gt; of this project.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Acknowledgments
&lt;/h2&gt;

&lt;p&gt;This library is based on modified code from the Microsoft &lt;a href="https://github.com/dotnet/runtime/tree/9daa4b41eb9f157e79eaf05e2f7451c9c8f6dbdc/src/libraries/System.IO.Compression/src/System/IO/Compression"&gt;System.IO.Compression&lt;/a&gt; repository, and includes the &lt;strong&gt;Deflate64&lt;/strong&gt; algorithm from the same source.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>zip</category>
      <category>archive</category>
      <category>development</category>
    </item>
    <item>
      <title>c# Message queue with Redis</title>
      <dc:creator>Rodion Shlomo Solomonyk</dc:creator>
      <pubDate>Fri, 31 May 2024 23:31:53 +0000</pubDate>
      <link>https://dev.to/coddicat/c-message-queue-with-redis-1hjk</link>
      <guid>https://dev.to/coddicat/c-message-queue-with-redis-1hjk</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In distributed microservice architectures, managing message queue can be particularly challenging when you need to ensure messages are processed in order, reliably, and with the ability to &lt;strong&gt;pause&lt;/strong&gt; and &lt;strong&gt;resume&lt;/strong&gt; processing. The &lt;a href="https://www.nuget.org/packages/RedisMessagePipeline"&gt;RedisMessagePipeline NuGet package&lt;/a&gt; offers a robust solution to these challenges, making your message handling tasks seamless and reliable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Unlike traditional message queue solutions that may be heavy and complicated, &lt;strong&gt;RedisMessagePipeline&lt;/strong&gt; offers a lightweight and straightforward approach. In distributed microservice architectures, managing message pipelines can be particularly challenging when you need to ensure messages are processed in order, reliably, and with the ability to &lt;strong&gt;pause&lt;/strong&gt; and &lt;strong&gt;resume&lt;/strong&gt; processing.&lt;/p&gt;

&lt;p&gt;Imagine a scenario where your microservice receives multiple client requests/events/message that need to be processed in the background. Each request might take a significant amount of time to complete, and you need to ensure that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Messages are processed in the order they are received.&lt;/li&gt;
&lt;li&gt;Processing can be paused and resumed without losing data or processing out of order.&lt;/li&gt;
&lt;li&gt;Failures are handled gracefully with retries or manual intervention.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Traditional event streaming or message queue solutions like &lt;strong&gt;Kafka&lt;/strong&gt;, &lt;strong&gt;Redis Streams&lt;/strong&gt;, &lt;strong&gt;NATS&lt;/strong&gt;, and &lt;strong&gt;RabbitMQ&lt;/strong&gt; have their strengths but might not provide the required order guarantees and flexible control over the message processing flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with Traditional Event Streaming
&lt;/h2&gt;

&lt;p&gt;When dealing with microservices, maintaining order and reliability in message processing can become complex. Here’s why some popular event streaming solutions might fall short:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Kafka&lt;/strong&gt;: While it supports ordered processing, Kafka lacks built-in mechanisms for easy stopping and resuming message consumption, making error handling cumbersome.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redis Streams&lt;/strong&gt;: Offers powerful features but managing ordered processing across multiple instances requires additional custom logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NATS&lt;/strong&gt;: Known for simplicity and speed, but does not inherently support ordered message processing or pausing/resuming the pipeline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RabbitMQ&lt;/strong&gt;: Lacks native support for ordered message processing and mechanisms for stopping and resuming message consumption easily.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  RedisMessagePipeline to the Rescue
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;RedisMessagePipeline&lt;/strong&gt; is designed to tackle these exact challenges, offering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Single Message Handling&lt;/strong&gt;: Ensures each message is processed individually in the correct order.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failure Handling&lt;/strong&gt;: Configurable policies for automatic retries or manual intervention.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pipeline Control&lt;/strong&gt;: Administrative controls to stop, resume, or clean the pipeline as needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ideal for Long-Running Tasks&lt;/strong&gt;: Perfect for scenarios where background processing of client requests takes time and order must be maintained.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplicity and Efficiency&lt;/strong&gt;: Unlike traditional message queue solutions that may be heavy and complicated, &lt;strong&gt;RedisMessagePipeline&lt;/strong&gt; offers a lightweight and straightforward approach.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;Let’s dive into a concrete example to see how RedisMessagePipeline solves these problems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Installation
&lt;/h3&gt;

&lt;p&gt;Install the RedisMessagePipeline package from NuGet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package RedisMessagePipeline
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Basic Configuration
&lt;/h3&gt;

&lt;p&gt;Set up the Redis client and pipeline settings in your application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;ConnectionMultiplexer&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ConnectionMultiplexer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"localhost:6379"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;IDatabase&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetDatabase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RedisPipelineFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;LoggerFactory&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;consumer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateConsumer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MyMessageHandler&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RedisPipelineConsumerSettings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"client-requests"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateAdmin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RedisPipelineAdminSettings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"client-requests"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Using the Pipeline
&lt;/h3&gt;

&lt;p&gt;Here’s how to use RedisMessagePipeline to store client requests and process them reliably in the background:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stop the Pipeline:&lt;/strong&gt; Temporarily pause message processing, for example, during maintenance or an update.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StopAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Push Messages:&lt;/strong&gt; Store incoming client requests in the pipeline. Each message will be processed in the order it was received.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PushAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"any request serialized data"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Resume the Pipeline:&lt;/strong&gt; Resume processing messages, ensuring they are handled in order.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ResumeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;None&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Process Messages:&lt;/strong&gt; Start the consumer to process the messages in the background.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;consumer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecuteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;None&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The RedisMessagePipeline NuGet package provides a powerful solution for managing ordered and reliable message processing in distributed microservice architectures. It ensures high reliability and consistency, making it an invaluable tool for developers who need precise control over their message pipelines. Unlike some modern event streaming solutions, RedisMessagePipeline offers simplicity and control, making it ideal for scenarios where ordered processing and pipeline control are paramount.&lt;/p&gt;

&lt;h2&gt;
  
  
  License and Support
&lt;/h2&gt;

&lt;p&gt;RedisMessagePipeline is distributed under the MIT License. For support and contributions.&lt;/p&gt;

&lt;p&gt;GitHub repository — &lt;a href="https://github.com/coddicat/RedisMessagePipeline"&gt;https://github.com/coddicat/RedisMessagePipeline&lt;/a&gt;&lt;/p&gt;

</description>
      <category>redis</category>
      <category>messagequeue</category>
      <category>development</category>
      <category>csharp</category>
    </item>
    <item>
      <title>JavaScript 3D ray casting: Chrome is the most efficient browser</title>
      <dc:creator>Rodion Shlomo Solomonyk</dc:creator>
      <pubDate>Fri, 31 May 2024 23:16:06 +0000</pubDate>
      <link>https://dev.to/coddicat/javascript-3d-ray-casting-chrome-is-the-most-efficient-browser-h25</link>
      <guid>https://dev.to/coddicat/javascript-3d-ray-casting-chrome-is-the-most-efficient-browser-h25</guid>
      <description>&lt;h3&gt;
  
  
  Links:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://raycastingjs.web.app/"&gt;Play the Game&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/coddicat/ts3d"&gt;GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5a4eyxhtla1wnadftv17.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5a4eyxhtla1wnadftv17.png" alt="Game scene" width="800" height="451"&gt;&lt;/a&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7plxb1suqju2m6xslgue.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7plxb1suqju2m6xslgue.png" alt="Game scene" width="800" height="451"&gt;&lt;/a&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuvqyt3iqnsjf6bv5zlp2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuvqyt3iqnsjf6bv5zlp2.png" alt="Game scene" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;As a full-stack developer, I constantly seek out tasks and projects to keep my skills sharp and my curiosity satisfied. My latest adventure took me back to the 90s, to the world of 3D ray casting. Inspired by classics like Wolfenstein 3D, I set out to implement this technique in pure JavaScript, without relying on the 3D functions of the canvas context. This project turned into a fascinating journey through the intricacies of game development and browser performance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frlavc9r78ta3ywrf00q1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frlavc9r78ta3ywrf00q1.jpg" alt="raycasting" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge
&lt;/h2&gt;

&lt;p&gt;Ray casting in the style of early 90s games involves casting rays from the player’s perspective, one for each pixel horizontally across the screen. The rays move in intervals of one tile, creating a 3D effect. Despite having no background in game development, I was eager to tackle challenges such as sprite rendering, player movement, and interaction with the environment and moving objects.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb6ho4caq35cdotogo3c2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb6ho4caq35cdotogo3c2.jpg" alt="raycasting" width="583" height="424"&gt;&lt;/a&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ohro1f8vpp2l2ro2bwk.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ohro1f8vpp2l2ro2bwk.gif" alt="raycasting" width="192" height="192"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why JavaScript?
&lt;/h2&gt;

&lt;p&gt;I chose to implement the project using pure JavaScript to see if modern browsers and computers could handle the intensive processing required for rendering game scenes. This approach meant manually processing and rendering each pixel, without relying on built-in 3D rendering functions. The question at the heart of the project was: Can modern web technology deliver acceptable performance for such a demanding task?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;willReadFrequently&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Journey
&lt;/h2&gt;

&lt;p&gt;What began as a simple curiosity-driven project quickly grew into a full-fledged endeavor, consuming six months of my time. The challenge of optimizing performance was particularly engaging. I frequently revisited my code, eliminating redundant operations and optimizing calculations, especially those involving trigonometric functions. For instance, I used bitwise shifts instead of multiplying or dividing by 2, and bitwise |0 instead of rounding or using Math.floor(), to speed up computations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pixel-Level Control
&lt;/h2&gt;

&lt;p&gt;For manipulating pixels, I used Uint32Array, which allowed me to write pixel states directly by index. This approach was effective because three bits represent color, and the fourth bit is alpha, enabling adjustments in pixel brightness and marking screen areas as occupied. This level of control was crucial for achieving the desired visual effects and performance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ArrayBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buf8&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8ClampedArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint32Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;alphaMask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0x00ffffff&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;light&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pixel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;textureImageData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;textureIndex&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dataIndex&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pixel&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;alphaMask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Overcoming Mathematical Hurdles
&lt;/h2&gt;

&lt;p&gt;One of the most daunting aspects was finding solutions and formulas for rendering horizontal surfaces such as floors, ceilings, and various beams. Without a background in mathematics or game development, this part of the project was particularly time-consuming. However, the result was a visually interesting and acceptable image that met my performance goals.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//// This's just an example. The actual code has been improved to reduce the number of calculations.&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;getTileSpriteDataIndexBySideX_positive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Ray&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;textureData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TextureData&lt;/span&gt;
  &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;textureData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fixSinAbs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fixDistance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;factY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;textureData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;rayAngle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fixSinAbs&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fixedDistance&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fixedCos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;ray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fixDistance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fixedCosDiff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fixedCos&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;offsetX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;fixedCosDiff&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fixedCosDiff&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;spriteOffsetX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;offsetX&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;offsetX&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;spriteOffsetY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;factY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fixedX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;spriteOffsetY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;imul&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fixedX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;spriteOffsetX&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Not Just JavaScript
&lt;/h2&gt;

&lt;p&gt;Although I initially aimed to use pure JavaScript, I eventually incorporated TypeScript to manage complex code structures more effectively. Additionally, I used the Vue framework to bundle everything together, ensuring a smoother development process and more maintainable codebase.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Tile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;texture&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Texture&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Wall&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="na"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;texture&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Texture&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;MapItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;walls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Wall&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;tiles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Tile&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;  
  &lt;span class="nl"&gt;mirror&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;stopRay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Performance Insights
&lt;/h2&gt;

&lt;p&gt;The project also served as an insightful browser performance test. On my laptop with an i7–10510U processor, Chrome emerged as the most efficient browser, followed by Edge, Firefox, and Safari. These findings highlighted the varying capabilities of different browsers when handling intensive JavaScript computations.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;FPS. 1920x1080. Chrome&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1emn0u1omhat8uv3mjlc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1emn0u1omhat8uv3mjlc.png" alt="Chrome" width="763" height="537"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;FPS. 1920x1080. Edge&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fufb9twy1r9shiflyk9x5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fufb9twy1r9shiflyk9x5.png" alt="Edge" width="796" height="588"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;FPS. 1920x1080. Firefox&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftkm8vprwanw5rg12elt4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftkm8vprwanw5rg12elt4.png" alt="Firefox" width="747" height="601"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Future Prospects
&lt;/h2&gt;

&lt;p&gt;While the current version of the game is a proof of concept, I have plans to enhance its playability. Future updates may include adding player goals, sound effects, and even multiplayer capabilities. The project has provided a solid foundation for exploring these possibilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This journey into 90s 3D ray casting has been both challenging and rewarding. It started as a pet project driven by curiosity and evolved into a deep dive into classic game programming techniques and modern web performance. I invite you to check out the game and explore its source code. Your feedback and contributions are welcome as I continue to refine and expand this project.&lt;/p&gt;

&lt;p&gt;Links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://raycastingjs.web.app/"&gt;Play the Game&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/coddicat/ts3d"&gt;GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thank you for reading, and happy coding!&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/4Bktj-XoUHs"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>raycasting</category>
      <category>chrome</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
