<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Valentyn Druzhynin on Medium]]></title>
        <description><![CDATA[Stories by Valentyn Druzhynin on Medium]]></description>
        <link>https://medium.com/@valentin.druzhinin?source=rss-67a1cfcdaaf6------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*2uFXhNP-RNZyWBiaVpMIKg.jpeg</url>
            <title>Stories by Valentyn Druzhynin on Medium</title>
            <link>https://medium.com/@valentin.druzhinin?source=rss-67a1cfcdaaf6------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 19:09:05 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@valentin.druzhinin/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Scaling PostgreSQL to 800 Million Users: Key Lessons from OpenAI’s Infrastructure Journey]]></title>
            <link>https://medium.com/@valentin.druzhinin/scaling-postgresql-to-800-million-users-key-lessons-from-openais-infrastructure-journey-5055f6e20b01?source=rss-67a1cfcdaaf6------2</link>
            <guid isPermaLink="false">https://medium.com/p/5055f6e20b01</guid>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[architecture]]></category>
            <category><![CDATA[database]]></category>
            <category><![CDATA[postgresql]]></category>
            <category><![CDATA[openai]]></category>
            <dc:creator><![CDATA[Valentyn Druzhynin]]></dc:creator>
            <pubDate>Sat, 31 Jan 2026 19:10:29 GMT</pubDate>
            <atom:updated>2026-01-31T19:10:29.139Z</atom:updated>
            <content:encoded><![CDATA[<p>On January 22, 2026, OpenAI published a great technical article:<a href="https://openai.com/index/scaling-postgresql/"> “Scaling PostgreSQL to power 800 million ChatGPT users”</a>. What makes this article particularly valuable isn’t the massive scale itself, but rather how every challenge they faced is familiar to any engineering team experiencing rapid database growth.</p><p>The key insight? There was no single magic optimization. Instead, OpenAI systematically applied multiple safeguards that progressively addressed common pain points. Let’s explore their approach and what we can learn from it.</p><h3>The Challenge: Explosive Growth Without a full Architecture rewrite</h3><p>After launching ChatGPT, OpenAI experienced unprecedented traffic growth. Within a year, their database load increased around 10 times. Despite this massive growth, they maintained a single-primary architecture with one Azure PostgreSQL Flexible Server primary instance and nearly 50 read replicas across different regions.</p><p>Rather than migrating to a different database system, they took a methodical approach:</p><ul><li>systematically reduce peak load</li><li>isolate risks</li><li>quickly cut off problematic patterns</li></ul><p>The rest of this article will show you their specific techniques, the historical context behind these approaches, and the associated risks.</p><h3>1. Reducing Primary Load: Less Reading, Less Writing</h3><h3>The Problem</h3><p>In a single-primary architecture, when you have one writer for the entire production system, it works well for read-heavy workloads, but struggles with write spikes. One bad deployment or backfill operation can instantly overload the primary. As OpenAI states directly: “heavy write spikes can quickly overload the primary.”</p><h3>Their Solution</h3><p><strong>Offload reads from the primary</strong></p><ul><li>Moved as many read queries as possible from the writer node to replicas</li><li>Isolated the primary for critical write operations only</li></ul><p><strong>Separate write-heavy, shardable workloads</strong></p><ul><li>Migrated horizontally partitionable workloads to separate databases like Azure Cosmos DB</li><li>Particularly effective for event logs, metrics, and high-volume update streams</li></ul><p><strong>Application-level optimizations</strong></p><ul><li>Eliminated duplicate or unnecessary writes</li><li>Implemented lazy writes where business logic permitted, smoothing out write spikes</li><li>Applied strict rate limits to backfill operations to prevent overwhelming the primary</li></ul><p><strong>Architectural constraint</strong></p><ul><li>Established a rule: no new tables in the current PostgreSQL instance</li><li>New workloads default to sharded systems</li></ul><h3>Industry Context</h3><p>This is a standard strategy in high-scale systems. Keep PostgreSQL for the transactional core, and move write-heavy data (events, logs, high-frequency updates) to:</p><ul><li>Sharded/partitioned NoSQL databases</li><li>Event stores</li><li>Separate PostgreSQL clusters with partitioning</li></ul><p>Lazy writes are equally common — deferring writes or batching them when business requirements allow.</p><h3>Risks to Consider</h3><ul><li><strong>Hot partitions</strong>: Incorrect partition keys simply move the pain from relational to non-relational databases</li><li><strong>Consistency challenges</strong>: With state split between PostgreSQL and NoSQL, you need clear ownership of truth and reconciliation strategies</li><li><strong>Semantic differences</strong>: OLTP relies on transactions and joins; sharded systems don’t, requiring changes to data access patterns</li><li><strong>Operational complexity</strong>: Multiple databases mean multiple failure modes and incident types</li><li><strong>Product expectations</strong>: Lazy writes can break assumptions about data visibility</li></ul><h3>2. Query Optimization: Tackling OLTP Anti-Patterns</h3><h3>The Problem</h3><p>OpenAI discovered that several expensive queries were consuming CPU and degrading API performance during traffic spikes. One particularly problematic query joined 12 tables, and spikes in this query’s execution led to high-severity incidents.</p><p>They also identified a “silent” problem: long idle transactions blocking autovacuum and causing table bloat.</p><h3>Their Solution</h3><p><strong>SQL optimization</strong></p><ul><li>Simplified complex multi-table JOINs where possible</li><li>Split necessary JOINs into multiple queries, moving some logic to the application layer</li><li>Specifically reviewed ORM-generated SQL, as this is often where performance disasters originate</li></ul><p><strong>Idle transaction management</strong></p><ul><li>Configured idle_in_transaction_session_timeout to prevent idle transactions from blocking autovacuum</li></ul><h3>Industry Context</h3><p>This is standard practice for high-QPS OLTP systems. While PostgreSQL excels in many scenarios, large multi-table joins can become problematic at production scale. ORMs simplify development but can transform simple queries into N+1 problems.</p><p>The “idle in transaction” issue is particularly insidious — PostgreSQL can keep transactions open, blocking vacuum or interfering with other sessions. The idle_in_transaction_session_timeout parameter does exactly what its name suggests: it terminates sessions stuck inside transactions.</p><h3>Risks to Consider</h3><ul><li><strong>Timeout impact on legitimate use cases</strong>: Overly aggressive timeouts can break migrations, long-running admin jobs, or report generation. These require separate roles and connections.</li><li><strong>Application-level joins</strong>: Moving joins to the application can introduce N+1 queries, increase application load, and create race condition opportunities</li><li><strong>Profiling is essential</strong>: Fixing symptoms without proper profiling will lead you back to the same problems within months</li></ul><h3>3. High Availability: Surviving Primary Failures</h3><h3>The Problem</h3><p>In a single-primary setup, the writer is the most critical point of failure. Losing a read replica is manageable — other replicas handle the load. But if the primary fails, writes stop completely, and sometimes even read queries fail.</p><h3>Their Solution</h3><p><strong>Read resilience</strong></p><ul><li>Moved nearly all critical read operations to replicas</li><li>This ensures that even if the primary fails, the service can continue reading</li><li>Writes fail, but this is no longer a SEV0 incident since the product remains partially functional</li></ul><p><strong>High availability configuration</strong></p><ul><li>Deployed the primary in HA mode with a hot standby read replica</li><li>The standby remains constantly synchronized and ready for promotion during failures or planned maintenance</li><li>Expensive, but highly reliable</li></ul><p><strong>Regional redundancy</strong></p><ul><li>Deployed multiple replicas in each region</li><li>Single replica failure doesn’t cause regional outages</li></ul><h3>Industry Context</h3><p>This is the fundamental pattern of graceful degradation. If you can make your product read-only temporarily instead of completely unavailable, that’s a significant win. HA with hot standby is standard for managed PostgreSQL services.</p><h3>Risks to Consider</h3><ul><li><strong>Eventual consistency</strong>: Reading from replicas requires managing replication lag</li><li><strong>Failover complexity</strong>: Potential split-brain scenarios, Unexpected latency or connection issues after promotion, Need for clear writer identification</li><li><strong>Cost</strong>: Capacity buffers are expensive, but the alternative (total outages) is worse</li></ul><h3>4. Connection Pooling: Managing Thousands of Connections</h3><h3>The Problem</h3><p>PostgreSQL doesn’t handle thousands of direct connections efficiently. Each connection consumes resources, and without connection management, applications can exhaust available connections during traffic spikes.</p><h3>Their Solution</h3><p><strong>PgBouncer deployment</strong></p><ul><li>Implemented connection pooling at scale</li><li>Dramatically reduced the number of actual database connections</li><li>Enabled connection reuse across application requests</li></ul><p><strong>Session vs. transaction pooling</strong></p><ul><li>Carefully chose pooling modes based on workload characteristics</li><li>Transaction pooling for stateless operations</li><li>Session pooling where connection state matters</li></ul><h3>Industry Context</h3><p>PgBouncer is the de facto standard for PostgreSQL connection pooling. It sits between applications and the database, multiplexing many application connections into fewer database connections. This is critical for applications with many short-lived queries.</p><h3>Risks to Consider</h3><ul><li><strong>Mode selection is critical</strong>: Transaction pooling breaks session-based features (temp tables, prepared statements, advisory locks)</li><li><strong>Configuration complexity</strong>: Pool sizes, timeouts, and connection limits require careful tuning</li><li><strong>Monitoring overhead</strong>: An additional layer to monitor and troubleshoot</li></ul><h3>5. Workload Isolation: Separating Critical from Batch Operations</h3><h3>The Problem</h3><p>When high-priority user-facing queries share resources with low-priority batch jobs or analytics, traffic spikes in batch operations can degrade user experience.</p><h3>Their Solution</h3><p><strong>Dedicated infrastructure</strong></p><ul><li>Separate read replicas for different workload types</li><li>High-priority replicas exclusively serve user-facing queries</li><li>Low-priority replicas handle analytics and batch operations</li></ul><p><strong>Resource allocation</strong></p><ul><li>Clear boundaries prevent batch jobs from impacting critical paths</li><li>Enables independent scaling of different workload types</li></ul><h3>Industry Context</h3><p>Workload isolation is standard in mature systems. You separate:</p><ul><li>OLTP from OLAP</li><li>User-facing from internal/admin</li><li>Real-time from batch</li></ul><p>This can be done through separate instances, replicas, schemas, or even separate databases.</p><h3>Risks to Consider</h3><ul><li><strong>Increased cost</strong>: More infrastructure to maintain and pay for</li><li><strong>Operational complexity</strong>: More moving parts to monitor and manage</li><li><strong>Data freshness trade-offs</strong>: Analytics replicas may lag behind the primary</li></ul><h3>6. Cache Strategy: Reducing Database Load</h3><h3>The Problem</h3><p>Without effective caching, every request hits the database. This creates unnecessary load on both read replicas and the primary, especially for frequently accessed but rarely changing data.</p><h3>Their Solution</h3><p><strong>Multi-layer caching</strong></p><ul><li>Application-level caches for hot data</li><li>Careful cache invalidation strategies</li><li>Cache warming for critical data paths</li></ul><p><strong>Request coalescing</strong></p><ul><li>Implemented request deduplication for identical concurrent queries</li><li>A single query serves multiple waiting requests</li><li>Particularly effective for expensive queries or hot keys</li></ul><h3>Industry Context</h3><p>Caching is fundamental to scalability, but implementation varies widely:</p><ul><li>CDNs for static content</li><li>Redis/Memcached for application-level caching</li><li>Application in-memory caches</li><li>Query result caching</li></ul><p>Request coalescing (sometimes called “request collapsing”) prevents stampedes when cache entries expire.</p><h3>Risks to Consider</h3><ul><li><strong>Cache consistency</strong>: Stale data can break business logic</li><li><strong>Thundering herd</strong>: Cache misses on hot keys can overwhelm the database</li><li><strong>Complexity</strong>: More code paths to test and debug</li><li><strong>Hot key bottlenecks</strong>: Extremely popular keys can create queuing issues even with caching</li></ul><h3>7. Cascading Replication: Scaling Read Capacity Globally</h3><h3>The Problem</h3><p>OpenAI maintains nearly 50 read replicas worldwide to reduce latency. However, in this architecture, the primary must stream WAL logs to every replica. More replicas mean increased network and CPU load on the primary, and greater replica lag. As they note: “we can’t add replicas indefinitely.”</p><h3>Their Solution</h3><p><strong>Cascading replication architecture</strong></p><ul><li>Testing with Azure PostgreSQL team</li><li>Intermediate replicas retransmit WAL logs down the chain</li><li>Enables scaling to hundreds of replicas without overloading the primary</li></ul><p><strong>Operational considerations</strong></p><ul><li>Adds operational complexity, especially for failover management</li><li>Still in the testing phase due to these complexities</li></ul><h3>Industry Context</h3><p>Cascading replication is used when:</p><ul><li>You need many replicas</li><li>Cross-region traffic is expensive</li><li>The primary lacks network capacity to serve all replicas directly</li></ul><p>PostgreSQL documentation explicitly describes how cascading replication reduces direct connections to the primary and can save cross-site bandwidth.</p><h3>Risks to Consider</h3><ul><li><strong>Failover complexity</strong>: Who’s upstream, who’s downstream, how to rebuild the tree</li><li><strong>Intermediate replica failures</strong>: Affect entire subtrees, requiring clear failover matrices</li><li><strong>Lag propagation</strong>: Replication lag can compound across the chain</li><li><strong>Debugging challenges</strong>: More complex to diagnose issues in multi-tier replication</li></ul><h3>8. Rate Limiting and Query Blocking: Preventing Cascading Failures</h3><h3>The Problem</h3><p>OpenAI describes several SEV incident scenarios:</p><ol><li>Sudden endpoint traffic spikes</li><li>Expensive query spikes</li><li>Retry storms exhausting CPU, I/O, and connections</li><li>Complete system degradation</li></ol><h3>Their Solution</h3><p><strong>Multi-layer rate limiting</strong></p><ul><li>Application level</li><li>Connection pooler level</li><li>Proxy level</li><li>Query level</li></ul><p><strong>Retry policy control</strong></p><ul><li>Careful management of retry intervals</li><li>Prevention of retry storms</li></ul><p><strong>Targeted load shedding</strong></p><ul><li>ORM-level rate limiting support</li><li>Ability to completely block specific query digests</li><li>Quickly shed the worst traffic to enable system recovery</li></ul><h3>Industry Context</h3><p>In mature large-scale systems, rate limiting and load shedding exist at multiple layers:</p><ul><li>API gateways</li><li>Services</li><li>Workers</li><li>Databases</li></ul><p>The distinction between rate limiting and load shedding is important — rate limiting prevents problems; load shedding responds to them.</p><h3>Risks to Consider</h3><ul><li><strong>Collateral damage</strong>: Can accidentally block business-critical traffic</li><li><strong>Incomplete coverage</strong>: Limits at entry points don’t prevent retry storms from workers</li><li><strong>Query digest blocking</strong>: A powerful lever that requires careful processes to avoid blocking legitimate traffic</li></ul><h3>9. Schema Management: Lightweight DDL Only</h3><h3>The Problem</h3><p>Even small schema changes (like ALTER COLUMN TYPE) can trigger full table rewrites. On large tables, this means hours of I/O, replication lag, long locks, and degradation of the production database.</p><h3>Their Solution</h3><p><strong>Strict DDL policies</strong></p><ul><li>Only lightweight schema changes permitted</li><li>5-second timeout on all schema changes (DDL must not “hang”)</li><li>CREATE/DROP INDEX CONCURRENTLY allowed (reduces write blocking)</li></ul><p><strong>Table creation freeze</strong></p><ul><li>Schema changes only on existing tables</li><li>New tables for new features go to sharded systems (CosmosDB)</li></ul><p><strong>Controlled backfills</strong></p><ul><li>It can take over a week</li><li>Strictly rate-limited to prevent write spikes</li></ul><h3>Industry Context</h3><p>In high-load OLTP systems, DDL is extremely dangerous. To prevent incidents, teams either:</p><ul><li>Implement zero-downtime migrations (expand/contract, shadow columns)</li><li>Enforce strict rules like OpenAI’s</li></ul><h3>Risks to Consider</h3><ul><li><strong>Lock escalation</strong>: Even “lightweight” DDL often takes strong locks by default (PostgreSQL docs explicitly mention ACCESS EXCLUSIVE for many operations)</li><li><strong>Architectural constraints</strong>: Refusing new tables in PostgreSQL is a strong limitation that forces careful domain boundary thinking</li><li><strong>Human error</strong>: Someone will run a seemingly innocent ALTER that triggers a rewrite</li><li><strong>Automation is essential</strong>: Rules must be automatically verified</li></ul><h3>Understanding Write Amplification in PostgreSQL</h3><p>OpenAI explicitly addresses why heavy writes are painful in PostgreSQL: MVCC (Multi-Version Concurrency Control). When updating even a single field, PostgreSQL copies the entire row. This creates:</p><ul><li>Write amplification</li><li>Dead tuples</li><li>Increased read amplification</li></ul><p>For deeper analysis, they reference “The Part of PostgreSQL We Hate the Most” by Bohan Zhang and Andy Pavlo.</p><h3>The Results</h3><p>After one year:</p><ul><li>PostgreSQL load increased over 10x</li><li>Single primary + ~50 read replicas maintained</li><li>Near-zero replication lag</li><li>Low-latency reads across regions</li><li>Low double-digit milliseconds for p99 client-side latency</li><li>Five-nines availability</li><li>Only one SEV-0 incident in a year (during ImageGen viral launch with 10x write traffic spike and 100M+ new users in a week)</li></ul><p>This isn’t a story of finding one trick that magically solved everything. It’s a story of progressively and deliberately addressing nine different problem classes.</p><h3>Practical Takeaways: What You Can Apply Today</h3><h4>Quick Wins (Can Implement Tomorrow)</h4><ul><li>Enable and tune idle_in_transaction_session_timeout. Separate migrations and admin jobs into different roles/connections</li><li>SQL review practice for ORM-generated queries. Start with top-N queries by CPU and latency</li><li>Rate limit your most problematic endpoints</li><li>Enforce rate limits on all backfill operations</li></ul><h4>Medium-Term Improvements (Requires Maturity &amp; Discipline)</h4><ul><li>Deploy PgBouncer for connection pooling</li><li>Implement workload isolation</li><li>High/low priority separation</li><li>Dedicated instances/replicas for different workload types</li></ul><h4>Advanced Optimizations (Difficult But High ROI at Scale)</h4><p><strong>Schema change policy</strong></p><ul><li>Only lightweight DDL</li><li>Timeouts enforced</li><li>CONCURRENTLY for indexes</li><li>Automated checks for table rewrites</li></ul><p><strong>Migrate write-heavy workloads</strong></p><ul><li>Move to sharded systems or separate clusters</li><li>Eliminate redundant writes</li><li>Implement lazy writes where appropriate</li></ul><p><strong>Cascading replication</strong></p><ul><li>For deployments with many replicas where primary WAL streaming becomes a bottleneck</li></ul><h3><strong>Conclusion</strong></h3><p>OpenAI’s journey with PostgreSQL demonstrates that scaling doesn’t always require radical architecture changes. Through systematic application of proven techniques — each addressing a specific pain point. They achieved remarkable results while maintaining a single primary architecture.</p><p>The lesson isn’t that PostgreSQL is perfect for all use cases, but that disciplined engineering practices, careful monitoring, and incremental improvements can take you surprisingly far. Every technique they employed is well-documented and battle-tested; the innovation was in the systematic application and operational discipline.</p><p>Whether you’re managing 800 million users or 800 thousand, these patterns provide a roadmap for scaling relational databases effectively.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5055f6e20b01" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Apache Airflow 3.0: What’s New and Is It Worth Upgrading?]]></title>
            <link>https://medium.com/@valentin.druzhinin/apache-airflow-3-0-whats-new-and-is-it-worth-upgrading-71d940f841d8?source=rss-67a1cfcdaaf6------2</link>
            <guid isPermaLink="false">https://medium.com/p/71d940f841d8</guid>
            <category><![CDATA[data-engineering]]></category>
            <category><![CDATA[orchestration]]></category>
            <category><![CDATA[airflow-3]]></category>
            <category><![CDATA[airflow]]></category>
            <category><![CDATA[data-analytics]]></category>
            <dc:creator><![CDATA[Valentyn Druzhynin]]></dc:creator>
            <pubDate>Wed, 25 Jun 2025 16:09:52 GMT</pubDate>
            <atom:updated>2025-06-25T16:09:52.281Z</atom:updated>
            <content:encoded><![CDATA[<p>Apache Airflow 3.0 marks a significant evolution in workflow orchestration. Released in April 2025, Airflow has taken decisive steps toward architectural modernization, scalability, developer experience enhancements, and robust security. In this article, we’ll analyze the major changes in version 3.0 and compare them to the 2.x series.</p><h3>Background</h3><p>Apache Airflow has long been the go-to standard for building complex workflows, especially in data engineering and machine learning. The 2.x series brought stability, DAG-level parallelism, task mapping, and efficient scheduling.</p><p>However, with the rise of distributed, event-driven, and polyglot architectures, limitations began to emerge. The monolithic architecture of version 2.x struggled to meet modern requirements.</p><p>Airflow 3.0 is not just a version upgrade — it’s a rethinking of architecture, UI, and even the orchestration philosophy. Let’s explore each part in detail.</p><h3>A Completely Redesigned UI</h3><p>This is arguably the most visible and delightful improvement. While Airflow already had a functional UI, it was outdated and long overdue for an overhaul.</p><p>The new architecture ensures a much smoother user experience, especially in environments with hundreds or thousands of DAGs. Pages load faster, UI elements are more intuitive, and the navigation model is consistent and responsive across all sections.</p><h3>Key UI Improvements:</h3><ul><li><strong>Improved Grid View</strong>: Better timeline navigation, filtering, and searching. Users can now more easily inspect DAG run statuses and diagnose failed or stuck tasks.</li><li><strong>Enhanced Graph View</strong>: Improved zooming, scrolling, and task metadata interactions make understanding complex DAGs at a glance much easier.</li><li><strong>Asset Panel</strong>: A new interface specifically for data-centric workflows. It allows users to visualize assets, their producers and consumers, and track DAG interactions.</li><li><strong>Built-in Dark Mode</strong>: Although dark mode existed in version 2.x, this release introduces a fully polished, design-first dark theme. This was one of the most requested features from the community.</li><li><strong>Dedicated DAG Runs Tab</strong>: Now with full customization. You can enable only the columns you need and sort by almost any column — including duration (available in v3.1) and conf. This dramatically improves usability.</li><li><strong>FastAPI Instead of Flask-AppBuilder</strong>: A major performance win. It brings async support, better type validation via Pydantic, and overall improved performance for the Airflow API.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*MaxO0aeysjmfabCS" /></figure><p>Dark mode</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*B3T2z451snlJu-9Q" /></figure><p>DAG Runs page</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Cv3tYvKt5f2uOCIS" /></figure><p>Assets panel</p><h3>DAG Versioning</h3><p>One of the most critical features introduced in Airflow 3.0 is full support for DAG versioning.</p><p>In previous versions, DAGs were read directly from code files, and the latest version was always applied, even to previously executed runs. This led to several well-known issues:</p><ul><li><strong>Execution Drift</strong>: If a DAG was updated during execution, different tasks could run with different versions.</li><li><strong>No Auditing</strong>: Airflow lacked built-in tools to trace which version of a DAG was used in a specific run.</li><li><strong>Historical Inaccuracy</strong>: The UI always showed the latest DAG version, even for older runs, making the execution history unreliable or misleading.</li></ul><p><strong>With DAG versioning:</strong></p><ul><li>You can rerun a DAG in the exact version it originally ran.</li><li>Errors can be tied to specific code versions.</li><li>In regulated environments, auditing is now possible.</li><li>CI/CD workflows can now integrate commit hashes directly with Airflow DAG versions.</li></ul><h3>Airflow 3.0 introduces:</h3><ul><li>Automatic DAG versioning on every code change or deployment.</li><li>Historical DAG runs are preserved with their specific version.</li><li>UI lets you select and rerun specific DAG versions.</li></ul><p>I find this feature particularly appealing, as in practice it’s nearly impossible to create a DAG that never requires changes. Business logic evolves, bugs emerge, or optimizations become necessary, and built-in versioning makes this process transparent and manageable. The screenshot below shows the new DAG orchestration interface along with a dropdown list of version history.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*V0FGUsbNN3PmM_PM" /></figure><p>DAGs versioning</p><h3>Asset-Driven DAGs</h3><p>The cornerstone of smart scheduling in Airflow 3.0 is the concept of <strong>Assets</strong>. Inspired by datasets introduced in earlier versions, the new @asset decorator enables pipelines that react to data states.</p><p>An <strong>Asset</strong> in Airflow is a data unit or result created, transformed, or consumed by a DAG.</p><p>Assets can be:</p><ul><li>Database tables</li><li>Files in cloud storage</li><li>Trained ML models</li><li>API responses</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*p5OUkGLphQXOQsLg" /></figure><p>Building DAG-to-DAG dependencies using an intermediate asset.</p><p>To create this example, you only need to define 2 DAGs with the following code.</p><pre># producer DAG<br>from airflow.sdk import Asset, dag, task<br><br>@dag<br>def my_producer_dag():<br>    @task(outlets=[Asset(&quot;my_asset&quot;)])<br>    def my_producer_task():<br>        pass<br><br>    my_producer_task()<br><br><br>my_producer_dag()</pre><pre># consumer DAG<br>from airflow.sdk import Asset, dag<br>from airflow.providers.standard.operators.empty import EmptyOperator<br><br>@dag(schedule=[Asset(&quot;my_asset&quot;)],)<br>def my_consumer_dag():<br>    EmptyOperator(task_id=&quot;empty_task&quot;)<br><br>my_consumer_dag()</pre><p>Now, when the producer is triggered, data will be written to our asset, and afterward, the consumer will automatically be triggered with the data in the asset. You can use the “&amp;” (and) and “|” (or) operators to create more complex dependencies between assets. Something like this:</p><pre>from airflow.sdk import Asset, dag<br><br>@dag(<br>    schedule=(Asset(&quot;asset1&quot;)  | Asset(&quot;asset2&quot;) | Asset(&quot;asset3&quot;) | Asset(&quot;asset4&quot;)),<br>)<br>def downstream1_on_any():<br>    pass<br>downstream1_on_any()</pre><p>This allows the next DAG to be triggered even if only one asset changes. It will appear in the DAG console like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*QQYThWuaE-ZDIKU8" /></figure><p>Conditional asset scheduling</p><p><strong>Assets</strong> also support:</p><ul><li><strong>Structured Metadata</strong>: Including name, URI, group IDs, and custom metadata fields for easier debugging and search.</li><li><strong>Composable Design</strong>: Assets can refer to each other for modular, interdependent pipelines.</li><li><strong>Watchers</strong>: Currently supporting AWS SQS, assets can be monitored for external signals that trigger DAG runs. More integrations are promised soon.</li></ul><p>This turns Airflow into a “run when the data is ready” scheduler rather than a purely time-based one.</p><h3>Architectural Changes</h3><p>Airflow 3.0 focuses on modularity, scalability, and deployment flexibility — fitting the needs of hybrid and multi-cloud data platforms.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/872/0*BBs8GK-29HA29wSp" /></figure><p>Comparison of architecture of Airflow 2 and Airflow 3</p><h3>Task Execution API and SDK</h3><p>One of the most powerful changes is the new Task Execution API and Task SDK, allowing task execution independent of Airflow’s core engine.</p><p>Benefits:</p><ul><li><strong>Multi-language Support</strong>: Tasks can now be written in Java, Go, R, etc.</li><li><strong>Isolated Environments</strong>: Run tasks in containers or remote environments, avoiding dependency conflicts.</li><li><strong>Simplified Dev Workflow</strong>: Standardized interfaces make testing, deploying, and onboarding easier.</li></ul><p>This architecture is a breakthrough that makes Airflow more flexible and cloud-native. It aligns with the broader trends in data engineering toward language diversity, containerization, and task-level reproducibility.</p><h3>Edge Executor</h3><p>The Edge Executor allows event-driven and geo-distributed task execution. Tasks can now run close to the data source — whether that’s a remote data center or an edge device.</p><p>Advantages:</p><ul><li><strong>Geographic flexibility: </strong>Tasks can be executed in remote or regional clusters, data centers, or even on edge devices.</li><li><strong>Instant responsiveness: </strong>Ideal for low-latency, event-driven use cases — such as IoT, financial streams, or real-time monitoring.</li><li><strong>Reduced network latency: </strong>Computation happens near the data source, lowering the cost of transferring large data volumes to a central cluster.</li><li><strong>Event-driven pipelines: </strong>The Edge Executor fully supports the new scheduling model introduced in Airflow 3.0.</li></ul><h3>ML and AI Workflow Support</h3><p>Airflow 3.0 becomes a first-class orchestration tool for modern ML/AI pipelines.</p><h3>Interval-Free DAGs</h3><p>Older Airflow versions relied on execution_date and fixed scheduling intervals. This didn’t work well for ML workflows.</p><p>Now, DAGs allow you:</p><ul><li><strong>Model experimentation</strong>: Run the same DAG multiple times with different model configurations or data subsets, without conflicts caused by execution_date.</li><li><strong>Hyperparameter tuning</strong>: Execute multiple DAGs in parallel with different parameter grids or search strategies — each tracked independently.</li><li><strong>Inference DAGs</strong>: Invoke DAGs repeatedly to process new data in real time or for batch predictions triggered by external events or user actions. Ideal for flexible scenarios like inference, data quality checks, or user-triggered workflows.</li><li><strong>Event-driven DAGs</strong>: DAGs can now be triggered by external events, such as a new file appearing in cloud storage or a Kafka message. This enables true real-time reactive orchestration.</li></ul><p>This decouples execution logic from time-based scheduling, making Airflow more flexible for ML engineers.</p><h3>Developer Experience Improvements</h3><h3>CLI Split: airflow and airflowctl</h3><p>Airflow 3.0 splits the CLI into two tools:</p><ul><li>airflow — for local dev and testing</li><li>airflowctl — for managing remote production environments</li></ul><p>This separation improves workflows for both developers and platform teams.</p><h3>Migration Guide: 2.x → 3.0</h3><p><strong>Minimum Requirements</strong>:</p><ul><li>Python 3.9+</li></ul><p><strong>Steps</strong>:</p><ol><li>Upgrade to latest 2.x (2.7.3+ recommended)</li><li>Check DAGs for deprecated features</li><li>Backup your DB</li><li>Install 3.0 in a separate environment</li><li>Test DAGs</li><li>Integrate airflowctl into production</li></ol><h3>Airflow 2.x vs 3.0: Comparison Table</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8p8ZvMoJ3xFuxZfBgUMiIg.png" /></figure><p><strong>Should You Upgrade?</strong></p><p><strong>Yes</strong>, if you:</p><ul><li>Need secure, remote task execution</li><li>Want DAG versioning</li><li>Build real-time, event- or asset-driven orchestration</li><li>Use multi-language pipelines</li></ul><p><strong>Wait</strong>, if you:</p><ul><li>Have deep Flask customizations</li><li>Don’t use event-based scheduling or Assets</li><li>Still use Python &lt; 3.9 or have legacy dependencies</li></ul><h3>Final Thoughts</h3><p>Airflow 3.0 is a major release with timely and impactful improvements. Support for event-driven and inference pipelines opens doors for many ML/AI projects. The reworked architecture makes Airflow more modular and decentralized. DAG versioning and asset orchestration elevate reliability and clarity.</p><p>This release is particularly exciting to me because I personally contributed to some of its new features. 🙂</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=71d940f841d8" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[My Honest Review of the Machine Learning Specialization on CourseraMy Honest Review of the Machine…]]></title>
            <link>https://medium.com/@valentin.druzhinin/my-honest-review-of-the-machine-learning-specialization-on-courseramy-honest-review-of-the-machine-996a7d02d52d?source=rss-67a1cfcdaaf6------2</link>
            <guid isPermaLink="false">https://medium.com/p/996a7d02d52d</guid>
            <category><![CDATA[coursera]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[deep-learning]]></category>
            <category><![CDATA[course]]></category>
            <dc:creator><![CDATA[Valentyn Druzhynin]]></dc:creator>
            <pubDate>Sun, 06 Apr 2025 15:58:42 GMT</pubDate>
            <atom:updated>2025-04-06T15:58:42.245Z</atom:updated>
            <content:encoded><![CDATA[<h3><strong>My Honest Review of the Machine Learning Specialization on Coursera</strong></h3><p>Hey everyone!<br>I’m Valentyn, and today I’d like to share my experience with the <strong>Machine Learning Specialization</strong> on Coursera. It’s the updated version of Andrew Ng’s course — yep, the same Andrew Ng who launched the original ML course over 10 years ago. Back when it wasn’t mainstream yet 🙂</p><h3>What’s inside?</h3><p>The specialization includes three courses:</p><p><strong>1. Supervised Machine Learning<br></strong> Explains what ML is, how regression, classification, and neural networks work. A lot of focus is on <em>intuition</em> — why a model behaves a certain way, which metrics to choose, how to detect overfitting, etc.</p><p><strong>2. Advanced Learning Algorithms<br></strong> Covers deeper models, optimization, regularization, and multilayer neural networks. You’ll start using <strong>TensorFlow</strong>, and it already feels like real ML work.</p><p><strong>3. Unsupervised Learning, Recommenders, Reinforcement Learning<br></strong> In my opinion, this one was the easiest. It covers clustering (k-means), PCA, recommendation systems, and a short intro to reinforcement learning. Feels more like an overview than a deep dive.</p><h3>What models and algorithms are covered?</h3><ul><li>Linear and logistic regression</li><li>Softmax, decision trees</li><li>Neural networks (single and multilayer)</li><li>K-means, PCA</li><li>Recommendation systems (collaborative filtering)</li><li>Q-learning (intro-level)</li></ul><h3>What tools are used?</h3><p>Everything’s done in <strong>Python</strong>.<br>Core libraries:</p><ul><li>NumPy, Pandas, Matplotlib</li><li>TensorFlow (via Keras API)</li></ul><p>The best part — you don’t need to install anything. All the exercises run in <strong>Jupyter Notebooks</strong>, right in your browser.</p><p><strong>How long does it take?</strong></p><p>It took me over <strong>two months</strong>, to study about <strong>6–8 hours per week</strong>. The material is explained clearly and accessibly. Even if your math is a little rusty — don’t worry, you’ll be fine.</p><p><strong>Pros:</strong></p><ul><li>Super clear and accessible explanations</li><li>Lots of hands-on practice right after each module</li><li>The difficulty grows gradually</li><li>You genuinely understand what ML is and how it works</li></ul><h3>Cons:</h3><ul><li>I wish there were more real-world tasks (like Kaggle competitions or industry use cases)</li><li>The reinforcement learning part feels a bit shallow — more of a preview than a full course</li></ul><h3>Who is this for?</h3><ul><li>Anyone looking to get started in AI/ML</li><li>Analysts, QA engineers, and developers thinking of switching fields</li></ul><h3>Final thoughts</h3><p>I liked this specialization. It’s not just theory — you learn how to build models.<br>If you’re starting from scratch (or almost), and want to understand what machine learning is and how to use it — this is a great place to start.</p><p>Next, you’ll want to dive into more <strong>practical, real-world tasks</strong> — and <strong>Kaggle</strong> is perfect for that.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=996a7d02d52d" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>