Files
nexus/sreweekly/articles/497/03-zero-downtime-database-migrations-lessons-from-moving-a-live-productio.html
2026-09-12 17:23:01 +08:00

1 line
167 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html><html data-dpl-id="dpl_9i6KweGTYWNasiv9e3XcXFAn1n9r" lang="en-US" data-color-scheme="light" data-resolved-scheme="light"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="image" imageSrcSet="/_next/image/?url=https%3A%2F%2Fwww.datocms-assets.com%2F55802%2F1694620437-anna-d.jpeg%3Fauto%3Dformat%252Ccompress%26fit%3Dmax%26q%3D80%26w%3D128&amp;w=48&amp;q=75 1x, /_next/image/?url=https%3A%2F%2Fwww.datocms-assets.com%2F55802%2F1694620437-anna-d.jpeg%3Fauto%3Dformat%252Ccompress%26fit%3Dmax%26q%3D80%26w%3D128&amp;w=96&amp;q=75 2x"/><link rel="preload" as="image" imageSrcSet="/_next/image/?url=https%3A%2F%2Fwww.datocms-assets.com%2F55802%2F1762222300-tines-blog-zero-downtime-cover-image.png%3Fauto%3Dformat%252Ccompress%26fit%3Dmax%26q%3D90%26w%3D2400&amp;w=640&amp;q=90 640w, /_next/image/?url=https%3A%2F%2Fwww.datocms-assets.com%2F55802%2F1762222300-tines-blog-zero-downtime-cover-image.png%3Fauto%3Dformat%252Ccompress%26fit%3Dmax%26q%3D90%26w%3D2400&amp;w=750&amp;q=90 750w, /_next/image/?url=https%3A%2F%2Fwww.datocms-assets.com%2F55802%2F1762222300-tines-blog-zero-downtime-cover-image.png%3Fauto%3Dformat%252Ccompress%26fit%3Dmax%26q%3D90%26w%3D2400&amp;w=828&amp;q=90 828w, /_next/image/?url=https%3A%2F%2Fwww.datocms-assets.com%2F55802%2F1762222300-tines-blog-zero-downtime-cover-image.png%3Fauto%3Dformat%252Ccompress%26fit%3Dmax%26q%3D90%26w%3D2400&amp;w=1080&amp;q=90 1080w, /_next/image/?url=https%3A%2F%2Fwww.datocms-assets.com%2F55802%2F1762222300-tines-blog-zero-downtime-cover-image.png%3Fauto%3Dformat%252Ccompress%26fit%3Dmax%26q%3D90%26w%3D2400&amp;w=1200&amp;q=90 1200w, /_next/image/?url=https%3A%2F%2Fwww.datocms-assets.com%2F55802%2F1762222300-tines-blog-zero-downtime-cover-image.png%3Fauto%3Dformat%252Ccompress%26fit%3Dmax%26q%3D90%26w%3D2400&amp;w=1920&amp;q=90 1920w, /_next/image/?url=https%3A%2F%2Fwww.datocms-assets.com%2F55802%2F1762222300-tines-blog-zero-downtime-cover-image.png%3Fauto%3Dformat%252Ccompress%26fit%3Dmax%26q%3D90%26w%3D2400&amp;w=2048&amp;q=90 2048w, /_next/image/?url=https%3A%2F%2Fwww.datocms-assets.com%2F55802%2F1762222300-tines-blog-zero-downtime-cover-image.png%3Fauto%3Dformat%252Ccompress%26fit%3Dmax%26q%3D90%26w%3D2400&amp;w=3840&amp;q=90 3840w" imageSizes="(max-width: 767px) 100vw, (max-width: 1439px) 70vw, 1040px"/><link rel="stylesheet" href="/_next/static/immutable/chunks/132trk_zc3ylu.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/immutable/chunks/1bmd2ryn1r-hv.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/immutable/chunks/3qp9-r2fgklws.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/immutable/chunks/43xkgk33-6316.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/immutable/chunks/3w0wy298bmb96.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/immutable/chunks/27k1lbkm9ytw4.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/immutable/chunks/45auz_6fncqnw.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/immutable/chunks/3a0f_y332k2v1.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/immutable/chunks/2iox-02sx9loa.js"/><script src="/_next/static/immutable/chunks/2n1jkab2r5d63.js" async=""></script><script src="/_next/static/immutable/chunks/1pguxqs8oj5s0.js" async=""></script><script src="/_next/static/immutable/chunks/43a_dvcajw8iz.js" async=""></script><script src="/_next/static/immutable/chunks/turbopack-3xk1_gujkngr6.js" async=""></script><script src="/_next/static/immutable/chunks/3sz971vfk095f.js" async="" crossorigin=""></script><script src="/_next/static/immutable/chunks/0czgivygkxa1z.js" async="" crossorigin=""></script><script src="/_next/static/immutable/chunks/2kpu_ktjkwf7q.js" async="" crossorigin=""></script><script src="/_next/static/immutable/chunks/07hv-f84dhd61.js" async="" crossorigin=""></script><script src="/_next/static/immutable/chunks/0639nwpv6l0pm.js" async="" crossorigin=""></script><script src="/_next/static/immutable/chunks/2826pm12av2bw.js" async="" crossorigin=""></script><script src="/_next/static/immutable/chunks/2_swgt_1o6v9a.js" async="" crossorigin=""></script><script src="/_next/static/immutable/chunks/3pjhofejz57jt.js" async=""></script><script src="/_next/static/immutable/chunks/1q-89ajkqbbkw.js" async=""></script><script src="/_next/static/immutable/chunks/2uho2m59t6w8f.js" async=""></script><script src="/_next/static/immutable/chunks/2ti9pdu9p4n5l.js" async=""></script><script src="/_next/static/immutable/chunks/31qqpjo4d7384.js" async=""></script><script src="/_next/static/immutable/chunks/3zb6qebqmln4d.js" async=""></script><meta name="theme-color" content="#fcf9f5"/><link rel="preload" href="/fonts/Roobert-Regular.woff2" as="font" type="font/woff2" crossorigin="anonymous"/><link rel="preload" href="/fonts/Reckless-Light.woff2" as="font" type="font/woff2" crossorigin="anonymous"/><link rel="preload" href="/fonts/Reckless-Regular.woff2" as="font" type="font/woff2" crossorigin="anonymous"/><title>Lessons from migrating a live production database without downtime</title><meta name="description" content="Discover how Tines engineered a zero-downtime AWS database migration—maintaining 100% availability while improving reliability and scalability."/><meta name="keywords" content="Tines, security workflow automation, No-Code, SOAR"/><link rel="canonical" href="https://www.tines.com/blog/zero-downtime-database-migrations-lessons-from-moving-a-live-production/"/><meta property="og:title" content="Lessons from migrating a live production database without downtime"/><meta property="og:description" content="Discover how Tines engineered a zero-downtime AWS database migration—maintaining 100% availability while improving reliability and scalability."/><meta property="og:image" content="https://www.datocms-assets.com/55802/1762955872-tines-blog-zero-downtime-og-image-1.png"/><meta property="og:type" content="article"/><meta name="twitter:card" content="summary_large_image"/><meta name="twitter:creator" content="@tines_hq"/><meta name="twitter:title" content="Tines | Intelligent workflow platform"/><meta name="twitter:description" content="Build and orchestrate intelligent workflows and agents, on the platform security and IT teams love."/><meta name="twitter:image" content="https://www.tines.com/v5-assets/images/og/tines-home.png"/><link rel="icon" href="/v5-assets/favicon.png"/><script id="color-scheme-init">(function(){var d=document.documentElement,p=location.pathname,R=["/stories/docs","/stories/whats-new","/workflow-capability-matrix","/3b/examples","/3b/product-updates","/library","/stories/five-minute-flows"],F=["/stories/five-minute-flows"],M=["/workflow-capability-matrix"];function tc(c){var m=document.querySelector('meta[name="theme-color"]');if(m)m.setAttribute("content",c)}function on(L){return L.some(function(r){return p===r||p.indexOf(r+"/")===0})}var ok=on(R);if(!ok){d.removeAttribute("data-dark-mode-supported");d.removeAttribute("data-page-theme");d.setAttribute("data-color-scheme","light");d.setAttribute("data-resolved-scheme","light");tc("#fcf9f5");if(on(["/campaigns"])){d.setAttribute("data-campaign-nav","")}return}d.setAttribute("data-dark-mode-supported","");if(on(F)){d.setAttribute("data-color-scheme","dark");d.setAttribute("data-resolved-scheme","dark");tc("#16131e");return}var pt=document.cookie.match(/page-theme=(\w+)/);var mq=/[?&]matrix=on/.test(location.search);if(on(M)&&((pt&&pt[1]==="matrix")||mq)){d.setAttribute("data-page-theme","matrix");d.setAttribute("data-color-scheme","dark");d.setAttribute("data-resolved-scheme","dark");tc("#0b1a17");return}else{d.removeAttribute("data-page-theme")}var cm=document.cookie.match(/preferred-color-scheme=(\w+)/);var s;if(cm){s=cm[1]}else{var sv=d.getAttribute("data-color-scheme");s=sv&&sv!=="light"?sv:"auto"}var r=s==="dark"?"dark":s==="light"?"light":window.matchMedia("(prefers-color-scheme:dark)").matches?"dark":"light";d.setAttribute("data-color-scheme",s);d.setAttribute("data-resolved-scheme",r);tc(r==="dark"?"#16131e":"#fcf9f5");if(on(["/campaigns"])){d.setAttribute("data-campaign-nav","")}})()</script><script src="/_next/static/immutable/chunks/0c0hxoamwjsbw.js" noModule=""></script></head><body><div hidden=""><!--$--><!--/$--></div><script type="application/ld+json">{"@context":"https://schema.org","@type":"Organization","name":"Tines","url":"https://www.tines.com/","@id":"https://www.tines.com/#org","description":"Tines 3B enables every team to build AI agents, apps, and automation in one safe, secure environment — with the control Security and IT need to govern it at scale.","foundingDate":"2018","founders":[{"@type":"Person","name":"Eoin Hinchy"},{"@type":"Person","name":"Thomas Kinsella"}],"sameAs":["https://www.linkedin.com/company/tines-io/","https://twitter.com/tines_hq","https://www.youtube.com/@TinesHQ","https://github.com/tines"],"contactPoint":[{"@type":"ContactPoint","contactType":"customer support","email":"hello@tines.com","availableLanguage":["en"]},{"@type":"ContactPoint","contactType":"analyst relations","email":"analysts@tines.com","availableLanguage":["en"]},{"@type":"ContactPoint","contactType":"media relations","email":"pr@tines.com","availableLanguage":["en"]}],"address":[{"@type":"PostalAddress","streetAddress":"10 Westland Square, Pearse Street","addressLocality":"Dublin","postalCode":"D02 CX36","addressCountry":"IE","name":"Tines Limited"},{"@type":"PostalAddress","streetAddress":"99 High Street, 14th Floor","addressLocality":"Boston","addressRegion":"MA","postalCode":"02111","addressCountry":"US","name":"Tines Technology Inc."}],"areaServed":"Global"}</script><div class="PageBackdrop-module-scss-module__KKf-uW__pageShell"><div id="page-backdrop-portal" class="PageBackdrop-module-scss-module__KKf-uW__slot" aria-hidden="true"></div><a href="#main-content" class="skip-link">Skip to content</a><header id="site-nav" class="SiteNav26-module-scss-module__OUXeaq__header"><div class="SiteNav26-module-scss-module__OUXeaq__blur" aria-hidden="true"></div><div class="SiteNav26-module-scss-module__OUXeaq__backdrop" aria-hidden="true"></div><div class="SiteNav26-module-scss-module__OUXeaq__bar"><div class="SiteNav26-module-scss-module__OUXeaq__left"><div class="SiteNav26-module-scss-module__OUXeaq__logoWrap"><a class="SiteNav26-module-scss-module__OUXeaq__logo" title="Tines" href="/"><svg class="TinesLogo" width="91" height="28" viewBox="0 0 103 32" fill="currentColor"><title>Tines</title><path d="M42.9689 10.5676H40V13.6565H42.9689V20.4041C42.9689 23.7629 45.1282 25.6822 48.427 25.6822H50.8561V22.3534H48.8468C47.4673 22.3534 46.6576 21.5737 46.6576 20.0442V13.6565H50.8561V10.5676H46.6576V6.00928H42.9689V10.5676Z"></path><path d="M52.5751 8.19849H56.6536V4H52.5751V8.19849ZM56.4437 25.6822V10.5676H52.755V25.6822H56.4437Z"></path><path d="M62.3935 25.6822V17.4352C62.3935 15.216 63.833 13.5966 65.9023 13.5966C67.7916 13.5966 69.0511 15.036 69.0511 17.1053V25.6822H72.7398V16.3256C72.7398 12.7868 70.5206 10.3277 67.1018 10.3277C64.9426 10.3277 63.2332 11.2574 62.3935 12.9068V10.5676H58.7048V25.6822H62.3935Z"></path><path d="M85.399 20.644C85.0092 22.0835 83.7796 22.8632 81.8903 22.8632C79.5511 22.8632 77.9917 21.3038 77.7218 18.8747H88.8778C88.9078 18.5748 88.9377 18.1249 88.9377 17.6151C88.9377 14.0164 86.6586 10.3277 81.5904 10.3277C76.6422 10.3277 74.183 14.0764 74.183 18.065C74.183 22.0235 76.9421 25.9221 81.8903 25.9221C85.6089 25.9221 88.4279 23.8529 88.9977 20.644H85.399ZM81.5904 13.0867C83.7796 13.0867 85.0991 14.5262 85.2491 16.4455H77.8117C78.2016 14.1664 79.5211 13.0867 81.5904 13.0867Z"></path><path d="M96.3428 25.9221C99.8815 25.9221 102.191 24.0628 102.191 21.2138C102.191 14.3763 93.7937 17.975 93.7937 14.7062C93.7937 13.6865 94.6634 13.0867 95.9829 13.0867C97.1525 13.0867 98.442 13.8065 98.6519 15.216H102.011C101.861 12.277 99.4316 10.3277 95.8929 10.3277C92.774 10.3277 90.4649 12.1871 90.4649 14.8261C90.4649 21.2138 98.7419 18.005 98.7419 21.3638C98.7419 22.2634 97.7822 22.9832 96.3428 22.9832C94.6933 22.9832 93.5837 22.0835 93.4338 20.644H90.075C90.2549 23.8829 92.6841 25.9221 96.3428 25.9221Z"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M11.8018 0C8.01458 0 4.66599 2.45749 3.53258 6.06868L0.415527 16L3.53258 25.9313C4.66599 29.5425 8.01458 32 11.8018 32H20.1981C23.9853 32 27.3339 29.5425 28.4673 25.9313L31.5844 16L28.4673 6.06868C27.3339 2.45749 23.9853 0 20.1981 0H11.8018ZM20.1982 2.49634C22.8938 2.49634 25.2772 4.24548 26.0839 6.81577L26.8481 9.25062C25.3107 7.98154 23.3639 7.26723 21.3292 7.26707L10.648 7.26679C8.62691 7.26694 6.69264 7.97168 5.16015 9.22481L5.91625 6.81577C6.72297 4.24548 9.10635 2.49634 11.8019 2.49634H20.1982ZM5.73674 12.1986L3.79587 14.7519L28.1811 14.7519L26.2404 12.1989C25.0741 10.6646 23.2571 9.76356 21.329 9.76341H10.5898C8.68349 9.78153 6.89125 10.6798 5.73674 12.1986ZM28.1771 17.2482L26.2403 19.7989C25.0739 21.3349 23.2555 22.237 21.326 22.2368L10.6509 22.2366C8.72137 22.2367 6.90298 21.3346 5.73661 19.7986L3.79996 17.2482L28.1771 17.2482ZM5.9161 25.1842C6.72282 27.7545 9.1062 29.5037 11.8018 29.5037H20.1981C22.8936 29.5037 25.277 27.7545 26.0837 25.1842L26.8485 22.7476C25.3104 24.0182 23.3622 24.7333 21.3258 24.7332L10.651 24.7329C8.6283 24.7331 6.69244 24.0274 5.15921 22.7727L5.9161 25.1842Z"></path></svg></a></div><nav class="SiteNav26-module-scss-module__OUXeaq__navList" aria-label="Primary"><button type="button" class="SiteNav26-module-scss-module__OUXeaq__navItem" aria-expanded="false">Product<span class="SiteNav26-module-scss-module__OUXeaq__navChevron" aria-hidden="true"></span></button><button type="button" class="SiteNav26-module-scss-module__OUXeaq__navItem" aria-expanded="false">Solutions<span class="SiteNav26-module-scss-module__OUXeaq__navChevron" aria-hidden="true"></span></button><a class="SiteNav26-module-scss-module__OUXeaq__navItem" href="/customers/">Customers</a><button type="button" class="SiteNav26-module-scss-module__OUXeaq__navItem" aria-expanded="false">Discover<span class="SiteNav26-module-scss-module__OUXeaq__navChevron" aria-hidden="true"></span></button><a class="SiteNav26-module-scss-module__OUXeaq__navItem" href="/pricing/">Pricing</a></nav></div><div class="SiteNav26-module-scss-module__OUXeaq__right"><a href="https://login.tines.com" target="_blank" rel="noopener noreferrer" class="SiteNav26-module-scss-module__OUXeaq__cta">Log in</a><a href="https://login.tines.com/saml_idp/signup" target="_blank" rel="noopener noreferrer" class="SiteNav26-module-scss-module__OUXeaq__cta">Sign up</a><a class="SiteNav26-module-scss-module__OUXeaq__cta SiteNav26-module-scss-module__OUXeaq__ctaFilled" href="/book-a-demo/">Book a demo</a><div class="SiteNav26-module-scss-module__OUXeaq__searchSlot"><button type="button" class="NavSearch-module-scss-module__vLhI_W__pillButton" aria-label="Open search"><svg viewBox="0 0 18 18" fill="none" aria-hidden="true"><path fill-rule="evenodd" clip-rule="evenodd" d="M8 3C10.7614 3 13 5.23858 13 8C13 9.11033 12.6367 10.1351 12.0244 10.9648C12.0262 10.9666 12.0285 10.9679 12.0303 10.9697L15.5303 14.4697C15.8232 14.7626 15.8232 15.2374 15.5303 15.5303C15.2374 15.8232 14.7626 15.8232 14.4697 15.5303L10.9697 12.0303C10.9679 12.0285 10.9666 12.0262 10.9648 12.0244C10.1351 12.6367 9.11033 13 8 13C5.23858 13 3 10.7614 3 8C3 5.23858 5.23858 3 8 3ZM8 4.5C6.067 4.5 4.5 6.067 4.5 8C4.5 9.933 6.067 11.5 8 11.5C9.933 11.5 11.5 9.933 11.5 8C11.5 6.067 9.933 4.5 8 4.5Z" fill="currentColor"></path></svg></button></div><button type="button" class="SiteNav26-module-scss-module__OUXeaq__hamburger" aria-label="Toggle menu" aria-expanded="false"><svg viewBox="0 0 18 18" fill="none" aria-hidden="true"><path d="M3 5h12M3 9h12M3 13h12" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"></path></svg></button></div></div><div class="SiteNav26-module-scss-module__OUXeaq__panel" hidden=""></div><div class="SiteNav26-module-scss-module__OUXeaq__panel" hidden=""></div><div class="SiteNav26-module-scss-module__OUXeaq__panel" hidden=""></div></header><main id="main-content"><div><div class="AreaNav-module-scss-module__oPsmja__areaNav"><div class="AreaNav-module-scss-module__oPsmja__bar"><div class="AreaNav-module-scss-module__oPsmja__inner AreaNav-module-scss-module__oPsmja__preMeasure"><span class="AreaNav-module-scss-module__oPsmja__pillWrap"><a class="AreaNav-module-scss-module__oPsmja__pill" href="/blog/">Blog</a></span><div class="AreaNav-module-scss-module__oPsmja__right AreaNav-module-scss-module__oPsmja__flushEnd"><span class="AreaNav-module-scss-module__oPsmja__sectionsZone"><nav class="AreaNav-module-scss-module__oPsmja__sections"><a class="AreaNav-module-scss-module__oPsmja__sectionTrigger" href="/blog/">Home</a><span class="AreaNav-module-scss-module__oPsmja__flatGroup"><a class="AreaNav-module-scss-module__oPsmja__sectionTrigger" href="/blog/news/">News</a><a class="AreaNav-module-scss-module__oPsmja__sectionTrigger" href="/blog/tutorials/">Tutorials</a><a class="AreaNav-module-scss-module__oPsmja__sectionTrigger" href="/blog/engineering/">Engineering</a><a class="AreaNav-module-scss-module__oPsmja__sectionTrigger" href="/blog/product-updates/">Product updates</a><a class="AreaNav-module-scss-module__oPsmja__sectionTrigger" href="/blog/inside-tines/">Inside Tines</a><a class="AreaNav-module-scss-module__oPsmja__sectionTrigger" href="/blog/tines-by-tines/">Tines × Tines</a></span><div class="AreaNav-module-scss-module__oPsmja__section AreaNav-module-scss-module__oPsmja__collapsible"><button type="button" class="AreaNav-module-scss-module__oPsmja__sectionTrigger" aria-expanded="false">Categories<span class="AreaNav-module-scss-module__oPsmja__caret" aria-hidden="true"></span></button></div><a class="AreaNav-module-scss-module__oPsmja__sectionTrigger" href="/blog/guides/">Guides</a><a class="AreaNav-module-scss-module__oPsmja__sectionTrigger" href="/blog/articles/">All articles</a></nav></span><div class="AreaNav-module-scss-module__oPsmja__rightFixed"></div></div></div></div></div><div class="blog-module-scss-module__RlMr6a__contentColumn"><script type="application/ld+json">{"@context":"https://schema.org","@type":"BlogPosting","headline":"Zero downtime database migrations: Lessons from moving a live production database","description":"What went right (and wrong) during a live tenant migration with “zero downtime.\"","datePublished":"2025-11-04T05:00:00+00:00","dateModified":"2025-11-12T13:58:21+00:00","url":"https://www.tines.com/blog/zero-downtime-database-migrations-lessons-from-moving-a-live-production/","image":"https://www.datocms-assets.com/55802/1762222300-tines-blog-zero-downtime-cover-image.png?auto=format%2Ccompress&fit=max&q=90&w=2400","author":[{"@type":"Person","name":"Anna Dowling"}],"publisher":{"@type":"Organization","name":"Tines","url":"https://www.tines.com"}}</script><div style="display:block;width:var(--xl);height:var(--xl);flex:0 0 var(--xl);user-select:none"></div><header><h1 class="ArticlePageHeader-module-scss-module__szLeSa__title">Zero downtime database migrations: Lessons from moving a live production database</h1><div class="ArticlePageHeader-module-scss-module__szLeSa__authorInfo"><div class="ArticlePageHeader-module-scss-module__szLeSa__authors"><div class="ArticlePageHeader-module-scss-module__szLeSa__authorPhotos"><img alt="" loading="eager" width="42" height="42" decoding="async" data-nimg="1" class="ArticlePageHeader-module-scss-module__szLeSa__authorPhoto" style="color:transparent" srcSet="/_next/image/?url=https%3A%2F%2Fwww.datocms-assets.com%2F55802%2F1694620437-anna-d.jpeg%3Fauto%3Dformat%252Ccompress%26fit%3Dmax%26q%3D80%26w%3D128&amp;w=48&amp;q=75 1x, /_next/image/?url=https%3A%2F%2Fwww.datocms-assets.com%2F55802%2F1694620437-anna-d.jpeg%3Fauto%3Dformat%252Ccompress%26fit%3Dmax%26q%3D80%26w%3D128&amp;w=96&amp;q=75 2x" src="/_next/image/?url=https%3A%2F%2Fwww.datocms-assets.com%2F55802%2F1694620437-anna-d.jpeg%3Fauto%3Dformat%252Ccompress%26fit%3Dmax%26q%3D80%26w%3D128&amp;w=96&amp;q=75"/></div><div><span class="ArticlePageHeader-module-scss-module__szLeSa__authorName"><span class="ArticlePageHeader-module-scss-module__szLeSa__writtenBy">Written by </span><span>Anna Dowling</span></span></div></div><div class="ArticlePageHeader-module-scss-module__szLeSa__meta"><time dateTime="2025-11-04T05:00:00.000Z">Published on <!-- -->November 4, 2025</time><div class="ArticlePageHeader-module-scss-module__szLeSa__categoryPills"><a class="ArticlePageHeader-module-scss-module__szLeSa__categoryPill" href="/blog/engineering/">Engineering</a></div></div></div></header><div style="display:block;width:var(--lg);height:var(--lg);flex:0 0 var(--lg);user-select:none"></div><div class="page-module-scss-module__Z5AZta__articleLayout page-module-scss-module__Z5AZta__noToc"><aside class="page-module-scss-module__Z5AZta__articleAside"><a href="https://login.tines.com/saml_idp/signup" class="DefaultSidebarContent-module-scss-module__nlQFtG__card"><div class="DefaultSidebarContent-module-scss-module__nlQFtG__text"><h3 class="DefaultSidebarContent-module-scss-module__nlQFtG__heading">Sign up for Tines 3B Explore Edition</h3><p class="DefaultSidebarContent-module-scss-module__nlQFtG__linkArrow">Get started for free</p></div><img alt="" aria-hidden="true" loading="lazy" width="90" height="90" decoding="async" data-nimg="1" class="DefaultSidebarContent-module-scss-module__nlQFtG__icon" style="color:transparent" src="/v5-assets/images/usp/blossoming-transformation.svg?dpl=dpl_9i6KweGTYWNasiv9e3XcXFAn1n9r"/></a></aside><div class="page-module-scss-module__Z5AZta__articleMain"><div class="page-module-scss-module__Z5AZta__coverImage"><img alt="Cover image for Zero downtime database migrations: Lessons from moving a live production database" loading="eager" width="2400" height="1260" decoding="async" data-nimg="1" style="color:transparent" sizes="(max-width: 767px) 100vw, (max-width: 1439px) 70vw, 1040px" srcSet="/_next/image/?url=https%3A%2F%2Fwww.datocms-assets.com%2F55802%2F1762222300-tines-blog-zero-downtime-cover-image.png%3Fauto%3Dformat%252Ccompress%26fit%3Dmax%26q%3D90%26w%3D2400&amp;w=640&amp;q=90 640w, /_next/image/?url=https%3A%2F%2Fwww.datocms-assets.com%2F55802%2F1762222300-tines-blog-zero-downtime-cover-image.png%3Fauto%3Dformat%252Ccompress%26fit%3Dmax%26q%3D90%26w%3D2400&amp;w=750&amp;q=90 750w, /_next/image/?url=https%3A%2F%2Fwww.datocms-assets.com%2F55802%2F1762222300-tines-blog-zero-downtime-cover-image.png%3Fauto%3Dformat%252Ccompress%26fit%3Dmax%26q%3D90%26w%3D2400&amp;w=828&amp;q=90 828w, /_next/image/?url=https%3A%2F%2Fwww.datocms-assets.com%2F55802%2F1762222300-tines-blog-zero-downtime-cover-image.png%3Fauto%3Dformat%252Ccompress%26fit%3Dmax%26q%3D90%26w%3D2400&amp;w=1080&amp;q=90 1080w, /_next/image/?url=https%3A%2F%2Fwww.datocms-assets.com%2F55802%2F1762222300-tines-blog-zero-downtime-cover-image.png%3Fauto%3Dformat%252Ccompress%26fit%3Dmax%26q%3D90%26w%3D2400&amp;w=1200&amp;q=90 1200w, /_next/image/?url=https%3A%2F%2Fwww.datocms-assets.com%2F55802%2F1762222300-tines-blog-zero-downtime-cover-image.png%3Fauto%3Dformat%252Ccompress%26fit%3Dmax%26q%3D90%26w%3D2400&amp;w=1920&amp;q=90 1920w, /_next/image/?url=https%3A%2F%2Fwww.datocms-assets.com%2F55802%2F1762222300-tines-blog-zero-downtime-cover-image.png%3Fauto%3Dformat%252Ccompress%26fit%3Dmax%26q%3D90%26w%3D2400&amp;w=2048&amp;q=90 2048w, /_next/image/?url=https%3A%2F%2Fwww.datocms-assets.com%2F55802%2F1762222300-tines-blog-zero-downtime-cover-image.png%3Fauto%3Dformat%252Ccompress%26fit%3Dmax%26q%3D90%26w%3D2400&amp;w=3840&amp;q=90 3840w" src="/_next/image/?url=https%3A%2F%2Fwww.datocms-assets.com%2F55802%2F1762222300-tines-blog-zero-downtime-cover-image.png%3Fauto%3Dformat%252Ccompress%26fit%3Dmax%26q%3D90%26w%3D2400&amp;w=3840&amp;q=90"/></div><article class="Article-module-scss-module__ZE_SmG__article page-module-scss-module__Z5AZta__articleContent"><h2 id="introduction"><a href="#introduction"><span><strong>Introduction</strong></span><svg aria-hidden="true" data-heading-link-icon="true" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M13.5438 10.456C12.7247 9.63723 11.614 9.17728 10.4558 9.17728C9.29766 9.17728 8.18692 9.63723 7.36781 10.456L4.27881 13.544C3.45969 14.3631 2.99951 15.4741 2.99951 16.6325C2.99951 17.7909 3.45969 18.9019 4.27881 19.721C5.09793 20.5401 6.2089 21.0003 7.36731 21.0003C8.52572 21.0003 9.63669 20.5401 10.4558 19.721L11.9998 18.177" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path><path d="M10.4561 13.544C11.2752 14.3628 12.3859 14.8227 13.5441 14.8227C14.7022 14.8227 15.8129 14.3628 16.6321 13.544L19.7211 10.456C20.5402 9.63687 21.0004 8.52591 21.0004 7.36749C21.0004 6.20908 20.5402 5.09811 19.7211 4.27899C18.9019 3.45987 17.791 2.99969 16.6326 2.99969C15.4741 2.99969 14.3632 3.45987 13.5441 4.27899L12.0001 5.82299" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg></a></h2><p>If you&#x27;ve ever been involved in a major database migration, you know just how complex and honestly, nerve-wracking they can be. At <a href="/">Tines</a>, we recently faced the challenge of migrating a customer&#x27;s dedicated tenant by moving all the customer’s <a href="/product/">critical workloads</a> running on Tines between two different AWS <a href="https://aws.amazon.com/about-aws/global-infrastructure/regions_az/">Regions</a>. All while maintaining 100% system availability. This wasn&#x27;t just about moving live data, it was about keeping our automation platform running without missing a single <a href="/docs/actions/types/webhook/">webhook</a> or disrupting critical customer <a href="/docs/stories/">workflows</a>.</p><p>The project&#x27;s scope grew beyond initial expectations, requiring improvements to our application and infrastructure. While we encountered several unexpected challenges, from handling gzipped content in <a href="https://aws.amazon.com/lambda/edge/">Lambda@Edge</a> to managing complex database connection states, these obstacles led to valuable improvements in our system&#x27;s architecture as well as many learnings.</p><h2 id="more-than-just-moving-live-data"><a href="#more-than-just-moving-live-data"><span><strong>More than just moving live data</strong></span><svg aria-hidden="true" data-heading-link-icon="true" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M13.5438 10.456C12.7247 9.63723 11.614 9.17728 10.4558 9.17728C9.29766 9.17728 8.18692 9.63723 7.36781 10.456L4.27881 13.544C3.45969 14.3631 2.99951 15.4741 2.99951 16.6325C2.99951 17.7909 3.45969 18.9019 4.27881 19.721C5.09793 20.5401 6.2089 21.0003 7.36731 21.0003C8.52572 21.0003 9.63669 20.5401 10.4558 19.721L11.9998 18.177" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path><path d="M10.4561 13.544C11.2752 14.3628 12.3859 14.8227 13.5441 14.8227C14.7022 14.8227 15.8129 14.3628 16.6321 13.544L19.7211 10.456C20.5402 9.63687 21.0004 8.52591 21.0004 7.36749C21.0004 6.20908 20.5402 5.09811 19.7211 4.27899C18.9019 3.45987 17.791 2.99969 16.6326 2.99969C15.4741 2.99969 14.3632 3.45987 13.5441 4.27899L12.0001 5.82299" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg></a></h2><p>We recently had a customer (we refer to them as “tenants” in our system) deployed on our cloud infrastructure in one AWS region, and they wanted to migrate to another region closer to them. This tenant is one of many we host, but each one can process millions of mission-critical workloads per day via Tines application.</p><p>Before diving into the challenges, it’s worth explaining briefly what a “busy Tines instance” looks like. A customer’s Tines application is constantly performing <a href="/docs/actions/">action runs</a>. These are automated tasks triggered by external events like incoming webhooks or <a href="https://explained.tines.com/en/articles/7843861-how-do-i-schedule-my-story">scheduled</a> stories which can produce a high volume of data. This level of activity is exactly why the migration had to be seamless: any disruption could impact thousands (or even millions) of automated processes that our customers rely on.</p><h3 id="the-challenge"><a href="#the-challenge"><span><strong>The challenge?</strong></span><svg aria-hidden="true" data-heading-link-icon="true" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M13.5438 10.456C12.7247 9.63723 11.614 9.17728 10.4558 9.17728C9.29766 9.17728 8.18692 9.63723 7.36781 10.456L4.27881 13.544C3.45969 14.3631 2.99951 15.4741 2.99951 16.6325C2.99951 17.7909 3.45969 18.9019 4.27881 19.721C5.09793 20.5401 6.2089 21.0003 7.36731 21.0003C8.52572 21.0003 9.63669 20.5401 10.4558 19.721L11.9998 18.177" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path><path d="M10.4561 13.544C11.2752 14.3628 12.3859 14.8227 13.5441 14.8227C14.7022 14.8227 15.8129 14.3628 16.6321 13.544L19.7211 10.456C20.5402 9.63687 21.0004 8.52591 21.0004 7.36749C21.0004 6.20908 20.5402 5.09811 19.7211 4.27899C18.9019 3.45987 17.791 2.99969 16.6326 2.99969C15.4741 2.99969 14.3632 3.45987 13.5441 4.27899L12.0001 5.82299" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg></a></h3><p>Moving this specific tenant&#x27;s deployment to a new region without dropping any data or disrupting any of these workloads. Oh, and downtime? That&#x27;s not an option.</p><p>Let me clarify what that actually means in practice:</p><ul><li><p><em>It’s acceptable if some requests or event deliveries are delayed during the migration.</em></p></li><li><p><em>It’s acceptable if web response times are temporarily slower than usual.</em></p></li><li><p><em>It’s not acceptable to lose or drop any events permanently.</em></p></li><li><p><em>It’s not acceptable to lose any database operations i.e. INSERTs, UPDATEs, or DELETEs.</em></p></li><li><p><em>And above all, we must maintain our data integrity guarantees at all times.</em></p></li></ul><p>For context, the diagram below provides a high-level overview of our deployment architecture before the migration.</p><p>As shown, each tenant’s environment consists of a dedicated <a href="https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html">AWS Application Load Balancer</a> (ALB) routing traffic to <a href="https://aws.amazon.com/ecs/">ECS</a> web and worker services running Tines application, which in turn interact with <a href="https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Aurora.AuroraPostgreSQL.html">AWS Aurora PostgreSQL</a> for database storage and AWS <a href="https://aws.amazon.com/elasticache/">ElastiCache</a> for Redis to support fast, in-memory operations. This setup is designed to handle high volumes of mission-critical workloads with reliability and scalability.</p><figure class="ImageBlock-module-scss-module__0Ef4dq__imageBlock"><img src="https://www.datocms-assets.com/55802/1762222651-zdt-pre-state-drawio.png?auto=format%2Ccompress&amp;fit=max&amp;q=90&amp;w=2000&amp;auto=format,compress&amp;q=90&amp;w=2000&amp;fit=max" alt="" width="491" height="701" loading="lazy"/><figcaption class="ImageBlock-module-scss-module__0Ef4dq__imageCaption"><p>Tines AWS deployment architecture pre-migration</p></figcaption></figure><h2 id="building-a-robust-migration-framework"><a href="#building-a-robust-migration-framework"><span><strong>Building a robust migration framework</strong></span><svg aria-hidden="true" data-heading-link-icon="true" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M13.5438 10.456C12.7247 9.63723 11.614 9.17728 10.4558 9.17728C9.29766 9.17728 8.18692 9.63723 7.36781 10.456L4.27881 13.544C3.45969 14.3631 2.99951 15.4741 2.99951 16.6325C2.99951 17.7909 3.45969 18.9019 4.27881 19.721C5.09793 20.5401 6.2089 21.0003 7.36731 21.0003C8.52572 21.0003 9.63669 20.5401 10.4558 19.721L11.9998 18.177" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path><path d="M10.4561 13.544C11.2752 14.3628 12.3859 14.8227 13.5441 14.8227C14.7022 14.8227 15.8129 14.3628 16.6321 13.544L19.7211 10.456C20.5402 9.63687 21.0004 8.52591 21.0004 7.36749C21.0004 6.20908 20.5402 5.09811 19.7211 4.27899C18.9019 3.45987 17.791 2.99969 16.6326 2.99969C15.4741 2.99969 14.3632 3.45987 13.5441 4.27899L12.0001 5.82299" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg></a></h2><p>The diagram below illustrates how requests, data, and replication traffic flowed between the <code>source</code> and <code>destination</code> customer deployments during the migration. As you will see, many components worked together to keep everything in sync and available. In the following sections, we’ll discuss each part of this framework and how we implemented it in detail.</p><figure class="ImageBlock-module-scss-module__0Ef4dq__imageBlock"><img src="https://www.datocms-assets.com/55802/1762222692-zdtmigration-drawio.png?auto=format%2Ccompress&amp;fit=max&amp;q=90&amp;w=2000&amp;auto=format,compress&amp;q=90&amp;w=2000&amp;fit=max" alt="" width="1031" height="1371" loading="lazy"/><figcaption class="ImageBlock-module-scss-module__0Ef4dq__imageCaption"><p>Tines AWS deployment architecture for migration</p></figcaption></figure><h3 id="smart-request-routing-with-lambdaedge"><a href="#smart-request-routing-with-lambdaedge"><span><strong>Smart request routing with Lambda@Edge</strong></span><svg aria-hidden="true" data-heading-link-icon="true" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M13.5438 10.456C12.7247 9.63723 11.614 9.17728 10.4558 9.17728C9.29766 9.17728 8.18692 9.63723 7.36781 10.456L4.27881 13.544C3.45969 14.3631 2.99951 15.4741 2.99951 16.6325C2.99951 17.7909 3.45969 18.9019 4.27881 19.721C5.09793 20.5401 6.2089 21.0003 7.36731 21.0003C8.52572 21.0003 9.63669 20.5401 10.4558 19.721L11.9998 18.177" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path><path d="M10.4561 13.544C11.2752 14.3628 12.3859 14.8227 13.5441 14.8227C14.7022 14.8227 15.8129 14.3628 16.6321 13.544L19.7211 10.456C20.5402 9.63687 21.0004 8.52591 21.0004 7.36749C21.0004 6.20908 20.5402 5.09811 19.7211 4.27899C18.9019 3.45987 17.791 2.99969 16.6326 2.99969C15.4741 2.99969 14.3632 3.45987 13.5441 4.27899L12.0001 5.82299" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg></a></h3><p>One of our biggest challenges was handling incoming requests during the migration. We needed a solution that could:</p><ul><li><p>Route requests to the appropriate environment, specifically, the new region deployment while ensuring no requests are dropped.</p></li><li><p>Handle failover scenarios</p></li><li><p>Process both normal and gzipped content</p></li><li><p>Maintain session consistency</p></li></ul><p>We used AWS <a href="https://aws.amazon.com/lambda/edge/">Lambda@Edge</a> to solve for this missing connector. Our implementation became more sophisticated than initially planned. We discovered that handling gzipped content and dealing with <a href="https://aws.amazon.com/cloudfront/">CloudFront</a> limitations required special attention.</p><p><strong>Gzipped content</strong></p><p>One challenge we faced was that when our origin server (the Tines app serving GraphQL requests) returned gzipped content, the Lambda@Edge function received the compressed data, which isn’t directly readable or modifiable. This issue did not affect our /webhooks endpoint, as it did not return gzipped responses. To inspect or modify the response body, we had to detect if the content was gzipped and then decompress it within the Lambda function using Node’s <a href="https://nodejs.org/api/zlib.html"><code>zlib</code></a> library. This added complexity, especially since we also needed to ensure that the final response sent to the client had the correct headers and wasn’t double-compressed or missing the appropriate Content-Encoding.</p><p><strong>Header filtering</strong></p><p>CloudFront imposes restrictions on which headers can be set or forwarded by Lambda@Edge. Some headers, like <code>Content-Length, Transfer-Encoding</code>, and certain connection-related headers, are either managed by CloudFront or disallowed entirely. We had to carefully filter out these headers in our Lambda response to avoid errors or unexpected behavior. This required extra logic to sanitize the response headers and ensure compatibility with CloudFront’s requirements.</p><div class="StructuredTextBlock-module-scss-module__LuuGDq__structuredTextBlock" data-backdrop="Canvas"><p> 💡 See gist of the Lambda@Edge Function here: </p><p><a href="https://gist.github.com/annadowling/4d605be38706184060cd837a37a77397">https://gist.github.com/annadowling/4d605be38706184060cd837a37a77397</a></p></div><h3 id="database-and-application-synchronization-strategy"><a href="#database-and-application-synchronization-strategy"><span><strong>Database and application synchronization strategy</strong></span><svg aria-hidden="true" data-heading-link-icon="true" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M13.5438 10.456C12.7247 9.63723 11.614 9.17728 10.4558 9.17728C9.29766 9.17728 8.18692 9.63723 7.36781 10.456L4.27881 13.544C3.45969 14.3631 2.99951 15.4741 2.99951 16.6325C2.99951 17.7909 3.45969 18.9019 4.27881 19.721C5.09793 20.5401 6.2089 21.0003 7.36731 21.0003C8.52572 21.0003 9.63669 20.5401 10.4558 19.721L11.9998 18.177" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path><path d="M10.4561 13.544C11.2752 14.3628 12.3859 14.8227 13.5441 14.8227C14.7022 14.8227 15.8129 14.3628 16.6321 13.544L19.7211 10.456C20.5402 9.63687 21.0004 8.52591 21.0004 7.36749C21.0004 6.20908 20.5402 5.09811 19.7211 4.27899C18.9019 3.45987 17.791 2.99969 16.6326 2.99969C15.4741 2.99969 14.3632 3.45987 13.5441 4.27899L12.0001 5.82299" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg></a></h3><p>When it came to migrating the customer’s database, we needed a way to keep data in sync between the old and new regions with minimal disruption. This is where a **blue/green deployment** approach came into play: we maintained both the old (“blue”) and new (“green”) environments in parallel, allowing us to switch traffic with minimal downtime and risk. To enable this, we used <a href="https://www.postgresql.org/docs/current/logical-replication.html"><strong>Logical Replication</strong></a>. Logical replication in PostgreSQL allows you to replicate data and stream changes (like INSERTs, UPDATEs, and DELETEs) from one database to another in near real-time, making it possible to keep two databases synchronized even as new data is being written. Below, we’ll cover the three key areas we instrumented for the database migration.</p><p><strong>Logical replication with pg_easy_replicate</strong></p><p>We used <a href="https://github.com/shayonj/pg_easy_replicate">pg_easy_replicate</a>, an open-source tool that simplifies setting up and managing logical replication. Out of the box, it handles a lot of the heavy lifting for replication setup very well.</p><figure class="ImageBlock-module-scss-module__0Ef4dq__imageBlock"><img src="https://www.datocms-assets.com/55802/1762222860-image-2.png?auto=format%2Ccompress&amp;fit=max&amp;q=90&amp;w=2000&amp;auto=format,compress&amp;q=90&amp;w=2000&amp;fit=max" alt="" width="541" height="586" loading="lazy"/></figure><p>As we got deeper into our migration implementation, we realized we needed some additional features to meet our requirements. This led us to contribute several enhancements back to the project, including:</p><ul><li><p>Advanced logging and debugging capabilities in <code>pg_easy_replicate</code> for better visibility into the replication process</p></li><li><p>Development of the <a href="https://github.com/shayonj/pg_easy_replicate?tab=readme-ov-file#exclude-tables-from-replication"><code>[exclude-tables]</code></a>command to allow selective table replication, giving teams more control over their migration process</p></li><li><p>Creation of the <a href="https://github.com/shayonj/pg_easy_replicate?tab=readme-ov-file#notify"><code>[notify]</code></a> command to send replication statistics to an endpoint on an interval, enabling real-time monitoring</p></li></ul><p>These enhancements not only supported our migration but also improved the tool for the broader PostgreSQL community. The ability to exclude specific tables and monitor replication progress in real-time proved invaluable for managing our large-scale migration with confidence.</p><p><strong>At the database level, here’s how logical replication worked in practice during our migration:</strong></p><p><code><strong>Source database</strong></code></p><p>This query shows the replication slot is active, and the increase in retained WAL is expected with ongoing activity.</p><figure class="ImageBlock-module-scss-module__0Ef4dq__imageBlock"><img src="https://www.datocms-assets.com/55802/1762222901-queryupdate-drawio.png?auto=format%2Ccompress&amp;fit=max&amp;q=90&amp;w=2000&amp;auto=format,compress&amp;q=90&amp;w=2000&amp;fit=max" alt="" width="897" height="205" loading="lazy"/><figcaption class="ImageBlock-module-scss-module__0Ef4dq__imageCaption"><p>psql query and output for source database logical replication</p></figcaption></figure><p><code><strong>Target database</strong></code></p><p>On the target, this query shows the received LSN and latest end LSN match, with minimal delay—showing logical replication running smoothly between regions.</p><figure class="ImageBlock-module-scss-module__0Ef4dq__imageBlock"><img src="https://www.datocms-assets.com/55802/1762222957-query2-drawio.png?auto=format%2Ccompress&amp;fit=max&amp;q=90&amp;w=2000&amp;auto=format,compress&amp;q=90&amp;w=2000&amp;fit=max" alt="" width="1157" height="97" loading="lazy"/><figcaption class="ImageBlock-module-scss-module__0Ef4dq__imageCaption"><p>psql query and output for target database logical replication</p></figcaption></figure><p><strong>2. PostgreSQL BLOB data synchronization</strong></p><p>A challenge emerged when we discovered PostgreSQL 14.10 <a href="https://www.postgresql.org/docs/current/logical-replication-restrictions.html#LOGICAL-REPLICATION-RESTRICTIONS">doesn&#x27;t support</a> Large Object(BLOBs) replication natively. While logical replication handled most of our data effectively, BLOBs weren&#x27;t supported yet and Tines stores customer data such as saved images and case attachments.</p><p>This required us to develop a custom synchronization solution using a Ruby <a href="https://sidekiq.org/">Sidekiq</a> job. The real complexity involved managing database connections during that sync process.</p><p>We implemented a connection switching mechanism to handle the export of the blob data from the source database and import into the target database:</p><div class="StructuredTextBlock-module-scss-module__LuuGDq__structuredTextBlock" data-backdrop="Canvas"><p>💡 <strong>See gist of the </strong><code><strong>DbConnectionSwitcher</strong></code><strong> class here:</strong> <a href="https://gist.github.com/annadowling/90db3a33a50cb01e2a293ff5c37702d4">https://gist.github.com/annadowling/90db3a33a50cb01e2a293ff5c37702d4</a></p></div><p><strong>3. Reliability improvements for Tines features</strong></p><p>A critical part of ensuring zero data loss during migration was improving the Tines <a href="/docs/actions/types/event-transformation/">Event Transform action</a> runs. Specifically the mechanisms for <code>Throttle</code> and <code>Delay</code> even transformation mode <strong>types</strong>. Previously, these features relied on our <a href="https://github.com/redis/redis">Redis</a> setup for keeping track of the referential IDs which would be read when enqueuing action runs. For the migration, we moved this logic to database-backed persistence using <a href="https://www.postgresql.org/">PostgreSQL</a> tables with <a href="https://guides.rubyonrails.org/active_record_basics.html">ActiveRecord</a> in the <a href="https://rubyonrails.org/">Rails</a> application.</p><p>It’s important to clarify that this change was about how Tines models and tracks references for delayed and throttled events. We treat data in Redis as ephemeral. Any loss of data inside Redis shouldn’t impact application performance or reliability. This is why we didn’t want to invest resources into replicating our Redis data across regions. For us, the priority was to ensure that no event references were lost, and that the timing and sequencing of delayed or throttled events would be preserved, even as we moved between regions.</p><p>By enhancing the <a href="/docs/actions/types/event-transformation/delay/">delay mode</a> &amp; <a href="/docs/actions/types/event-transformation/throttle/">throttle mode</a> to use database persistence, we were able to:</p><ul><li><p>Prevent the loss of any delayed or throttled events during the transition.</p></li><li><p>Maintain the precise timing and intervals for scheduled and rate-limited event emission, even in the middle of a migration.</p></li></ul><p>This approach gave us the reliability we needed for the migration, without the risk of losing critical event data.</p><h3 id="vpc-peering"><a href="#vpc-peering"><span>VPC Peering</span><svg aria-hidden="true" data-heading-link-icon="true" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M13.5438 10.456C12.7247 9.63723 11.614 9.17728 10.4558 9.17728C9.29766 9.17728 8.18692 9.63723 7.36781 10.456L4.27881 13.544C3.45969 14.3631 2.99951 15.4741 2.99951 16.6325C2.99951 17.7909 3.45969 18.9019 4.27881 19.721C5.09793 20.5401 6.2089 21.0003 7.36731 21.0003C8.52572 21.0003 9.63669 20.5401 10.4558 19.721L11.9998 18.177" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path><path d="M10.4561 13.544C11.2752 14.3628 12.3859 14.8227 13.5441 14.8227C14.7022 14.8227 15.8129 14.3628 16.6321 13.544L19.7211 10.456C20.5402 9.63687 21.0004 8.52591 21.0004 7.36749C21.0004 6.20908 20.5402 5.09811 19.7211 4.27899C18.9019 3.45987 17.791 2.99969 16.6326 2.99969C15.4741 2.99969 14.3632 3.45987 13.5441 4.27899L12.0001 5.82299" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg></a></h3><p>A pre-requisite for configuring cross-region logical replication was establishing secure, low-latency network connectivity between the source and destination AWS environments. Logical replication in PostgreSQL requires the source and target databases to communicate directly, which isn’t possible by default when they’re in separate VPCs. To solve this, we leveraged AWS’s <a href="https://docs.aws.amazon.com/vpc/latest/userguide/vpc-peering.html"><strong>VPC peering</strong></a>.</p><p>Instead of manually configuring everything, we built a reusable VPC peering setup with AWS CDK. This automated the whole process and gave us a template we can re-use for future cases.</p><p>Here’s how our CDK implementation works:</p><ul><li><p><strong>Bidirectional routing:</strong> The stack configures peering and updates route tables on both the requester (source) and provider (destination) sides, ensuring traffic can flow in both directions as needed for replication and application communication.</p></li><li><p><strong>Security group rules:</strong> We programmatically add security group ingress rules to allow PostgreSQL traffic (port 5432) between the peered VPCs.</p></li><li><p><strong>Parameterization:</strong> The configuration is driven by parameters (VPC IDs, CIDRs, security group IDs, etc.), making it easy to adapt for any customer deployment.</p></li></ul><p></p><div class="StructuredTextBlock-module-scss-module__LuuGDq__structuredTextBlock" data-backdrop="Canvas"><p>💡 See gist of CDK VPCPeeringConfiguration here: <a href="https://gist.github.com/annadowling/6c5abcc6afbf9be45c52562e109aebb6">https://gist.github.com/annadowling/6c5abcc6afbf9be45c52562e109aebb6</a></p></div><h2 id="migration-automation-with-tines"><a href="#migration-automation-with-tines"><span><strong>Migration automation with Tines</strong></span><svg aria-hidden="true" data-heading-link-icon="true" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M13.5438 10.456C12.7247 9.63723 11.614 9.17728 10.4558 9.17728C9.29766 9.17728 8.18692 9.63723 7.36781 10.456L4.27881 13.544C3.45969 14.3631 2.99951 15.4741 2.99951 16.6325C2.99951 17.7909 3.45969 18.9019 4.27881 19.721C5.09793 20.5401 6.2089 21.0003 7.36731 21.0003C8.52572 21.0003 9.63669 20.5401 10.4558 19.721L11.9998 18.177" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path><path d="M10.4561 13.544C11.2752 14.3628 12.3859 14.8227 13.5441 14.8227C14.7022 14.8227 15.8129 14.3628 16.6321 13.544L19.7211 10.456C20.5402 9.63687 21.0004 8.52591 21.0004 7.36749C21.0004 6.20908 20.5402 5.09811 19.7211 4.27899C18.9019 3.45987 17.791 2.99969 16.6326 2.99969C15.4741 2.99969 14.3632 3.45987 13.5441 4.27899L12.0001 5.82299" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg></a></h2><p>Rather than rely on manual processes, we automated the migration through a Tines story. The automation workflow managed four critical phases:</p><ul><li><p><strong>Setup</strong> - Resource validation and health check</p></li><li><p><strong>Replication</strong> - Database synchronization and monitoring</p></li><li><p><strong>Switchover</strong> - Traffic routing and environment transition</p></li><li><p><strong>Post-migration</strong> - Cleanup and verification</p></li></ul><p>Each phase included comprehensive error handling, with real-time monitoring through Slack notifications.</p><figure class="ImageBlock-module-scss-module__0Ef4dq__imageBlock"><img src="https://www.datocms-assets.com/55802/1762223107-zero-downtime-dedicated-tenant-migration-storyboard.png?auto=format%2Ccompress&amp;fit=max&amp;q=90&amp;w=2000&amp;auto=format,compress&amp;q=90&amp;w=2000&amp;fit=max" alt="" width="5220" height="5332" loading="lazy"/><figcaption class="ImageBlock-module-scss-module__0Ef4dq__imageCaption"><p>Migration story in Tines</p></figcaption></figure><h3 id="the-art-of-switching-over-orchestrating-failover"><a href="#the-art-of-switching-over-orchestrating-failover"><span><strong>The art of switching over: orchestrating failover</strong></span><svg aria-hidden="true" data-heading-link-icon="true" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M13.5438 10.456C12.7247 9.63723 11.614 9.17728 10.4558 9.17728C9.29766 9.17728 8.18692 9.63723 7.36781 10.456L4.27881 13.544C3.45969 14.3631 2.99951 15.4741 2.99951 16.6325C2.99951 17.7909 3.45969 18.9019 4.27881 19.721C5.09793 20.5401 6.2089 21.0003 7.36731 21.0003C8.52572 21.0003 9.63669 20.5401 10.4558 19.721L11.9998 18.177" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path><path d="M10.4561 13.544C11.2752 14.3628 12.3859 14.8227 13.5441 14.8227C14.7022 14.8227 15.8129 14.3628 16.6321 13.544L19.7211 10.456C20.5402 9.63687 21.0004 8.52591 21.0004 7.36749C21.0004 6.20908 20.5402 5.09811 19.7211 4.27899C18.9019 3.45987 17.791 2.99969 16.6326 2.99969C15.4741 2.99969 14.3632 3.45987 13.5441 4.27899L12.0001 5.82299" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg></a></h3><p>One of the most critical parts of our migration was the actual “failover” to the target deployment / database. This was the moment when we switched all application traffic from the old region to the new one. This was triggered via the story using the <code>switchover</code> command input. In our context, failover meant deliberately making the source deployment unavailable for writes, so that all new requests would be routed to the destination (new region) deployment.</p><p>To achieve this, we put the source database into <strong>READ ONLY mode</strong> using the <a href="https://github.com/shayonj/pg_easy_replicate?tab=readme-ov-file#performing-switchover">pg_easy_replicate switchover command</a>. This meant any write attempts (INSERT, UPDATE, DELETE) would fail, causing the application to return 5xx errors for those operations.</p><p>Our Lambda@Edge function was designed to detect these 5xx responses from the source environment. When it saw them, it would automatically reroute requests to the new region deployment.</p><p>This approach ensured a clean cutover by preventing new data from being written to the old deployment, while Lambda@Edge automated the requests failover. As a result, downtime was minimal and the transition appeared nearly instantaneous to users.</p><figure class="ImageBlock-module-scss-module__0Ef4dq__imageBlock"><img src="https://www.datocms-assets.com/55802/1762223261-switchover-drawio.png?auto=format%2Ccompress&amp;fit=max&amp;q=90&amp;w=2000&amp;auto=format,compress&amp;q=90&amp;w=2000&amp;fit=max" alt="" width="1411" height="361" loading="lazy"/><figcaption class="ImageBlock-module-scss-module__0Ef4dq__imageCaption"><p>Slack output of switchover monitoring</p></figcaption></figure><h2 id="what-we-learned"><a href="#what-we-learned"><span><strong>What we learned</strong></span><svg aria-hidden="true" data-heading-link-icon="true" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M13.5438 10.456C12.7247 9.63723 11.614 9.17728 10.4558 9.17728C9.29766 9.17728 8.18692 9.63723 7.36781 10.456L4.27881 13.544C3.45969 14.3631 2.99951 15.4741 2.99951 16.6325C2.99951 17.7909 3.45969 18.9019 4.27881 19.721C5.09793 20.5401 6.2089 21.0003 7.36731 21.0003C8.52572 21.0003 9.63669 20.5401 10.4558 19.721L11.9998 18.177" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path><path d="M10.4561 13.544C11.2752 14.3628 12.3859 14.8227 13.5441 14.8227C14.7022 14.8227 15.8129 14.3628 16.6321 13.544L19.7211 10.456C20.5402 9.63687 21.0004 8.52591 21.0004 7.36749C21.0004 6.20908 20.5402 5.09811 19.7211 4.27899C18.9019 3.45987 17.791 2.99969 16.6326 2.99969C15.4741 2.99969 14.3632 3.45987 13.5441 4.27899L12.0001 5.82299" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg></a></h2><p><code><strong>What went well</strong></code></p><p>Our logical replication strategy was robust. We achieved zero data loss, real-time monitoring gave us the visibility we needed throughout, and schema synchronization worked without a hitch. In addition we improved the design and reliability of a number of core product areas including the delay and throttle modes event transform action.</p><figure class="ImageBlock-module-scss-module__0Ef4dq__imageBlock"><img src="https://www.datocms-assets.com/55802/1762223304-image-3.png?auto=format%2Ccompress&amp;fit=max&amp;q=90&amp;w=2000&amp;auto=format,compress&amp;q=90&amp;w=2000&amp;fit=max" alt="" width="324" height="102" loading="lazy"/></figure><p>Beyond the success of this migration, the frameworks and automation we built will benefit future projects. With more than 100 PostgreSQL Aurora clusters running in our cloud, the tools, processes, and lessons from this experience will be a huge help when it comes time to upgrade or make large-scale changes across our entire fleet.</p><p>We also created a reusable VPC peering setup, making it easy to securely connect databases across regions and pave the way for future use cases. Lambda@Edge request routing also exceeded our expectations. It handled some pretty complex routing scenarios, provided seamless failover, and kept webhook processing running smoothly during the entire migration. You can see clearly in our Honeycomb graph that after the switchover, when the source stack started returning 500 responses (yellow line), requests were seamlessly re-routed to the new stack (green line).</p><figure class="ImageBlock-module-scss-module__0Ef4dq__imageBlock"><img src="https://www.datocms-assets.com/55802/1762871536-image-4.png?auto=format%2Ccompress&amp;fit=max&amp;q=90&amp;w=2000&amp;auto=format,compress&amp;q=90&amp;w=2000&amp;fit=max" alt="" width="829" height="330" loading="lazy"/><figcaption class="ImageBlock-module-scss-module__0Ef4dq__imageCaption"><p>Honeycomb output of monitoring webhook requests during the switchover.</p></figcaption></figure><p><code><strong>The tricky parts</strong></code></p><p>Of course, not everything was smooth sailing. The infrastructure complexity was real! What initially seemed like a straightforward migration quickly revealed a web of interdependencies. Solving one problem often uncovered two more, and our project timeline stretched as new edge cases emerged.</p><p>Some of the most challenging moments came from the database side. For example, we ran into a challenging issue days before the migration: a database migration had run on the source stack but not on the target as a result of a blindspot in our deployment disabling funditonality. Since logical replication doesn’t support <a href="https://www.commandprompt.com/education/postgresql-data-definition-language-ddl/">DDL</a>(Data Definition Language) changes, the replication subscription worker kept dying and retrying. We had to pause deploys and lock the schema versions on both stacks. Even then, Rails’ <code>schema_migrations</code> table was already synced, so Rails thought the migration had already run on the target and wouldn’t retry it. We ended up adding the missing columns manually, which finally allowed replication to resume. By that point, we’d accumulated nearly 5GB of WAL, so we had to wait for the backlog to clear before we could safely switch over. Keeping an eye on the oldest slot lag metric became essential.</p><p>We also ran into an unexpected issue with the BLOB data synchronization job. On migration day, the job that syncs attachments between stacks failed due to unexpected JSON parsing issues, something we hadn’t seen in testing. We ended up “<a href="https://dev.to/ayushn21/applying-monkey-patches-in-rails-1bj1">monkey patching</a>” the job via the Rails console in real time, and quickly followed up with a permanent fix after the migration due to time constraints.</p><p>Request routing brought its own set of hurdles. Lambda@Edge and CloudFront had their quirks, and handling gzipped content was more challenging than we anticipated. Additionally, we did not support WebSocket requests during the migration. However, since nothing critical in our system relied on WebSockets, we were able to tolerate this limitation without impacting core functionality. Finally, the system we built for this migration now requires ongoing monitoring and maintenance. Good documentation became essential, and with our apps and infrastructure always evolving, maintaining this process would not be straight forward.</p><h2 id="conclusion"><a href="#conclusion"><span><strong>Conclusion</strong></span><svg aria-hidden="true" data-heading-link-icon="true" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M13.5438 10.456C12.7247 9.63723 11.614 9.17728 10.4558 9.17728C9.29766 9.17728 8.18692 9.63723 7.36781 10.456L4.27881 13.544C3.45969 14.3631 2.99951 15.4741 2.99951 16.6325C2.99951 17.7909 3.45969 18.9019 4.27881 19.721C5.09793 20.5401 6.2089 21.0003 7.36731 21.0003C8.52572 21.0003 9.63669 20.5401 10.4558 19.721L11.9998 18.177" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path><path d="M10.4561 13.544C11.2752 14.3628 12.3859 14.8227 13.5441 14.8227C14.7022 14.8227 15.8129 14.3628 16.6321 13.544L19.7211 10.456C20.5402 9.63687 21.0004 8.52591 21.0004 7.36749C21.0004 6.20908 20.5402 5.09811 19.7211 4.27899C18.9019 3.45987 17.791 2.99969 16.6326 2.99969C15.4741 2.99969 14.3632 3.45987 13.5441 4.27899L12.0001 5.82299" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg></a></h2><p>This migration taught us that in modern cloud systems, there&#x27;s no such thing as a &quot;simple&quot; database migration. The complexity of maintaining zero downtime while moving critical customer data pushed us to innovate and improve our infrastructure in ways we hadn&#x27;t anticipated.</p><p>The systems and code we built now serve as building blocks for future improvements at Tines. While the journey was far more complex than initially expected, it left us with more robust infrastructure and valuable lessons about managing large-scale cloud systems.</p><p>Final thought: database migrations, it turns out, are less about moving live data and more about making sure nobody notices you&#x27;re moving live data.</p></article></div></div><div style="display:block;width:var(--sectionPadding);height:var(--sectionPadding);flex:0 0 var(--sectionPadding);user-select:none"></div></div><!--$--><!--/$--><section class="WildCodeCTASection-module-scss-module__eN7prW__section"><div class="InteractiveCursor-module-scss-module__DEDOvG__region WildCodeCTASection-module-scss-module__eN7prW__wordmark"><div class="WildCodeField-module-scss-module__N5x1Gq__wordmark" role="img" aria-label="Start today"></div></div><div class="WildCodeCTASection-module-scss-module__eN7prW__text"><p class="WildCodeCTASection-module-scss-module__eN7prW__tagline">Sign up today to get started or schedule time with our team to learn more.</p><div class="CtaButtons-module-scss-module__okO8Ua__actions"><a href="https://login.tines.com/saml_idp/signup"><span class="Button-module-scss-module__KA9GbG__buttonWrapper Button-module-scss-module__KA9GbG__fullWidth" style="width:100%"><span class="Button-module-scss-module__KA9GbG__buttonInner Button-module-scss-module__KA9GbG__filled Button-module-scss-module__KA9GbG__mono" style="--buttonColor:var(--pageForegroundColor);--buttonTextColor:var(--pageBg, var(--lightest))"><span class="Button-module-scss-module__KA9GbG__buttonText">Sign up free</span></span></span></a><a href="/book-a-demo/"><span class="Button-module-scss-module__KA9GbG__buttonWrapper Button-module-scss-module__KA9GbG__fullWidth CtaButtons-module-scss-module__okO8Ua__secondary" style="width:100%"><span class="Button-module-scss-module__KA9GbG__buttonInner Button-module-scss-module__KA9GbG__outlined Button-module-scss-module__KA9GbG__mono" style="--buttonColor:var(--pageForegroundColor);--buttonTextColor:var(--pageBg, var(--lightest))"><span class="Button-module-scss-module__KA9GbG__buttonText">Book a demo</span></span></span></a></div></div></section></div></main><footer id="site-footer" class="SiteFooter26-module-scss-module__LzK7FG__footer"><div class="SiteFooter26-module-scss-module__LzK7FG__sections"><div class="SiteFooter26-module-scss-module__LzK7FG__section"><button type="button" class="SiteFooter26-module-scss-module__LzK7FG__sectionToggle" aria-expanded="false"><span class="SiteFooter26-module-scss-module__LzK7FG__sectionTitle">Product</span><svg width="10" height="7" viewBox="0 0 10 7" fill="none" class="SiteFooter26-module-scss-module__LzK7FG__chevron"><path d="M1.04785 1L5.04785 5L9.04785 1" stroke="currentColor" stroke-width="1.5"></path></svg></button><div class="SiteFooter26-module-scss-module__LzK7FG__sectionContent"><div class="SiteFooter26-module-scss-module__LzK7FG__sectionContentInner"><div class="SiteFooter26-module-scss-module__LzK7FG__group"><a class="SiteFooter26-module-scss-module__LzK7FG__groupTitleLink" href="/3b/">Tines 3B</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/3b/examples/">Examples gallery</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/3b/product-updates/">Product updates</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="https://docs.3b.tines.com/" target="_blank" rel="noopener noreferrer">Docs<span class="SiteFooter26-module-scss-module__LzK7FG__externalMark" aria-hidden="true">↗</span></a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="https://status.tines.com/" target="_blank" rel="noopener noreferrer">Status<span class="SiteFooter26-module-scss-module__LzK7FG__externalMark" aria-hidden="true">↗</span></a></div><div class="SiteFooter26-module-scss-module__LzK7FG__group"><a class="SiteFooter26-module-scss-module__LzK7FG__groupTitleLink" href="/stories/">Tines Stories</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/stories/storyboard/">Storyboard</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/stories/cases/">Cases</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/stories/workbench/">Workbench</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/stories/agents/">Agents</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/stories/monitoring/">Monitoring</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/workflow-capability-matrix/">Workflow capability matrix</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/stories/templates/">Templates</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/library/">Library</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/stories/five-minute-flows/">Five-minute flows</a><a href="/university/" class="SiteFooter26-module-scss-module__LzK7FG__leaf">University</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/stories/whats-new/">What’s new</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/stories/docs/">Docs and API</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="https://status.tines.com/" target="_blank" rel="noopener noreferrer">Status<span class="SiteFooter26-module-scss-module__LzK7FG__externalMark" aria-hidden="true">↗</span></a></div><div class="SiteFooter26-module-scss-module__LzK7FG__group"><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/pricing/">Pricing</a></div></div></div></div><div class="SiteFooter26-module-scss-module__LzK7FG__section"><button type="button" class="SiteFooter26-module-scss-module__LzK7FG__sectionToggle" aria-expanded="false"><span class="SiteFooter26-module-scss-module__LzK7FG__sectionTitle">Solutions</span><svg width="10" height="7" viewBox="0 0 10 7" fill="none" class="SiteFooter26-module-scss-module__LzK7FG__chevron"><path d="M1.04785 1L5.04785 5L9.04785 1" stroke="currentColor" stroke-width="1.5"></path></svg></button><div class="SiteFooter26-module-scss-module__LzK7FG__sectionContent"><div class="SiteFooter26-module-scss-module__LzK7FG__sectionContentInner"><div class="SiteFooter26-module-scss-module__LzK7FG__group"><a class="SiteFooter26-module-scss-module__LzK7FG__groupTitleLink" href="/solutions/it/">IT</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/solutions/it/it-as-a-business-enabler/">IT as a business enabler </a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/solutions/it/infrastructure-management/">Infrastructure management</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/solutions/it/networking/">Networking</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/solutions/it/application-management/">Application management</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/solutions/it/it-service-delivery-and-support/">IT service delivery and support</a></div><div class="SiteFooter26-module-scss-module__LzK7FG__group"><a class="SiteFooter26-module-scss-module__LzK7FG__groupTitleLink" href="/solutions/security/">Security</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/solutions/security/ai-soc/">AI SOC</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/solutions/security/soar/">SOAR</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/solutions/security/governance-risk-and-compliance/">GRC</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/solutions/security/threat-intelligence/">Threat intelligence</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/solutions/security/vulnerability-management/">Vulnerability management</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/solutions/security/network-security/">Network security</a></div><div class="SiteFooter26-module-scss-module__LzK7FG__group"><a class="SiteFooter26-module-scss-module__LzK7FG__leaf SiteFooter26-module-scss-module__LzK7FG__leafBold" href="/public-sector/">Public sector</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf SiteFooter26-module-scss-module__LzK7FG__leafBold" href="/financial-services/">Financial services</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf SiteFooter26-module-scss-module__LzK7FG__leafBold" href="/mssps/">MSSPs</a></div><div class="SiteFooter26-module-scss-module__LzK7FG__group"><a href="/professional-services/" class="SiteFooter26-module-scss-module__LzK7FG__leaf">Professional services</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/ai-in-tines/">AI in Tines</a><a href="/enterprise/" class="SiteFooter26-module-scss-module__LzK7FG__leaf">Enterprise</a></div></div></div></div><div class="SiteFooter26-module-scss-module__LzK7FG__section"><button type="button" class="SiteFooter26-module-scss-module__LzK7FG__sectionToggle" aria-expanded="false"><span class="SiteFooter26-module-scss-module__LzK7FG__sectionTitle">Discover</span><svg width="10" height="7" viewBox="0 0 10 7" fill="none" class="SiteFooter26-module-scss-module__LzK7FG__chevron"><path d="M1.04785 1L5.04785 5L9.04785 1" stroke="currentColor" stroke-width="1.5"></path></svg></button><div class="SiteFooter26-module-scss-module__LzK7FG__sectionContent"><div class="SiteFooter26-module-scss-module__LzK7FG__sectionContentInner"><div class="SiteFooter26-module-scss-module__LzK7FG__group"><a class="SiteFooter26-module-scss-module__LzK7FG__groupTitleLink" href="/customers/">Customers</a></div><div class="SiteFooter26-module-scss-module__LzK7FG__group"><a class="SiteFooter26-module-scss-module__LzK7FG__groupTitleLink" href="/blog/">Blog</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/blog/series/">Series</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/blog/guides/">Guides</a><a href="/podcast/" class="SiteFooter26-module-scss-module__LzK7FG__leaf">Podcast</a></div><div class="SiteFooter26-module-scss-module__LzK7FG__group"><a class="SiteFooter26-module-scss-module__LzK7FG__groupTitleLink" href="/events/">Events</a><a href="/webinars/" class="SiteFooter26-module-scss-module__LzK7FG__leaf">Webinars</a><a href="/bootcamps/" class="SiteFooter26-module-scss-module__LzK7FG__leaf">Bootcamps</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/events/builder-connect/">Builder Connect</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="https://www.workflow.live/" target="_blank" rel="noopener noreferrer">Workflow.live<span class="SiteFooter26-module-scss-module__LzK7FG__externalMark" aria-hidden="true">↗</span></a></div><div class="SiteFooter26-module-scss-module__LzK7FG__group"><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="https://community.tines.com/" target="_blank" rel="noopener noreferrer">Community<span class="SiteFooter26-module-scss-module__LzK7FG__externalMark" aria-hidden="true">↗</span></a><a href="/you-did-what-with-tines/" class="SiteFooter26-module-scss-module__LzK7FG__leaf">YDWWT</a></div><div class="SiteFooter26-module-scss-module__LzK7FG__group"><a href="/customer-center/" class="SiteFooter26-module-scss-module__LzK7FG__leaf">Customer center</a></div></div></div></div><div class="SiteFooter26-module-scss-module__LzK7FG__stackedColumn"><div class="SiteFooter26-module-scss-module__LzK7FG__section"><button type="button" class="SiteFooter26-module-scss-module__LzK7FG__sectionToggle" aria-expanded="false"><span class="SiteFooter26-module-scss-module__LzK7FG__sectionTitle">Company</span><svg width="10" height="7" viewBox="0 0 10 7" fill="none" class="SiteFooter26-module-scss-module__LzK7FG__chevron"><path d="M1.04785 1L5.04785 5L9.04785 1" stroke="currentColor" stroke-width="1.5"></path></svg></button><div class="SiteFooter26-module-scss-module__LzK7FG__sectionContent"><div class="SiteFooter26-module-scss-module__LzK7FG__sectionContentInner"><div class="SiteFooter26-module-scss-module__LzK7FG__group"><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/about/">About us</a><a href="/careers/" class="SiteFooter26-module-scss-module__LzK7FG__leaf">Careers</a><a href="/newsroom/" class="SiteFooter26-module-scss-module__LzK7FG__leaf">Newsroom</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/partners/">Partners</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="/contact/">Contact</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="https://store.tines.com/" target="_blank" rel="noopener noreferrer">Store<span class="SiteFooter26-module-scss-module__LzK7FG__externalMark" aria-hidden="true">↗</span></a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="https://live.standards.site/tines" target="_blank" rel="noopener noreferrer">Brand assets<span class="SiteFooter26-module-scss-module__LzK7FG__externalMark" aria-hidden="true">↗</span></a></div><div class="SiteFooter26-module-scss-module__LzK7FG__group"><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="https://www.linkedin.com/company/tines-io/" target="_blank" rel="noopener noreferrer">LinkedIn<span class="SiteFooter26-module-scss-module__LzK7FG__externalMark" aria-hidden="true">↗</span></a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="https://twitter.com/tines_hq" target="_blank" rel="noopener noreferrer">X<span class="SiteFooter26-module-scss-module__LzK7FG__externalMark" aria-hidden="true">↗</span></a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="https://www.youtube.com/@TinesHQ" target="_blank" rel="noopener noreferrer">YouTube<span class="SiteFooter26-module-scss-module__LzK7FG__externalMark" aria-hidden="true">↗</span></a></div></div></div></div><div class="SiteFooter26-module-scss-module__LzK7FG__section"><button type="button" class="SiteFooter26-module-scss-module__LzK7FG__sectionToggle" aria-expanded="false"><span class="SiteFooter26-module-scss-module__LzK7FG__sectionTitle">Trust and security</span><svg width="10" height="7" viewBox="0 0 10 7" fill="none" class="SiteFooter26-module-scss-module__LzK7FG__chevron"><path d="M1.04785 1L5.04785 5L9.04785 1" stroke="currentColor" stroke-width="1.5"></path></svg></button><div class="SiteFooter26-module-scss-module__LzK7FG__sectionContent"><div class="SiteFooter26-module-scss-module__LzK7FG__sectionContentInner"><div class="SiteFooter26-module-scss-module__LzK7FG__group"><a href="/security/" class="SiteFooter26-module-scss-module__LzK7FG__groupTitleLink">Security</a><a href="/tines-security-best-practices/" class="SiteFooter26-module-scss-module__LzK7FG__leaf">Security best practices</a><a class="SiteFooter26-module-scss-module__LzK7FG__leaf" href="https://trust.tines.com/" target="_blank" rel="noopener noreferrer">Trust center<span class="SiteFooter26-module-scss-module__LzK7FG__externalMark" aria-hidden="true">↗</span></a></div><div class="SiteFooter26-module-scss-module__LzK7FG__group"><a href="/legal/" class="SiteFooter26-module-scss-module__LzK7FG__groupTitleLink">Legal</a><a href="/privacy/" class="SiteFooter26-module-scss-module__LzK7FG__leaf">Privacy</a><a href="/terms/" class="SiteFooter26-module-scss-module__LzK7FG__leaf">Terms</a><a href="/dpa/" class="SiteFooter26-module-scss-module__LzK7FG__leaf">DPA</a><a href="/cookies-policy/" class="SiteFooter26-module-scss-module__LzK7FG__leaf">Cookies policy</a></div></div></div></div></div></div><div class="SiteFooter26-module-scss-module__LzK7FG__bottomBar"><span>© Tines</span><a class="SiteFooter26-module-scss-module__LzK7FG__bottomLogo" title="Tines" href="/"><svg class="tines-logo-icon " width="28" height="28" viewBox="0 0 48 48" fill="currentColor"><path fill-rule="evenodd" clip-rule="evenodd" d="M17.6641 0C11.9485 0 6.89478 3.68623 5.18424 9.10302L0.47998 24L5.18424 38.897C6.89479 44.3138 11.9485 48 17.6641 48H30.3358C36.0515 48 41.1052 44.3138 42.8157 38.897L47.52 24L42.8157 9.10302C41.1052 3.68623 36.0515 0 30.3358 0H17.6641ZM30.336 3.74451C34.4042 3.74451 38.0012 6.36822 39.2187 10.2237L40.372 13.8759C38.0518 11.9723 35.1137 10.9008 32.0429 10.9006L15.9228 10.9002C12.8726 10.9004 9.9534 11.9575 7.64056 13.8372L8.78167 10.2237C9.99917 6.36822 13.5962 3.74451 17.6643 3.74451H30.336ZM8.51076 18.2979L5.58159 22.1278L42.3838 22.1278L39.4549 18.2983C37.6948 15.9969 34.9524 14.6453 32.0426 14.6451H15.835C12.958 14.6723 10.2531 16.0197 8.51076 18.2979ZM42.3778 25.8723L39.4547 29.6983C37.6944 32.0023 34.9501 33.3555 32.0381 33.3553L15.9271 33.3548C13.0152 33.3551 10.2708 32.0019 8.51056 29.6979L5.58776 25.8723L42.3778 25.8723ZM8.78145 37.7763C9.99894 41.6318 13.5959 44.2555 17.6641 44.2555H30.3358C34.404 44.2555 38.001 41.6318 39.2185 37.7763L40.3726 34.1214C38.0514 36.0273 35.111 37.1 32.0378 37.0998L15.9274 37.0994C12.8747 37.0996 9.9531 36.0412 7.63915 34.159L8.78145 37.7763Z"></path></svg></a><button class="SiteFooter26-module-scss-module__LzK7FG__cookieSettings">Cookie settings</button></div></footer></div><script src="/_next/static/immutable/chunks/2iox-02sx9loa.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n4:I[11926,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\"],\"default\"]\n5:I[550880,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\"],\"default\"]\n7:I[311235,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\"],\"OutletBoundary\"]\n8:\"$Sreact.suspense\"\nb:I[311235,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\"],\"ViewportBoundary\"]\nd:I[311235,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\"],\"MetadataBoundary\"]\nf:I[940405,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\"],\"default\",1]\n:HL[\"/_next/static/immutable/chunks/132trk_zc3ylu.css\",\"style\"]\n:HL[\"/_next/static/immutable/chunks/1bmd2ryn1r-hv.css\",\"style\"]\n:HL[\"/_next/static/immutable/chunks/3qp9-r2fgklws.css\",\"style\"]\n:HL[\"/_next/static/immutable/chunks/43xkgk33-6316.css\",\"style\"]\n:HL[\"/_next/static/immutable/chunks/3w0wy298bmb96.css\",\"style\"]\n:HL[\"/_next/static/immutable/chunks/27k1lbkm9ytw4.css\",\"style\"]\n:HL[\"/_next/static/immutable/chunks/45auz_6fncqnw.css\",\"style\"]\n:HL[\"/_next/static/immutable/chunks/3a0f_y332k2v1.css\",\"style\"]\na:X\n0:{\"P\":null,\"c\":[\"\",\"blog\",\"zero-downtime-database-migrations-lessons-from-moving-a-live-production\",\"\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"blog\",{\"children\":[[\"segments\",\"zero-downtime-database-migrations-lessons-from-moving-a-live-production\",\"c\",[\"guides\",\"series\"]],{\"children\":[\"__PAGE__\",{},\"$undefined\",\"$undefined\",4096]},\"$undefined\",\"$undefined\",4096]},\"$undefined\",\"$undefined\",4096]},\"$undefined\",\"$undefined\",4112],[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/immutable/chunks/132trk_zc3ylu.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"1\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/immutable/chunks/1bmd2ryn1r-hv.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"2\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/immutable/chunks/3qp9-r2fgklws.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"3\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/immutable/chunks/43xkgk33-6316.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"4\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/immutable/chunks/3w0wy298bmb96.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-1\",{\"src\":\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-2\",{\"src\":\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-3\",{\"src\":\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-4\",{\"src\":\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-5\",{\"src\":\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-6\",{\"src\":\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\",\"async\":true,\"nonce\":\"$undefined\"}]],\"$L2\"]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/immutable/chunks/27k1lbkm9ytw4.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"1\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/immutable/chunks/45auz_6fncqnw.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/immutable/chunks/3pjhofejz57jt.js\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-1\",{\"src\":\"/_next/static/immutable/chunks/1q-89ajkqbbkw.js\",\"async\":true,\"nonce\":\"$undefined\"}]],\"$L3\"]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L4\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L5\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[\"$L6\",[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/immutable/chunks/3a0f_y332k2v1.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/immutable/chunks/2uho2m59t6w8f.js\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-1\",{\"src\":\"/_next/static/immutable/chunks/2ti9pdu9p4n5l.js\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-2\",{\"src\":\"/_next/static/immutable/chunks/31qqpjo4d7384.js\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-3\",{\"src\":\"/_next/static/immutable/chunks/3zb6qebqmln4d.js\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"$L7\",null,{\"children\":[\"$\",\"$8\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@9\"}]}]]}],{},null,false,null]},null,false,\"$a\"]},null,false,null]},null,false,null],[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$Lb\",null,{\"children\":\"$Lc\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$Ld\",null,{\"children\":[\"$\",\"$8\",null,{\"name\":\"Next.Metadata\",\"children\":\"$Le\"}]}]}],null]}],false]],\"m\":\"$undefined\",\"G\":[\"$f\",[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/immutable/chunks/132trk_zc3ylu.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"1\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/immutable/chunks/1bmd2ryn1r-hv.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"2\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/immutable/chunks/3qp9-r2fgklws.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"3\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/immutable/chunks/43xkgk33-6316.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"4\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/immutable/chunks/3w0wy298bmb96.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]]],\"S\":false,\"h\":null,\"r\":\"$undefined\",\"s\":\"$undefined\",\"a\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\"}\na:C\nc:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n10:I[62200,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\",\"/_next/static/immutable/chunks/3pjhofejz57jt.js\",\"/_next/static/immutable/chunks/1q-89ajkqbbkw.js\",\"/_next/static/immutable/chunks/2uho2m59t6w8f.js\",\"/_next/static/immutable/chunks/2ti9pdu9p4n5l.js\",\"/_next/static/immutable/chunks/31qqpjo4d7384.js\",\"/_next/static/immutable/chunks/3zb6qebqmln4d.js\"],\"AreaNavActiveProvider\"]\n11:I[380637,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\",\"/_next/static/immutable/chunks/3pjhofejz57jt.js\",\"/_next/static/immutable/chunks/1q-89ajkqbbkw.js\"],\"AreaNav\"]\n12:I[323752,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\",\"/_next/static/immutable/chunks/3pjhofejz57jt.js\",\"/_next/static/immutable/chunks/1q-89ajkqbbkw.js\"],\"WildCodeCTASection\"]\n:HL[\"/fonts/Roobert-Regular.woff2\",\"font\",{\"crossOrigin\":\"anonymous\",\"type\":\"font/woff2\"}]\n:HL[\"/fonts/Reckless-Light.woff2\",\"font\",{\"crossOrigin\":\"anonymous\",\"type\":\"font/woff2\"}]\n:HL[\"/fonts/Reckless-Regular.woff2\",\"font\",{\"crossOrigin\":\"anonymous\",\"type\":\"font/woff2\"}]\n3:[\"$\",\"div\",null,{\"children\":[null,[\"$\",\"$L10\",null,{\"children\":[[\"$\",\"$L11\",null,{\"nav\":{\"id\":\"blog\",\"prefix\":\"/blog\",\"pill\":{\"label\":\"Blog\",\"href\":\"/blog\"},\"sections\":[{\"label\":\"Home\",\"href\":\"/blog\"},{\"label\":\"Categories\",\"flattenWhenWide\":true,\"links\":[{\"label\":\"News\",\"href\":\"/blog/news\"},{\"label\":\"Tutorials\",\"href\":\"/blog/tutorials\"},{\"label\":\"Engineering\",\"href\":\"/blog/engineering\"},{\"label\":\"Product updates\",\"href\":\"/blog/product-updates\"},{\"label\":\"Inside Tines\",\"href\":\"/blog/inside-tines\"},{\"label\":\"Tines × Tines\",\"href\":\"/blog/tines-by-tines\"}]},{\"label\":\"Guides\",\"href\":\"/blog/guides\"},{\"label\":\"All articles\",\"href\":\"/blog/articles\"}]}}],[\"$\",\"$L4\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L5\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}],[\"$\",\"$L12\",null,{}]]}]]}]\n13:T699,(function(){var d=document.documentElement,p=location.pathname,R=[\"/stories/docs\",\"/stories/whats-new\",\"/workflow-capability-matrix\",\"/3b/examples\",\"/3b/product-updates\",\"/library\",\"/stories/five-minute-flows\"],F=[\"/stories/five-minute-flows\"],M=[\"/workflow-capability-matrix\"];function tc(c){var m=document.querySelector('meta[name=\"theme-color\"]');if(m)m.setAttribute(\"content\",c)}function on(L){return L.some(function(r){return p===r||p.indexOf(r+\"/\")===0})}var ok=on(R);if(!ok){d.removeAttribute(\"data-dark-mode-supported\");d.removeAttribute(\"data-page-theme\");d.setAttribute(\"data-color-scheme\",\"light\");d.setAttribute(\"data-resolved-scheme\",\"light\");tc(\"#fcf9f5\");if(on([\"/campaigns\"])){d.setAttribute(\"data-campaign-nav\",\"\")}return}d.setAttribute(\"data-dark-mode-supported\",\"\");if(on(F)){d.setAttribute(\"data-color-scheme\",\"dark\");d.setAttribute(\"data-resolved-scheme\",\"dark\");tc(\"#16131e\");return}var pt=document.cookie.match(/page-theme=(\\w+)/);var mq=/[?\u0026]matrix=on/.test(location.search);if(on(M)\u0026\u0026((pt\u0026\u0026pt[1]===\"matrix\")||mq)){d.setAttribute(\"data-page-theme\",\"matrix\");d.setAttribute(\"data-color-scheme\",\"dark\");d.setAttribute(\"data-resolved-scheme\",\"dark\");tc(\"#0b1a17\");return}else{d.removeAttribute(\"data-page-theme\")}var cm=document.cookie.match(/preferred-color-scheme=(\\w+)/);var s;if(cm){s=cm[1]}else{var sv=d.getAttribute(\"data-color-scheme\");s=sv\u0026\u0026sv!==\"light\"?sv:\"auto\"}var r=s===\"dark\"?\"dark\":s===\"light\"?\"light\":window.matchMedia(\"(prefers-color-scheme:dark)\").matches?\"dark\":\"light\";d.setAttribute(\"data-color-scheme\",s);d.setAttribute(\"data-resolved-scheme\",r);tc(r===\"dark\"?\"#16131e\":\"#fcf9f5\");if(on([\"/campaigns\"])){d.setAttribute(\"data-campaign-nav\",\"\")}})()14:T53a,{\"@context\":\"https://schema.org\",\"@type\":\"Organization\",\"name\":\"Tines\",\"url\":\"https://www.tines.com/\",\"@id\":\"https://www.tines.com/#org\",\"description\":\"Tines 3B enables every team to build AI agents, apps, and automation in one safe, secure environment — with the control Security and IT need to govern it at scale.\",\"foundingDate\":\"2018\",\"founders\":[{\"@type\":\"Person\",\"name\":\"Eoin Hinchy\"},{\"@type\":\"Person\",\"name\":\"Thomas Kinsella\"}],\"sameAs\":[\"https://www.linkedin.com/company/tines-io/\",\"https://twitter.com/tines_hq\",\"https://www.youtube.com/@TinesHQ\",\"https://github.com/tines\"],\"contactPoint\":[{\"@type\":\"ContactPoint\",\"contactType\":\"customer support\",\"email\":\"hello@tines.com\",\"availableLanguage\":[\"en\"]},{\"@type\":\"ContactPoint\",\"contactType\":\"analyst relations\",\"email\":\"analysts@tines.com\",\"availableLanguage\":[\"en\"]},{\"@type\":\"ContactPoint\",\"contactType\":\"media relations\",\"email\":\"pr@tines.com\",\"availableLanguage\":[\"en\"]}],\"address\":[{\"@type\":\"PostalAddress\",\"streetAddress\":\"10 Westland Square, Pearse Street\",\"addressLocality\":\"Dublin\",\"postalCode\":\"D02 CX36\",\"addressCountry\":\"IE\",\"name\":\"Tines Limited\"},{\"@type\":\"PostalAddress\",\"streetAddress\":\"99 High Street, 14th Floor\",\"addressLocality\":\"Boston\",\"addressRegion\":\"MA\",\"postalCode\":\"02111\",\"addressCountry\":\"US\",\"name\":\"Tines Technology Inc.\"}],\"areaServed\":\"Global\"}2:[\"$\",\"html\",null,{\"lang\":\"en-US\",\"data-color-scheme\":\"light\",\"data-resolved-scheme\":\"light\",\"suppressHydrationWarning\":true,\"children\":[[\"$\",\"head\",null,{\"children\":[[\"$\",\"script\",null,{\"id\":\"color-scheme-init\",\"dangerouslySetInnerHTML\":{\"__html\":\"$13\"}}],[\"$\",\"meta\",null,{\"name\":\"theme-color\",\"content\":\"#fcf9f5\"}],[\"$\",\"link\",null,{\"rel\":\"preload\",\"href\":\"/fonts/Roobert-Regular.woff2\",\"as\":\"font\",\"type\":\"font/woff2\",\"crossOrigin\":\"anonymous\"}],[\"$\",\"link\",null,{\"rel\":\"preload\",\"href\":\"/fonts/Reckless-Light.woff2\",\"as\":\"font\",\"type\":\"font/woff2\",\"crossOrigin\":\"anonymous\"}],[\"$\",\"link\",null,{\"rel\":\"preload\",\"href\":\"/fonts/Reckless-Regular.woff2\",\"as\":\"font\",\"type\":\"font/woff2\",\"crossOrigin\":\"anonymous\"}]]}],[\"$\",\"body\",null,{\"children\":[[\"$\",\"script\",null,{\"type\":\"application/ld+json\",\"dangerouslySetInnerHTML\":{\"__html\":\"$14\"}}],\"$L15\",\"$L16\",\"$L17\"]}]]}]\n18:I[12579,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\"],\"default\"]\n19:I[757183,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\"],\"AppCookiesProvider\"]\n1a:I[44679,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\"],\"CookieConsentInit\"]\n1b:I[209507,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\"],\"PageViews\"]\n1c:I[102237,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\"],\"PageThemeRouteSync\"]\n1d:I[373871,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\"],\"default\"]\n1e:I[871026,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\"],\"SiteNav26\"]\n1f:I[414512,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\"],\"SiteNavHeightTracker\"]\n20:I[841241,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\",\"/_next/static/immutable/chunks/3pjhofejz57jt.js\",\"/_next/static/immutable/chunks/1q-89ajkqbbkw.js\",\"/_next/static/immutable/chunks/2uho2m59t6w8f.js\",\"/_next/static/immutable/chunks/2ti9pdu9p4n5l.js\",\"/_next/static/immutable/chunks/31qqpjo4d7384.js\",\"/_next/static/immutable/chunks/3zb6qebqmln4d.js\"],\"Image\"]\n21:I[980849,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\",\"/_next/static/immutable/chunks/3pjhofejz57jt.js\",\"/_next/static/immutable/chunks/1q-89ajkqbbkw.js\",\"/_next/static/immutable/chunks/2uho2m59t6w8f.js\",\"/_next/static/immutable/chunks/2ti9pdu9p4n5l.js\",\"/_next/static/immutable/chunks/31qqpjo4d7384.js\",\"/_next/static/immutable/chunks/3zb6qebqmln4d.js\"],\"default\"]\n23:I[20190,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\"],\"DevGridOverlay\"]\n24:I[70011,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\"],\"DevPanel\"]\n25:I[535897,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\"],\"PreviewModeDevTag\"]\n26:I[868430,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\"],\"\"]\n:HL[\"/_next/static/immutable/chunks/3_-011rjao1d9.css\",\"style\"]\n15:[\"$\",\"$L18\",null,{}]\n16:[\"$\",\"$L19\",null,{\"cookies\":{\"emailAddress\":null,\"lastUsedTenantDomain\":\"\"},\"children\":[[\"$\",\"$L1a\",null,{}],[\"$\",\"$L1b\",null,{}],[\"$\",\"$L1c\",null,{}],[\"$\",\"$L1d\",null,{\"children\":[[\"$\",\"div\",null,{\"className\":\"PageBackdrop-module-scss-module__KKf-uW__pageShell\",\"children\":[[\"$\",\"div\",null,{\"id\":\"page-backdrop-portal\",\"className\":\"PageBackdrop-module-scss-module__KKf-uW__slot\",\"aria-hidden\":\"true\"}],[\"$\",\"a\",null,{\"href\":\"#main-content\",\"className\":\"skip-link\",\"children\":\"Skip to content\"}],[\"$\",\"$L1e\",null,{\"featured\":{\"blog\":{\"title\":\"We gave all our employees access to Tines 3B. Here's what happened next.\",\"href\":\"/blog/tines-3b-first-90/\",\"imageUrl\":\"https://www.datocms-assets.com/55802/1788825912-2026-09-we-gave-employees-3b-header.png?auto=format%2Ccompress\u0026fit=crop\u0026h=420\u0026q=70\u0026w=800\"},\"event\":{\"title\":\"Wild code: You can’t govern what you can’t see\",\"href\":\"/webinars/wild-code-you-cant-govern-what-you-cant-see/\",\"imageUrl\":\"https://www.datocms-assets.com/55802/1785849013-techadvicewebinar-dato-1200-x-630-2.png?auto=format%2Ccompress\u0026fit=crop\u0026h=420\u0026q=70\u0026w=800\"}}}],[\"$\",\"$L1f\",null,{}],[\"$\",\"main\",null,{\"id\":\"main-content\",\"children\":[\"$\",\"$L4\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L5\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[null,[\"$\",\"div\",null,{\"className\":\"not-found-module-scss-module__DyuKYG__wrap\",\"children\":[[\"$\",\"$L20\",null,{\"className\":\"not-found-module-scss-module__DyuKYG__illustration\",\"src\":\"/v5-assets/images/tines-404-illustration-ufo.png\",\"alt\":\"\",\"width\":120,\"height\":120}],[\"$\",\"h1\",null,{\"className\":\"not-found-module-scss-module__DyuKYG__heading\",\"children\":\"Page not found\"}],[\"$\",\"p\",null,{\"className\":\"not-found-module-scss-module__DyuKYG__subtext\",\"children\":\"We couldn’t find what you were looking for.\"}],[\"$\",\"$L21\",null,{\"to\":\"/\",\"color\":\"purple\",\"shade\":700,\"children\":\"Go home\"}]]}]],[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/immutable/chunks/3_-011rjao1d9.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]]],\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}],\"$L22\"]}],[\"$\",\"$L23\",null,{}],[\"$\",\"$L24\",null,{}],[\"$\",\"$L25\",null,{}]]}]]}]\n17:[[\"$\",\"$L26\",null,{\"id\":\"gtag-shim\",\"strategy\":\"afterInteractive\",\"data-cookieconsent\":\"ignore\",\"dangerouslySetInnerHTML\":{\"__html\":\"window.dataLayer=window.dataLayer||[];window.gtag=window.gtag||function(){dataLayer.push(arguments)};\"}}],[\"$\",\"$L26\",null,{\"id\":\"gtm-loader\",\"strategy\":\"afterInteractive\",\"data-cookieconsent\":\"ignore\",\"dangerouslySetInnerHTML\":{\"__html\":\"(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({\\\"gtm.start\\\":new Date().getTime(),event:\\\"gtm.js\\\"});var f=d.getElementsByTagName(s)[0],j=d.createElement(s),dl=l!=\\\"dataLayer\\\"?\\\"\u0026l=\\\"+l:\\\"\\\";j.async=true;j.src=\\\"https://www.googletagmanager.com/gtm.js?id=\\\"+i+dl;f.parentNode.insertBefore(j,f)})(window,document,\\\"script\\\",\\\"dataLayer\\\",\\\"GTM-5Z8VZQ5\\\");\"}}]]\n28:I[875931,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\"],\"IconMark\"]\n6:[\"$\",\"div\",null,{\"className\":\"blog-module-scss-module__RlMr6a__contentColumn\",\"children\":\"$L27\"}]\n9:null\ne:[[\"$\",\"title\",\"0\",{\"children\":\"Lessons from migrating a live production database without downtime\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Discover how Tines engineered a zero-downtime AWS database migration—maintaining 100% availability while improving reliability and scalability.\"}],[\"$\",\"meta\",\"2\",{\"name\":\"keywords\",\"content\":\"Tines, security workflow automation, No-Code, SOAR\"}],[\"$\",\"link\",\"3\",{\"rel\":\"canonical\",\"href\":\"https://www.tines.com/blog/zero-downtime-database-migrations-lessons-from-moving-a-live-production/\"}],[\"$\",\"meta\",\"4\",{\"property\":\"og:title\",\"content\":\"Lessons from migrating a live production database without downtime\"}],[\"$\",\"meta\",\"5\",{\"property\":\"og:description\",\"content\":\"Discover how Tines engineered a zero-downtime AWS database migration—maintaining 100% availability while improving reliability and scalability.\"}],[\"$\",\"meta\",\"6\",{\"property\":\"og:image\",\"content\":\"https://www.datocms-assets.com/55802/1762955872-tines-blog-zero-downtime-og-image-1.png\"}],[\"$\",\"meta\",\"7\",{\"property\":\"og:type\",\"content\":\"article\"}],[\"$\",\"meta\",\"8\",{\"name\":\"twitter:card\",\"content\":\"summary_large_image\"}],[\"$\",\"meta\",\"9\",{\"name\":\"twitter:creator\",\"content\":\"@tines_hq\"}],[\"$\",\"meta\",\"10\",{\"name\":\"twitter:title\",\"content\":\"Tines | Intelligent workflow platform\"}],[\"$\",\"meta\",\"11\",{\"name\":\"twitter:description\",\"content\":\"Build and orchestrate intelligent workflows and agents, on the platform security and IT teams love.\"}],[\"$\",\"meta\",\"12\",{\"name\":\"twitter:image\",\"content\":\"https://www.tines.com/v5-assets/images/og/tines-home.png\"}],[\"$\",\"link\",\"13\",{\"rel\":\"icon\",\"href\":\"/v5-assets/favicon.png\"}],[\"$\",\"$L28\",\"14\",{}]]\n29:I[586848,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\"],\"FooterSection\"]\n2a:I[693529,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\",\"/_next/static/immutable/chunks/3pjhofejz57jt.js\",\"/_next/static/immutable/chunks/1q-89ajkqbbkw.js\",\"/_next/static/immutable/chunks/2uho2m59t6w8f.js\",\"/_next/static/immutable/chunks/2ti9pdu9p4n5l.js\",\"/_next/static/immutable/chunks/31qqpjo4d7384.js\",\"/_next/static/immutable/chunks/3zb6qebqmln4d.js\"],\"\"]\n22:[\"$\",\"footer\",null,{\"id\":\"site-footer\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__footer\",\"children\":[[\"$\",\"div\",null,{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__sections\",\"children\":[[[\"$\",\"$L29\",\"Product\",{\"title\":\"Product\",\"children\":[[\"$\",\"div\",\"0\",{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__group\",\"children\":[[\"$\",\"$L2a\",null,{\"href\":\"/3b/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__groupTitleLink\",\"children\":\"Tines 3B\"}],[[\"$\",\"$L2a\",\"Examples gallery\",{\"href\":\"/3b/examples/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Examples gallery\"}],[\"$\",\"$L2a\",\"Product updates\",{\"href\":\"/3b/product-updates/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Product updates\"}],[\"$\",\"a\",\"Docs\",{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"href\":\"https://docs.3b.tines.com/\",\"target\":\"_blank\",\"rel\":\"noopener noreferrer\",\"children\":[\"Docs\",[\"$\",\"span\",null,{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__externalMark\",\"aria-hidden\":\"true\",\"children\":\"↗\"}]]}],[\"$\",\"a\",\"Status\",{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"href\":\"https://status.tines.com/\",\"target\":\"_blank\",\"rel\":\"noopener noreferrer\",\"children\":[\"Status\",[\"$\",\"span\",null,{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__externalMark\",\"aria-hidden\":\"true\",\"children\":\"↗\"}]]}]]]}],[\"$\",\"div\",\"1\",{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__group\",\"children\":[[\"$\",\"$L2a\",null,{\"href\":\"/stories/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__groupTitleLink\",\"children\":\"Tines Stories\"}],[[\"$\",\"$L2a\",\"Storyboard\",{\"href\":\"/stories/storyboard/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Storyboard\"}],[\"$\",\"$L2a\",\"Cases\",{\"href\":\"/stories/cases/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Cases\"}],[\"$\",\"$L2a\",\"Workbench\",{\"href\":\"/stories/workbench/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Workbench\"}],[\"$\",\"$L2a\",\"Agents\",{\"href\":\"/stories/agents/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Agents\"}],[\"$\",\"$L2a\",\"Monitoring\",{\"href\":\"/stories/monitoring/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Monitoring\"}],[\"$\",\"$L2a\",\"Workflow capability matrix\",{\"href\":\"/workflow-capability-matrix/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Workflow capability matrix\"}],[\"$\",\"$L2a\",\"Templates\",{\"href\":\"/stories/templates/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Templates\"}],[\"$\",\"$L2a\",\"Library\",{\"href\":\"/library/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Library\"}],[\"$\",\"$L2a\",\"Five-minute flows\",{\"href\":\"/stories/five-minute-flows/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Five-minute flows\"}],[\"$\",\"a\",\"University\",{\"href\":\"/university/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"University\"}],[\"$\",\"$L2a\",\"What’s new\",{\"href\":\"/stories/whats-new/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"What’s new\"}],[\"$\",\"$L2a\",\"Docs and API\",{\"href\":\"/stories/docs/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Docs and API\"}],[\"$\",\"a\",\"Status\",{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"href\":\"https://status.tines.com/\",\"target\":\"_blank\",\"rel\":\"noopener noreferrer\",\"children\":[\"Status\",[\"$\",\"span\",null,{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__externalMark\",\"aria-hidden\":\"true\",\"children\":\"↗\"}]]}]]]}],[\"$\",\"div\",\"2\",{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__group\",\"children\":[null,[[\"$\",\"$L2a\",\"Pricing\",{\"href\":\"/pricing/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Pricing\"}]]]}]]}],[\"$\",\"$L29\",\"Solutions\",{\"title\":\"Solutions\",\"children\":[[\"$\",\"div\",\"0\",{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__group\",\"children\":[[\"$\",\"$L2a\",null,{\"href\":\"/solutions/it/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__groupTitleLink\",\"children\":\"IT\"}],[[\"$\",\"$L2a\",\"IT as a business enabler \",{\"href\":\"/solutions/it/it-as-a-business-enabler/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"IT as a business enabler \"}],\"$L2b\",\"$L2c\",\"$L2d\",\"$L2e\"]]}],\"$L2f\",\"$L30\",\"$L31\"]}],\"$L32\"],\"$L33\"]}],\"$L34\"]}]\n36:I[986992,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\"],\"CookieSettingsButton\"]\n37:I[383156,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\",\"/_next/static/immutable/chunks/3pjhofejz57jt.js\",\"/_next/static/immutable/chunks/1q-89ajkqbbkw.js\",\"/_next/static/immutable/chunks/2uho2m59t6w8f.js\",\"/_next/static/immutable/chunks/2ti9pdu9p4n5l.js\",\"/_next/static/immutable/chunks/31qqpjo4d7384.js\",\"/_next/static/immutable/chunks/3zb6qebqmln4d.js\"],\"HashScroller\"]\n38:I[62200,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\",\"/_next/static/immutable/chunks/3pjhofejz57jt.js\",\"/_next/static/immutable/chunks/1q-89ajkqbbkw.js\",\"/_next/static/immutable/chunks/2uho2m59t6w8f.js\",\"/_next/static/immutable/chunks/2ti9pdu9p4n5l.js\",\"/_next/static/immutable/chunks/31qqpjo4d7384.js\",\"/_next/static/immutable/chunks/3zb6qebqmln4d.js\"],\"SetAreaNavActive\"]\n2b:[\"$\",\"$L2a\",\"Infrastructure management\",{\"href\":\"/solutions/it/infrastructure-management/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Infrastructure management\"}]\n2c:[\"$\",\"$L2a\",\"Networking\",{\"href\":\"/solutions/it/networking/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Networking\"}]\n2d:[\"$\",\"$L2a\",\"Application management\",{\"href\":\"/solutions/it/application-management/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Application management\"}]\n2e:[\"$\",\"$L2a\",\"IT service delivery and support\",{\"href\":\"/solutions/it/it-service-delivery-and-support/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"IT service delivery and support\"}]\n2f:[\"$\",\"div\",\"1\",{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__group\",\"children\":[[\"$\",\"$L2a\",null,{\"href\":\"/solutions/security/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__groupTitleLink\",\"children\":\"Security\"}],[[\"$\",\"$L2a\",\"AI SOC\",{\"href\":\"/solutions/security/ai-soc/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"AI SOC\"}],[\"$\",\"$L2a\",\"SOAR\",{\"href\":\"/solutions/security/soar/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"SOAR\"}],[\"$\",\"$L2a\",\"GRC\",{\"href\":\"/solutions/security/governance-risk-and-compliance/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"GRC\"}],[\"$\",\"$L2a\",\"Threat intelligence\",{\"href\":\"/solutions/security/threat-intelligence/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Threat intelligence\"}],[\"$\",\"$L2a\",\"Vulnerability management\",{\"href\":\"/solutions/security/vulnerability-management/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Vulnerability management\"}],[\"$\",\"$L2a\",\"Network security\",{\"href\":\"/solutions/security/network-security/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Network security\"}]]]}]\n30:[\"$\",\"div\",\"2\",{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__group\",\"children\":[null,[[\"$\",\"$L2a\",\"Public sector\",{\"href\":\"/public-sector/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf SiteFooter26-module-scss-module__LzK7FG__leafBold\",\"children\":\"Public sector\"}],[\"$\",\"$L2a\",\"Financial services\",{\"href\":\"/financial-services/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf SiteFooter26-module-scss-module__LzK7FG__leafBold\",\"children\":\"Financial services\"}],[\"$\",\"$L2a\",\"MSSPs\",{\"href\":\"/mssps/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf SiteFooter26-module-scss-module__LzK7FG__leafBold\",\"children\":\"MSSPs\"}]]]}]\n31:[\"$\",\"div\",\"3\",{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__group\",\"children\":[null,[[\"$\",\"a\",\"Professional services\",{\"href\":\"/professional-services/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Professional services\"}],[\"$\",\"$L2a\",\"AI in Tines\",{\"href\":\"/ai-in-tines/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"AI in Tines\"}],[\"$\",\"a\",\"Enterprise\",{\"href\":\"/enterprise/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Enterprise\"}]]]}]\n32:[\"$\",\"$L29\",\"Discover\",{\"title\":\"Discover\",\"children\":[[\"$\",\"div\",\"0\",{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__group\",\"children\":[[\"$\",\"$L2a\",null,{\"href\":\"/customers/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__groupTitleLink\",\"children\":\"Customers\"}],[]]}],[\"$\",\"div\",\"1\",{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__group\",\"children\":[[\"$\",\"$L2a\",null,{\"href\":\"/blog/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__groupTitleLink\",\"children\":\"Blog\"}],[[\"$\",\"$L2a\",\"Series\",{\"href\":\"/blog/series/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Series\"}],[\"$\",\"$L2a\",\"Guides\",{\"href\":\"/blog/guides/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Guides\"}],[\"$\",\"a\",\"Podcast\",{\"href\":\"/podcast/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Podcast\"}]]]}],[\"$\",\"div\",\"2\",{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__group\",\"children\":[[\"$\",\"$L2a\",null,{\"href\":\"/events/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__groupTitleLink\",\"children\":\"Events\"}],[[\"$\",\"a\",\"Webinars\",{\"href\":\"/webinars/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Webinars\"}],[\"$\",\"a\",\"Bootcamps\",{\"href\":\"/bootcamps/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Bootcamps\"}],[\"$\",\"$L2a\",\"Builder Connect\",{\"href\":\"/events/builder-connect/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Builder Connect\"}],[\"$\",\"a\",\"Workflow.live\",{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"href\":\"https://www.workflow.live/\",\"target\":\"_blank\",\"rel\":\"noopener noreferrer\",\"children\":[\"Workflow.live\",[\"$\",\"span\",null,{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__externalMark\",\"aria-hidden\":\"true\",\"children\":\"↗\"}]]}]]]}],[\"$\",\"div\",\"3\",{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__group\",\"children\":[null,[[\"$\",\"a\",\"Community\",{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"href\":\"https://community.tines.com/\",\"target\":\"_blank\",\"rel\":\"noopener noreferrer\",\"children\":[\"Community\",[\"$\",\"span\",null,{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__externalMark\",\"aria-hidden\":\"true\",\"children\":\"↗\"}]]}],[\"$\",\"a\",\"YDWWT\",{\"href\":\"/you-did-what-with-tines/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"YDWWT\"}]]]}],[\"$\",\"div\",\"4\",{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__group\",\"children\":[null,[[\"$\",\"a\",\"Customer center\",{\"href\":\"/customer-center/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Customer center\"}]]]}]]}]\n33:[\"$\",\"div\",null,{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__stackedColumn\",\"children\":[[\"$\",\"$L29\",\"Company\",{\"title\":\"Company\",\"children\":[[\"$\",\"div\",\"0\",{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__group\",\"children\":[null,[[\"$\",\"$L2a\",\"About us\",{\"href\":\"/about/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"About us\"}],[\"$\",\"a\",\"Careers\",{\"href\":\"/careers/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Careers\"}],[\"$\",\"a\",\"Newsroom\",{\"href\":\"/newsroom/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Newsroom\"}],[\"$\",\"$L2a\",\"Partners\",{\"href\":\"/partners/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Partners\"}],[\"$\",\"$L2a\",\"Contact\",{\"href\":\"/contact/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Contact\"}],[\"$\",\"a\",\"Store\",{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"href\":\"https://store.tines.com/\",\"target\":\"_blank\",\"rel\":\"noopener noreferrer\",\"children\":[\"Store\",[\"$\",\"span\",null,{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__externalMark\",\"aria-hidden\":\"true\",\"children\":\"↗\"}]]}],[\"$\",\"a\",\"Brand assets\",{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"href\":\"https://live.standards.site/tines\",\"target\":\"_blank\",\"rel\":\"noopener noreferrer\",\"children\":[\"Brand assets\",[\"$\",\"span\",null,{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__externalMark\",\"aria-hidden\":\"true\",\"children\":\"↗\"}]]}]]]}],[\"$\",\"div\",\"1\",{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__group\",\"children\":[null,[[\"$\",\"a\",\"LinkedIn\",{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"href\":\"https://www.linkedin.com/company/tines-io/\",\"target\":\"_blank\",\"rel\":\"noopener noreferrer\",\"children\":[\"LinkedIn\",[\"$\",\"span\",null,{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__externalMark\",\"aria-hidden\":\"true\",\"children\":\"↗\"}]]}],[\"$\",\"a\",\"X\",{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"href\":\"https://twitter.com/tines_hq\",\"target\":\"_blank\",\"rel\":\"noopener noreferrer\",\"children\":[\"X\",[\"$\",\"span\",null,{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__externalMark\",\"aria-hidden\":\"true\",\"children\":\"↗\"}]]}],[\"$\",\"a\",\"YouTube\",{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"href\":\"https://www.youtube.com/@TinesHQ\",\"target\":\"_blank\",\"rel\":\"noopener noreferrer\",\"children\":[\"YouTube\",[\"$\",\"span\",null,{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__externalMark\",\"aria-hidden\":\"true\",\"children\":\"↗\"}]]}]]]}]]}],[\"$\",\"$L29\",\"Trust and security\",{\"title\":\"Trust and security\",\"children\":[[\"$\",\"div\",\"0\",{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__group\",\"children\":[[\"$\",\"a\",null,{\"href\":\"/security/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__groupTitleLink\",\"children\":\"Security\"}],[[\"$\",\"a\",\"Security best practices\",{\"href\":\"/tines-security-best-practices/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Security best practices\"}],[\"$\",\"a\",\"Trust center\",{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"href\":\"https://trust.tines.com/\",\"target\":\"_blank\",\"rel\":\"noopener noreferrer\",\"children\":[\"Trust center\",[\"$\",\"span\",null,{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__externalMark\",\"aria-hidden\":\"true\",\"children\":\"↗\"}]]}]]]}],[\"$\",\"div\",\"1\",{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__group\",\"children\":[[\"$\",\"a\",null,{\"href\":\"/legal/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__groupTitleLink\",\"children\":\"Legal\"}],[[\"$\",\"a\",\"Privacy\",{\"href\":\"/privacy/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Privacy\"}],[\"$\",\"a\",\"Terms\",{\"href\":\"/terms/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Terms\"}],[\"$\",\"a\",\"DPA\",{\"href\":\"/dpa/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"DPA\"}],[\"$\",\"a\",\"Cookies policy\",{\"href\":\"/cookies-policy/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__leaf\",\"children\":\"Cookies policy\"}]]]}]]}]]}]\n35:T44a,M17.6641 0C11.9485 0 6.89478 3.68623 5.18424 9.10302L0.47998 24L5.18424 38.897C6.89479 44.3138 11.9485 48 17.6641 48H30.3358C36.0515 48 41.1052 44.3138 42.8157 38.897L47.52 24L42.8157 9.10302C41.1052 3.68623 36.0515 0 30.3358 0H17.6641ZM30.336 3.74451C34.4042 3.74451 38.0012 6.36822 39.2187 10.2237L40.372 13.8759C38.0518 11.9723 35.1137 10.9008 32.0429 10.9006L15.9228 10.9002C12.8726 10.9004 9.9534 11.9575 7.64056 13.8372L8.78167 10.2237C9.99917 6.36822 13.5962 3.74451 17.6643 3.74451H30.336ZM8.51076 18.2979L5.58159 22.1278L42.3838 22.1278L39.4549 18.2983C37.6948 15.9969 34.9524 14.6453 32.0426 14.6451H15.835C12.958 14.6723 10.2531 16.0197 8.51076 18.2979ZM42.3778 25.8723L39.4547 29.6983C37.6944 32.0023 34.9501 33.3555 32.0381 33.3553L15.9271 33.3548C13.0152 33.3551 10.2708 32.0019 8.51056 29.6979L5.58776 25.8723L42.3778 25.8723ZM8.78145 37.7763C9.99894 41.6318 13.5959 44.2555 17.6641 44.2555H30.3358C34.404 44.2555 38.001 41.6318 39.2185 37.7763L40.3726 34.1214C38.0514 36.0273 35.111 37.1 32.0378 37.0998L15.9274 37.0994C12.8747 37.0996 9.9531 36.0412 7.63915 34.159L8.78145 37.7763Z34:[\"$\",\"div\",null,{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__bottomBar\",\"children\":[[\"$\",\"span\",null,{\"children\":\"© Tines\"}],[\"$\",\"$L2a\",null,{\"href\":\"/\",\"className\":\"SiteFooter26-module-scss-module__LzK7FG__bottomLogo\",\"title\":\"Tines\",\"children\":[\"$\",\"svg\",null,{\"className\":\"tines-logo-icon \",\"width\":28,\"height\":28,\"viewBox\":\"0 0 48 48\",\"fill\":\"currentColor\",\"children\":[\"$\",\"path\",null,{\"fillRule\":\"evenodd\",\"clipRule\":\"evenodd\",\"d\":\"$35\"}]}]}],[\"$\",\"$L36\",null,{\"className\":\"SiteFooter26-module-scss-module__LzK7FG__cookieSettings\"}]]}]\n27:[[\"$\",\"$L37\",null,{}],[\"$\",\"$L38\",null,{\"href\":\"/blog/engineering/\"}],[\"$\",\"script\",null,{\"type\":\"application/ld+json\",\"dangerouslySetInnerHTML\":{\"__html\":\"{\\\"@context\\\":\\\"https://schema.org\\\",\\\"@type\\\":\\\"BlogPosting\\\",\\\"headline\\\":\\\"Zero downtime database migrations: Lessons from moving a live production database\\\",\\\"description\\\":\\\"What went right (and wrong) during a live tenant migration with “zero downtime.\\\\\\\"\\\",\\\"datePublished\\\":\\\"2025-11-04T05:00:00+00:00\\\",\\\"dateModified\\\":\\\"2025-11-12T13:58:21+00:00\\\",\\\"url\\\":\\\"https://www.tines.com/blog/zero-downtime-database-migrations-lessons-from-moving-a-live-production/\\\",\\\"image\\\":\\\"https://www.datocms-assets.com/55802/1762222300-tines-blog-zero-downtime-cover-image.png?auto=format%2Ccompress\u0026fit=max\u0026q=90\u0026w=2400\\\",\\\"author\\\":[{\\\"@type\\\":\\\"Person\\\",\\\"name\\\":\\\"Anna Dowling\\\"}],\\\"publisher\\\":{\\\"@type\\\":\\\"Organization\\\",\\\"name\\\":\\\"Tines\\\",\\\"url\\\":\\\"https://www.tines.com\\\"}}\"}}],[\"$\",\"div\",null,{\"className\":\"$undefined\",\"style\":{\"display\":\"block\",\"width\":\"var(--xl)\",\"height\":\"var(--xl)\",\"flex\":\"0 0 var(--xl)\",\"userSelect\":\"none\"}}],[\"$\",\"header\",null,{\"children\":[null,[\"$\",\"h1\",null,{\"className\":\"ArticlePageHeader-module-scss-module__szLeSa__title\",\"children\":\"Zero downtime database migrations: Lessons from moving a live production database\"}],[\"$\",\"div\",null,{\"className\":\"ArticlePageHeader-module-scss-module__szLeSa__authorInfo\",\"children\":[[\"$\",\"div\",null,{\"className\":\"ArticlePageHeader-module-scss-module__szLeSa__authors\",\"children\":[[\"$\",\"div\",null,{\"className\":\"ArticlePageHeader-module-scss-module__szLeSa__authorPhotos\",\"children\":[[\"$\",\"$L20\",\"QPR-Ix8nQbCoi_jbVb_zUA\",{\"src\":\"https://www.datocms-assets.com/55802/1694620437-anna-d.jpeg?auto=format%2Ccompress\u0026fit=max\u0026q=80\u0026w=128\",\"alt\":\"\",\"className\":\"ArticlePageHeader-module-scss-module__szLeSa__authorPhoto\",\"width\":42,\"height\":42,\"loading\":\"eager\"}]]}],[\"$\",\"div\",null,{\"children\":[[\"$\",\"span\",null,{\"className\":\"ArticlePageHeader-module-scss-module__szLeSa__authorName\",\"children\":[[\"$\",\"span\",null,{\"className\":\"ArticlePageHeader-module-scss-module__szLeSa__writtenBy\",\"children\":\"Written by \"}],[[\"$\",\"span\",\"QPR-Ix8nQbCoi_jbVb_zUA\",{\"children\":[\"Anna Dowling\",\"\"]}]]]}],null]}]]}],[\"$\",\"div\",null,{\"className\":\"ArticlePageHeader-module-scss-module__szLeSa__meta\",\"children\":[[\"$\",\"time\",null,{\"dateTime\":\"2025-11-04T05:00:00.000Z\",\"children\":[\"Published on \",\"November 4, 2025\"]}],[\"$\",\"div\",null,{\"className\":\"ArticlePageHeader-module-scss-module__szLeSa__categoryPills\",\"children\":[[\"$\",\"$L2a\",\"engineering\",{\"href\":\"/blog/engineering/\",\"className\":\"ArticlePageHeader-module-scss-module__szLeSa__categoryPill\",\"children\":\"Engineering\"}]]}]]}]]}]]}],null,[\"$\",\"div\",null,{\"className\":\"$undefined\",\"style\":{\"display\":\"block\",\"width\":\"var(--lg)\",\"height\":\"var(--lg)\",\"flex\":\"0 0 var(--lg)\",\"userSelect\":\"none\"}}],[\"$\",\"div\",null,{\"className\":\"page-module-scss-module__Z5AZta__articleLayout page-module-scss-module__Z5AZta__noToc\",\"children\":[[\"$\",\"aside\",null,{\"className\":\"page-module-scss-module__Z5AZta__articleAside\",\"children\":[\"$\",\"a\",null,{\"href\":\"https://login.tines.com/saml_idp/signup\",\"className\":\"DefaultSidebarContent-module-scss-module__nlQFtG__card\",\"children\":[[\"$\",\"div\",null,{\"className\":\"DefaultSidebarContent-module-scss-module__nlQFtG__text\",\"children\":[[\"$\",\"h3\",null,{\"className\":\"DefaultSidebarContent-module-scss-module__nlQFtG__heading\",\"children\":\"Sign up for Tines 3B Explore Edition\"}],[\"$\",\"p\",null,{\"className\":\"DefaultSidebarContent-module-scss-module__nlQFtG__linkArrow\",\"children\":\"Get started for free\"}]]}],[\"$\",\"$L20\",null,{\"className\":\"DefaultSidebarContent-module-scss-module__nlQFtG__icon\",\"src\":\"/v5-assets/images/usp/blossoming-transformation.svg\",\"alt\":\"\",\"aria-hidden\":\"true\",\"height\":90,\"width\":90}]]}]}],[\"$\",\"div\",null,{\"className\":\"page-module-scss-module__Z5AZta__articleMain\",\"children\":[[\"$\",\"div\",null,{\"className\":\"page-module-scss-module__Z5AZta__coverImage\",\"children\":[\"$\",\"$L20\",null,{\"src\":\"https://www.datocms-assets.com/55802/1762222300-tines-blog-zero-downtime-cover-image.png?auto=format%2Ccompress\u0026fit=max\u0026q=90\u0026w=2400\",\"width\":2400,\"height\":1260,\"alt\":\"Cover image for Zero downtime database migrations: Lessons from moving a live production database\",\"sizes\":\"(max-width: 767px) 100vw, (max-width: 1439px) 70vw, 1040px\",\"quality\":90,\"loading\":\"eager\"}]}],null,\"$L39\",null,null]}]]}],null,\"$L3a\"]\n3b:I[383156,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\",\"/_next/static/immutable/chunks/3pjhofejz57jt.js\",\"/_next/static/immutable/chunks/1q-89ajkqbbkw.js\",\"/_next/static/immutable/chunks/2uho2m59t6w8f.js\",\"/_next/static/immutable/chunks/2ti9pdu9p4n5l.js\",\"/_next/static/immutable/chunks/31qqpjo4d7384.js\",\"/_next/static/immutable/chunks/3zb6qebqmln4d.js\"],\"LinkedHeading\"]\n3c:I[729731,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\",\"/_next/static/immutable/chunks/3pjhofejz57jt.js\",\"/_next/static/immutable/chunks/1q-89ajkqbbkw.js\",\"/_next/static/immutable/chunks/2uho2m59t6w8f.js\",\"/_next/static/immutable/chunks/2ti9pdu9p4n5l.js\",\"/_next/static/immutable/chunks/31qqpjo4d7384.js\",\"/_next/static/immutable/chunks/3zb6qebqmln4d.js\"],\"SmartLink\"]\n39:[\"$\",\"article\",null,{\"className\":\"Article-module-scss-module__ZE_SmG__article page-module-scss-module__Z5AZta__articleContent\",\"children\":[[[\"$\",\"$L3b\",\"0\",{\"level\":2,\"id\":\"introduction\",\"children\":[[\"$\",\"strong\",\"0\",{\"children\":\"Introduction\"}]]}],[\"$\",\"p\",\"1\",{\"children\":[\"If you've ever been involved in a major database migration, you know just how complex and honestly, nerve-wracking they can be. At \",[\"$\",\"$L3c\",\"1\",{\"to\":\"https://www.tines.com/\",\"children\":[\"Tines\"]}],\", we recently faced the challenge of migrating a customer's dedicated tenant by moving all the customer’s \",[\"$\",\"$L3c\",\"3\",{\"to\":\"https://www.tines.com/product/\",\"children\":[\"critical workloads\"]}],\" running on Tines between two different AWS \",[\"$\",\"$L3c\",\"5\",{\"to\":\"https://aws.amazon.com/about-aws/global-infrastructure/regions_az/\",\"children\":[\"Regions\"]}],\". All while maintaining 100% system availability. This wasn't just about moving live data, it was about keeping our automation platform running without missing a single \",[\"$\",\"$L3c\",\"7\",{\"to\":\"https://www.tines.com/docs/actions/types/webhook/\",\"children\":[\"webhook\"]}],\" or disrupting critical customer \",[\"$\",\"$L3c\",\"9\",{\"to\":\"https://www.tines.com/docs/stories/\",\"children\":[\"workflows\"]}],\".\"]}],[\"$\",\"p\",\"2\",{\"children\":[\"The project's scope grew beyond initial expectations, requiring improvements to our application and infrastructure. While we encountered several unexpected challenges, from handling gzipped content in \",[\"$\",\"$L3c\",\"1\",{\"to\":\"https://aws.amazon.com/lambda/edge/\",\"children\":[\"Lambda@Edge\"]}],\" to managing complex database connection states, these obstacles led to valuable improvements in our system's architecture as well as many learnings.\"]}],[\"$\",\"$L3b\",\"3\",{\"level\":2,\"id\":\"more-than-just-moving-live-data\",\"children\":[[\"$\",\"strong\",\"0\",{\"children\":\"More than just moving live data\"}]]}],[\"$\",\"p\",\"4\",{\"children\":[\"We recently had a customer (we refer to them as “tenants” in our system) deployed on our cloud infrastructure in one AWS region, and they wanted to migrate to another region closer to them. This tenant is one of many we host, but each one can process millions of mission-critical workloads per day via Tines application.\"]}],[\"$\",\"p\",\"5\",{\"children\":[\"Before diving into the challenges, it’s worth explaining briefly what a “busy Tines instance” looks like. A customer’s Tines application is constantly performing \",[\"$\",\"$L3c\",\"1\",{\"to\":\"https://www.tines.com/docs/actions/\",\"children\":[\"action runs\"]}],\". These are automated tasks triggered by external events like incoming webhooks or \",[\"$\",\"$L3c\",\"3\",{\"to\":\"https://explained.tines.com/en/articles/7843861-how-do-i-schedule-my-story\",\"children\":[\"scheduled\"]}],\" stories which can produce a high volume of data. This level of activity is exactly why the migration had to be seamless: any disruption could impact thousands (or even millions) of automated processes that our customers rely on.\"]}],[\"$\",\"$L3b\",\"6\",{\"level\":3,\"id\":\"the-challenge\",\"children\":[[\"$\",\"strong\",\"0\",{\"children\":\"The challenge?\"}]]}],[\"$\",\"p\",\"7\",{\"children\":[\"Moving this specific tenant's deployment to a new region without dropping any data or disrupting any of these workloads. Oh, and downtime? That's not an option.\"]}],[\"$\",\"p\",\"8\",{\"children\":[\"Let me clarify what that actually means in practice:\"]}],[\"$\",\"ul\",\"9\",{\"children\":[[\"$\",\"li\",\"0\",{\"children\":[[\"$\",\"p\",\"0\",{\"children\":[[\"$\",\"em\",\"0\",{\"children\":\"It’s acceptable if some requests or event deliveries are delayed during the migration.\"}]]}]]}],[\"$\",\"li\",\"1\",{\"children\":[[\"$\",\"p\",\"0\",{\"children\":[[\"$\",\"em\",\"0\",{\"children\":\"It’s acceptable if web response times are temporarily slower than usual.\"}]]}]]}],[\"$\",\"li\",\"2\",{\"children\":[[\"$\",\"p\",\"0\",{\"children\":[[\"$\",\"em\",\"0\",{\"children\":\"It’s not acceptable to lose or drop any events permanently.\"}]]}]]}],\"$L3d\",\"$L3e\"]}],\"$L3f\",\"$L40\",\"$L41\",\"$L42\",\"$L43\",\"$L44\",\"$L45\",\"$L46\",\"$L47\",\"$L48\",\"$L49\",\"$L4a\",\"$L4b\",\"$L4c\",\"$L4d\",\"$L4e\",\"$L4f\",\"$L50\",\"$L51\",\"$L52\",\"$L53\",\"$L54\",\"$L55\",\"$L56\",\"$L57\",\"$L58\",\"$L59\",\"$L5a\",\"$L5b\",\"$L5c\",\"$L5d\",\"$L5e\",\"$L5f\",\"$L60\",\"$L61\",\"$L62\",\"$L63\",\"$L64\",\"$L65\",\"$L66\",\"$L67\",\"$L68\",\"$L69\",\"$L6a\",\"$L6b\",\"$L6c\",\"$L6d\",\"$L6e\",\"$L6f\",\"$L70\",\"$L71\",\"$L72\",\"$L73\",\"$L74\",\"$L75\",\"$L76\",\"$L77\",\"$L78\",\"$L79\",\"$L7a\",\"$L7b\",\"$L7c\",\"$L7d\",\"$L7e\",\"$L7f\",\"$L80\",\"$L81\",\"$L82\",\"$L83\",\"$L84\",\"$L85\",\"$L86\",\"$L87\",\"$L88\",\"$L89\"],\"$L8a\"]}]\n3a:[\"$\",\"div\",null,{\"className\":\"$undefined\",\"style\":{\"display\":\"block\",\"width\":\"var(--sectionPadding)\",\"height\":\"var(--sectionPadding)\",\"flex\":\"0 0 var(--sectionPadding)\",\"userSelect\":\"none\"}}]\n8b:I[285528,[\"/_next/static/immutable/chunks/3sz971vfk095f.js\",\"/_next/static/immutable/chunks/0czgivygkxa1z.js\",\"/_next/static/immutable/chunks/2kpu_ktjkwf7q.js\",\"/_next/static/immutable/chunks/07hv-f84dhd61.js\",\"/_next/static/immutable/chunks/0639nwpv6l0pm.js\",\"/_next/static/immutable/chunks/2826pm12av2bw.js\",\"/_next/static/immutable/chunks/2_swgt_1o6v9a.js\",\"/_next/static/immutable/chunks/3pjhofejz57jt.js\",\"/_next/static/immutable/chunks/1q-89ajkqbbkw.js\",\"/_next/static/immutable/chunks/2uho2m59t6w8f.js\",\"/_next/static/immutable/chunks/2ti9pdu9p4n5l.js\",\"/_next/static/immutable/chunks/31qqpjo4d7384.js\",\"/_next/static/immutable/chunks/3zb6qebqmln4d.js\"],\"CodeBlockCopy\"]\n3d:[\"$\",\"li\",\"3\",{\"children\":[[\"$\",\"p\",\"0\",{\"children\":[[\"$\",\"em\",\"0\",{\"children\":\"It’s not acceptable to lose any database operations i.e. INSERTs, UPDATEs, or DELETEs.\"}]]}]]}]\n3e:[\"$\",\"li\",\"4\",{\"children\":[[\"$\",\"p\",\"0\",{\"children\":[[\"$\",\"em\",\"0\",{\"children\":\"And above all, we must maintain our data integrity guarantees at all times.\"}]]}]]}]\n3f:[\"$\",\"p\",\"10\",{\"children\":[\"For context, the diagram below provides a high-level overview of our deployment architecture before the migration.\"]}]\n40:[\"$\",\"p\",\"11\",{\"children\":[\"As shown, each tenant’s environment consists of a dedicated \",[\"$\",\"$L3c\",\"1\",{\"to\":\"https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html\",\"children\":[\"AWS Application Load Balancer\"]}],\" (ALB) routing traffic to \",[\"$\",\"$L3c\",\"3\",{\"to\":\"https://aws.amazon.com/ecs/\",\"children\":[\"ECS\"]}],\" web and worker services running Tines application, which in turn interact with \",[\"$\",\"$L3c\",\"5\",{\"to\":\"https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/Aurora.AuroraPostgreSQL.html\",\"children\":[\"AWS Aurora PostgreSQL\"]}],\" for database storage and AWS \",[\"$\",\"$L3c\",\"7\",{\"to\":\"https://aws.amazon.com/elasticache/\",\"children\":[\"ElastiCache\"]}],\" for Redis to support fast, in-memory operations. This setup is designed to handle high volumes of mission-critical workloads with reliability and scalability.\"]}]\n41:[\"$\",\"figure\",\"12\",{\"className\":\"ImageBlock-module-scss-module__0Ef4dq__imageBlock\",\"children\":[[\"$\",\"img\",null,{\"src\":\"https://www.datocms-assets.com/55802/1762222651-zdt-pre-state-drawio.png?auto=format%2Ccompress\u0026fit=max\u0026q=90\u0026w=2000\u0026auto=format,compress\u0026q=90\u0026w=2000\u0026fit=max\",\"alt\":\"\",\"width\":491,\"height\":701,\"loading\":\"lazy\"}],[\"$\",\"figcaption\",null,{\"className\":\"ImageBlock-module-scss-module__0Ef4dq__imageCaption\",\"children\":[[\"$\",\"p\",\"p-0\",{\"children\":\"Tines AWS deployment architecture pre-migration\"}]]}]]}]\n42:[\"$\",\"$L3b\",\"13\",{\"level\":2,\"id\":\"building-a-robust-migration-framework\",\"children\":[[\"$\",\"strong\",\"0\",{\"children\":\"Building a robust migration framework\"}]]}]\n43:[\"$\",\"p\",\"14\",{\"children\":[\"The diagram below illustrates how requests, data, and replication traffic flowed between the \",[\"$\",\"code\",\"1\",{\"children\":\"source\"}],\" and \",[\"$\",\"code\",\"3\",{\"children\":\"destination\"}],\" customer deployments during the migration. As you will see, many components worked together to keep everything in sync and available. In the following sections, we’ll discuss each part of this framework and how we implemented it in detail.\"]}]\n44:[\"$\",\"figure\",\"15\",{\"className\":\"ImageBlock-module-scss-module__0Ef4dq__imageBlock\",\"children\":[[\"$\",\"img\",null,{\"src\":\"https://www.datocms-assets.com/55802/1762222692-zdtmigration-drawio.png?auto=format%2Ccompress\u0026fit=max\u0026q=90\u0026w=2000\u0026auto=format,compress\u0026q=90\u0026w=2000\u0026fit=max\",\"alt\":\"\",\"width\":1031,\"height\":1371,\"loading\":\"lazy\"}],[\"$\",\"figcaption\",null,{\"className\":\"ImageBlock-module-scss-module__0Ef4dq__imageCaption\",\"children\":[[\"$\",\"p\",\"p-0\",{\"children\":\"Tines AWS deployment architecture for migration\"}]]}]]}]\n45:[\"$\",\"$L3b\",\"16\",{\"level\":3,\"id\":\"smart-request-routing-with-lambdaedge\",\"children\":[[\"$\",\"strong\",\"0\",{\"children\":\"Smart request routing with Lambda@Edge\"}]]}]\n46:[\"$\",\"p\",\"17\",{\"children\":[\"One of our biggest challenges was handling incoming requests during the migration. We needed a solution that could:\"]}]\n47:[\"$\",\"ul\",\"18\",{\"children\":[[\"$\",\"li\",\"0\",{\"children\":[[\"$\",\"p\",\"0\",{\"children\":[\"Route requests to the appropriate environment, specifically, the new region deployment while ensuring no requests are dropped.\"]}]]}],[\"$\",\"li\",\"1\",{\"children\":[[\"$\",\"p\",\"0\",{\"children\":[\"Handle failover scenarios\"]}]]}],[\"$\",\"li\",\"2\",{\"children\":[[\"$\",\"p\",\"0\",{\"children\":[\"Process both normal and gzipped content\"]}]]}],[\"$\",\"li\",\"3\",{\"children\":[[\"$\",\"p\",\"0\",{\"children\":[\"Maintain session consistency\"]}]]}]]}]\n48:[\"$\",\"p\",\"19\",{\"children\":[\"We used AWS \",[\"$\",\"$L3c\",\"1\",{\"to\":\"https://aws.amazon.com/lambda/edge/\",\"children\":[\"Lambda@Edge\"]}],\" to solve for this missing connector. Our implementation became more sophisticated than initially planned. We discovered that handling gzipped content and dealing with \",[\"$\",\"$L3c\",\"3\",{\"to\":\"https://aws.amazon.com/cloudfront/\",\"children\":[\"CloudFront\"]}],\" limitations required special attention.\"]}]\n49:[\"$\",\"p\",\"20\",{\"children\":[[\"$\",\"strong\",\"0\",{\"children\":\"Gzipped content\"}]]}]\n4a:[\"$\",\"p\",\"21\",{\"children\":[\"One challenge we faced was that when our origin server (the Tines app serving GraphQL requests) returned gzipped content, the Lambda@Edge function received the compressed data, which isn’t directly readable or modifiable. This issue did not affect our /webhooks endpoint, as it did not return gzipped responses. To inspect or modify the response body, we had to detect if the content was gzipped and then decompress it within the Lambda function using Node’s \",[\"$\",\"$L3c\",\"1\",{\"to\":\"https://nodejs.org/api/zlib.html\",\"children\":[[\"$\",\"code\",\"0\",{\"children\":\"zlib\"}]]}],\" library. This added complexity, especially since we also needed to ensure that the final response sent to the client had the correct headers and wasn’t double-compressed or missing the appropriate Content-Encoding.\"]}]\n4b:[\"$\",\"p\",\"22\",{\"children\":[[\"$\",\"strong\",\"0\",{\"children\":\"Header filtering\"}]]}]\n4c:[\"$\",\"p\",\"23\",{\"children\":[\"CloudFront imposes restrictions on which headers can be set or forwarded by Lambda@Edge. Some headers, like \",[\"$\",\"code\",\"1\",{\"children\":\"Content-Length, Transfer-Encoding\"}],\", and certain connection-related headers, are either managed by CloudFront or disallowed entirely. We had to carefully filter out these headers in our Lambda response to avoid errors or unexpected behavior. This required extra logic to sanitize the response headers and ensure compatibility with CloudFront’s requirements.\"]}]\n4d:[\"$\",\"div\",\"24\",{\"className\":\"StructuredTextBlock-module-scss-module__LuuGDq__structuredTextBlock\",\"data-backdrop\":\"Canvas\",\"data-color-theme\":\"$undefined\",\"data-appearance\":\"$undefined\",\"children\":[[\"$\",\"$1\",\"t-0\",{\"children\":[[\"$\",\"p\",\"t-0\",{\"children\":[[\"$\",\"$1\",\"t-0\",{\"children\":[\" 💡 See gist of the Lambda@Edge Function here: \"]}]]}],[\"$\",\"p\",\"t-1\",{\"children\":[[\"$\",\"a\",\"t-0\",{\"href\":\"https://gist.github.com/annadowling/4d605be38706184060cd837a37a77397\",\"children\":[[\"$\",\"$1\",\"t-0\",{\"children\":[\"https://gist.github.com/annadowling/4d605be38706184060cd837a37a77397\"]}]]}]]}]]}]]}]\n4e:[\"$\",\"$L3b\",\"25\",{\"level\":3,\"id\":\"database-and-application-synchronization-strategy\",\"children\":[[\"$\",\"strong\",\"0\",{\"children\":\"Database and application synchronization strategy\"}]]}]\n4f:[\"$\",\"p\",\"26\",{\"children\":[\"When it came to migrating the customer’s database, we needed a way to keep data in sync between the old and new regions with minimal disruption. This is where a **blue/green deployment** approach came into play: we maintained both the old (“blue”) and new (“green”) environments in parallel, allowing us to switch traffic with minimal downtime and risk. To enable this, we used \",[\"$\",\"$L3c\",\"1\",{\"to\":\"https://www.postgresql.org/docs/current/logical-replication.html\",\"children\":[[\"$\",\"strong\",\"0\",{\"children\":\"Logical Replication\"}]]}],\". Logical replication in PostgreSQL allows you to replicate data and stream changes (like INSERTs, UPDATEs, and DELETEs) from one database to another in near real-time, making it possible to keep two databases synchronized even as new data is being written. Below, we’ll cover the three key areas we instrumented for the database migration.\"]}]\n50:[\"$\",\"p\",\"27\",{\"children\":[[\"$\",\"strong\",\"0\",{\"children\":\"Logical replication with pg_easy_replicate\"}]]}]\n51:[\"$\",\"p\",\"28\",{\"children\":[\"We used \",[\"$\",\"$L3c\",\"1\",{\"to\":\"https://github.com/shayonj/pg_easy_replicate\",\"children\":[\"pg_easy_replicate\"]}],\", an open-source tool that simplifies setting up and managing logical replication. Out of the box, it handles a lot of the heavy lifting for replication setup very well.\"]}]\n52:[\"$\",\"figure\",\"29\",{\"className\":\"ImageBlock-module-scss-module__0Ef4dq__imageBlock\",\"children\":[[\"$\",\"img\",null,{\"src\":\"https://www.datocms-assets.com/55802/1762222860-image-2.png?auto=format%2Ccompress\u0026fit=max\u0026q=90\u0026w=2000\u0026auto=format,compress\u0026q=90\u0026w=2000\u0026fit=max\",\"alt\":\"\",\"width\":541,\"height\":586,\"loading\":\"lazy\"}],null]}]\n53:[\"$\",\"p\",\"30\",{\"children\":[\"As we got deeper into our migration implementation, we realized we needed some additional features to meet our requirements. This led us to contribute several enhancements back to the project, including:\"]}]\n54:[\"$\",\"ul\",\"31\",{\"children\":[[\"$\",\"li\",\"0\",{\"children\":[[\"$\",\"p\",\"0\",{\"children\":[\"Advanced logging and debugging capabilities in \",[\"$\",\"code\",\"1\",{\"children\":\"pg_easy_replicate\"}],\" for better visibility into the replication process\"]}]]}],[\"$\",\"li\",\"1\",{\"children\":[[\"$\",\"p\",\"0\",{\"children\":[\"Development of the \",[\"$\",\"$L3c\",\"1\",{\"to\":\"https://github.com/shayonj/pg_easy_replicate?tab=readme-ov-file#exclude-tables-from-replication\",\"children\":[[\"$\",\"code\",\"0\",{\"children\":\"[exclude-tables]\"}]]}],\"command to allow selective table replication, giving teams more control over their migration process\"]}]]}],[\"$\",\"li\",\"2\",{\"children\":[[\"$\",\"p\",\"0\",{\"children\":[\"Creation of the \",[\"$\",\"$L3c\",\"1\",{\"to\":\"https://github.com/shayonj/pg_easy_replicate?tab=readme-ov-file#notify\",\"children\":[[\"$\",\"code\",\"0\",{\"children\":\"[notify]\"}]]}],\" command to send replication statistics to an endpoint on an interval, enabling real-time monitoring\"]}]]}]]}]\n55:[\"$\",\"p\",\"32\",{\"children\":[\"These enhancements not only supported our migration but also improved the tool for the broader PostgreSQL community. The ability to exclude specific tables and monitor replication progress in real-time proved invaluable for managing our large-scale migration with confidence.\"]}]\n56:[\"$\",\"p\",\"33\",{\"children\":[[\"$\",\"strong\",\"0\",{\"children\":\"At the database level, here’s how logical replication worked in practice during our migration:\"}]]}]\n57:[\"$\",\"p\",\"34\",{\"children\":[[\"$\",\"code\",\"0\",{\"children\":[\"$\",\"strong\",null,{\"children\":\"Source database\"}]}]]}]\n58:[\"$\",\"p\",\"35\",{\"children\":[\"This query shows the replication slot is active, and the increase in retained WAL is expected with ongoing activity.\"]}]\n59:[\"$\",\"figure\",\"36\",{\"className\":\"ImageBlock-module-scss-module__0Ef4dq__imageBlock\",\"children\":[[\"$\",\"img\",null,{\"src\":\"https://www.datocms-assets.com/55802/1762222901-queryupdate-drawio.png?auto=format%2Ccompress\u0026fit=max\u0026q=90\u0026w=2000\u0026auto=format,compress\u0026q=90\u0026w=2000\u0026fit=max\",\"alt\":\"\",\"width\":897,\"height\":205,\"loading\":\"lazy\"}],[\"$\",\"figcaption\",null,{\"className\":\"ImageBlock-module-scss-module__0Ef4dq__imageCaption\",\"children\":[[\"$\",\"p\",\"p-0\",{\"children\":\"psql query and output for source database logical replication\"}]]}]]}]\n5a:[\"$\",\"p\",\"37\",{\"children\":[[\"$\",\"code\",\"0\",{\"children\":[\"$\",\"strong\",null,{\"children\":\"Target database\"}]}]]}]\n5b:[\"$\",\"p\",\"38\",{\"children\":[\"On the target, this query shows the received LSN and latest end LSN match, with minimal delay—showing logical replication running smoothly between regions.\"]}]\n5c:[\"$\",\"figure\",\"39\",{\"className\":\"ImageBlock-module-scss-module__0Ef4dq__imageBlock\",\"children\":[[\"$\",\"img\",null,{\"src\":\"https://www.datocms-assets.com/55802/1762222957-query2-drawio.png?auto=format%2Ccompress\u0026fit=max\u0026q=90\u0026w=2000\u0026auto=format,compress\u0026q=90\u0026w=2000\u0026fit=max\",\"alt\":\"\",\"width\":1157,\"height\":97,\"loading\":\"lazy\"}],[\"$\",\"figcaption\",null,{\"className\":\"ImageBlock-module-scss-module__0Ef4dq__imageCaption\",\"children\":[[\"$\",\"p\",\"p-0\",{\"children\":\"psql query and output for target database logical replication\"}]]}]]}]\n5d:[\"$\",\"p\",\"40\",{\"children\":[[\"$\",\"strong\",\"0\",{\"children\":\"2. PostgreSQL BLOB data synchronization\"}]]}]\n5e:[\"$\",\"p\",\"41\",{\"children\":[\"A challenge emerged when we discovered PostgreSQL 14.10 \",[\"$\",\"$L3c\",\"1\",{\"to\":\"https://www.postgresql.org/docs/current/logical-replication-restrictions.html#LOGICAL-REPLICATION-RESTRICTIONS\",\"children\":[\"doesn't support\"]}],\" Large Object(BLOBs) replication natively. While logical replication handled most of our data effectively, BLOBs weren't supported yet and Tines stores customer data such as saved images and case attachments.\"]}]\n5f:[\"$\",\"p\",\"42\",{\"children\":[\"This required us to develop a custom synchronization solution using a Ruby \",[\"$\",\"$L3c\",\"1\",{\"to\":\"https://sidekiq.org/\",\"children\":[\"Sidekiq\"]}],\" job. The real complexity involved managing database connections during that sync process.\"]}]\n60:[\"$\",\"p\",\"43\",{\"children\":[\"We implemented a connection switching mechanism to handle the export of the blob data from the source database and import into the target database:\"]}]\n61:[\"$\",\"div\",\"44\",{\"className\":\"StructuredTextBlock-module-scss-module__LuuGDq__structuredTextBlock\",\"data-backdrop\":\"Canvas\",\"data-color-theme\":\"$undefined\",\"data-appearance\":\"$undefined\",\"children\":[[\"$\",\"$1\",\"t-0\",{\"children\":[[\"$\",\"p\",\"t-0\",{\"children\":[[\"$\",\"$1\",\"t-0\",{\"children\":[\"💡 \"]}],[\"$\",\"strong\",\"t-1\",{\"children\":[\"$\",\"$1\",\"t-1\",{\"children\":[\"See gist of the \"]}]}],[\"$\",\"code\",\"t-2\",{\"children\":[\"$\",\"strong\",\"t-2\",{\"children\":[\"$\",\"$1\",\"t-2\",{\"children\":[\"DbConnectionSwitcher\"]}]}]}],[\"$\",\"strong\",\"t-3\",{\"children\":[\"$\",\"$1\",\"t-3\",{\"children\":[\" class here:\"]}]}],[\"$\",\"$1\",\"t-4\",{\"children\":[\" \"]}],[\"$\",\"a\",\"t-5\",{\"href\":\"https://gist.github.com/annadowling/90db3a33a50cb01e2a293ff5c37702d4\",\"children\":[[\"$\",\"$1\",\"t-0\",{\"children\":[\"https://gist.github.com/annadowling/90db3a33a50cb01e2a293ff5c37702d4\"]}]]}]]}]]}]]}]\n62:[\"$\",\"p\",\"45\",{\"children\":[[\"$\",\"strong\",\"0\",{\"children\":\"3. Reliability improvements for Tines features\"}]]}]\n63:[\"$\",\"p\",\"46\",{\"children\":[\"A critical part of ensuring zero data loss during migration was improving the Tines \",[\"$\",\"$L3c\",\"1\",{\"to\":\"https://www.tines.com/docs/actions/types/event-transformation/\",\"children\":[\"Event Transform action\"]}],\" runs. Specifically the mechanisms for \",[\"$\",\"code\",\"3\",{\"children\":\"Throttle\"}],\" and \",[\"$\",\"code\",\"5\",{\"children\":\"Delay\"}],\" even transformation mode \",[\"$\",\"strong\",\"7\",{\"children\":\"types\"}],\". Previously, these features relied on our \",[\"$\",\"$L3c\",\"9\",{\"to\":\"https://github.com/redis/redis\",\"children\":[\"Redis\"]}],\" setup for keeping track of the referential IDs which would be read when enqueuing action runs. For the migration, we moved this logic to database-backed persistence using \",[\"$\",\"$L3c\",\"11\",{\"to\":\"https://www.postgresql.org/\",\"children\":[\"PostgreSQL\"]}],\" tables with \",[\"$\",\"$L3c\",\"13\",{\"to\":\"https://guides.rubyonrails.org/active_record_basics.html\",\"children\":[\"ActiveRecord\"]}],\" in the \",[\"$\",\"$L3c\",\"15\",{\"to\":\"https://rubyonrails.org/\",\"children\":[\"Rails\"]}],\" application.\"]}]\n64:[\"$\",\"p\",\"47\",{\"children\":[\"It’s important to clarify that this change was about how Tines models and tracks references for delayed and throttled events. We treat data in Redis as ephemeral. Any loss of data inside Redis shouldn’t impact application performance or reliability. This is why we didn’t want to invest resources into replicating our Redis data across regions. For us, the priority was to ensure that no event references were lost, and that the timing and sequencing of delayed or throttled events would be preserved, even as we moved between regions.\"]}]\n65:[\"$\",\"p\",\"48\",{\"children\":[\"By enhancing the \",[\"$\",\"$L3c\",\"1\",{\"to\":\"https://www.tines.com/docs/actions/types/event-transformation/delay/\",\"children\":[\"delay mode\"]}],\" \u0026 \",[\"$\",\"$L3c\",\"3\",{\"to\":\"https://www.tines.com/docs/actions/types/event-transformation/throttle/\",\"children\":[\"throttle mode\"]}],\" to use database persistence, we were able to:\"]}]\n66:[\"$\",\"ul\",\"49\",{\"children\":[[\"$\",\"li\",\"0\",{\"children\":[[\"$\",\"p\",\"0\",{\"children\":[\"Prevent the loss of any delayed or throttled events during the transition.\"]}]]}],[\"$\",\"li\",\"1\",{\"children\":[[\"$\",\"p\",\"0\",{\"children\":[\"Maintain the precise timing and intervals for scheduled and rate-limited event emission, even in the middle of a migration.\"]}]]}]]}]\n67:[\"$\",\"p\",\"50\",{\"children\":[\"This approach gave us the reliability we needed for the migration, without the risk of losing critical event data.\"]}]\n68:[\"$\",\"$L3b\",\"51\",{\"level\":3,\"id\":\"vpc-peering\",\"children\":[\"VPC Peering\"]}]\n69:[\"$\",\"p\",\"52\",{\"children\":[\"A pre-requisite for configuring cross-region logical replication was establishing secure, low-latency network connectivity between the source and destination AWS environments. Logical replication in PostgreSQL requires the source and target databases to communicate directly, which isn’t possible by default when they’re in separate VPCs. To solve this, we leveraged AWS’s \",[\"$\",\"$L3c\",\"1\",{\"to\":\"https://docs.aws.amazon.com/vpc/latest/userguide/vpc-peering.html\",\"children\":[[\"$\",\"strong\",\"0\",{\"children\":\"VPC peering\"}]]}],\".\"]}]\n6a:[\"$\",\"p\",\"53\",{\"children\":[\"Instead of manually configuring everything, we built a reusable VPC peering setup with AWS CDK. This automated the whole process and gave us a template we can re-use for future cases.\"]}]\n6b:[\"$\",\"p\",\"54\",{\"children\":[\"Here’s how our CDK implementation works:\"]}]\n6c:[\"$\",\"ul\",\"55\",{\"children\":[[\"$\",\"li\",\"0\",{\"children\":[[\"$\",\"p\",\"0\",{\"children\":[[\"$\",\"strong\",\"0\",{\"children\":\"Bidirectional routing:\"}],\" The stack configures peering and updates route tables on both the requester (source) and provider (destination) sides, ensuring traffic can flow in both directions as needed for replication and application communication.\"]}]]}],[\"$\",\"li\",\"1\",{\"children\":[[\"$\",\"p\",\"0\",{\"children\":[[\"$\",\"strong\",\"0\",{\"children\":\"Security group rules:\"}],\" We programmatically add security group ingress rules to allow PostgreSQL traffic (port 5432) between the peered VPCs.\"]}]]}],[\"$\",\"li\",\"2\",{\"children\":[[\"$\",\"p\",\"0\",{\"children\":[[\"$\",\"strong\",\"0\",{\"children\":\"Parameterization:\"}],\" The configuration is driven by parameters (VPC IDs, CIDRs, security group IDs, etc.), making it easy to adapt for any customer deployment.\"]}]]}]]}]\n6d:[\"$\",\"p\",\"56\",{\"children\":[\"\"]}]\n6e:[\"$\",\"div\",\"57\",{\"className\":\"StructuredTextBlock-module-scss-module__LuuGDq__structuredTextBlock\",\"data-backdrop\":\"Canvas\",\"data-color-theme\":\"$undefined\",\"data-appearance\":\"$undefined\",\"children\":[[\"$\",\"$1\",\"t-0\",{\"children\":[[\"$\",\"p\",\"t-0\",{\"children\":[[\"$\",\"$1\",\"t-0\",{\"children\":[\"💡 See gist of CDK VPCPeeringConfiguration here: \"]}],[\"$\",\"a\",\"t-1\",{\"href\":\"https://gist.github.com/annadowling/6c5abcc6afbf9be45c52562e109aebb6\",\"children\":[[\"$\",\"$1\",\"t-0\",{\"children\":[\"https://gist.github.com/annadowling/6c5abcc6afbf9be45c52562e109aebb6\"]}]]}]]}]]}]]}]\n6f:[\"$\",\"$L3b\",\"58\",{\"level\":2,\"id\":\"migration-automation-with-tines\",\"children\":[[\"$\",\"strong\",\"0\",{\"children\":\"Migration automation with Tines\"}]]}]\n70:[\"$\",\"p\",\"59\",{\"children\":[\"Rather than rely on manual processes, we automated the migration through a Tines story. The automation workflow managed four critical phases:\"]}]\n71:[\"$\",\"ul\",\"60\",{\"children\":[[\"$\",\"li\",\"0\",{\"children\":[[\"$\",\"p\",\"0\",{\"children\":[[\"$\",\"strong\",\"0\",{\"children\":\"Setup\"}],\" - Resource validation and health check\"]}]]}],[\"$\",\"li\",\"1\",{\"children\":[[\"$\",\"p\",\"0\",{\"children\":[[\"$\",\"strong\",\"0\",{\"children\":\"Replication\"}],\" - Database synchronization and monitoring\"]}]]}],[\"$\",\"li\",\"2\",{\"children\":[[\"$\",\"p\",\"0\",{\"children\":[[\"$\",\"strong\",\"0\",{\"children\":\"Switchover\"}],\" - Traffic routing and environment transition\"]}]]}],[\"$\",\"li\",\"3\",{\"children\":[[\"$\",\"p\",\"0\",{\"children\":[[\"$\",\"strong\",\"0\",{\"children\":\"Post-migration\"}],\" - Cleanup and verification\"]}]]}]]}]\n72:[\"$\",\"p\",\"61\",{\"children\":[\"Each phase included comprehensive error handling, with real-time monitoring through Slack notifications.\"]}]\n73:[\"$\",\"figure\",\"62\",{\"className\":\"ImageBlock-module-scss-module__0Ef4dq__imageBlock\",\"children\":[[\"$\",\"img\",null,{\"src\":\"https://www.datocms-assets.com/55802/1762223107-zero-downtime-dedicated-tenant-migration-storyboard.png?auto=format%2Ccompress\u0026fit=max\u0026q=90\u0026w=2000\u0026auto=format,compress\u0026q=90\u0026w=2000\u0026fit=max\",\"alt\":\"\",\"width\":5220,\"height\":5332,\"loading\":\"lazy\"}],[\"$\",\"figcaption\",null,{\"className\":\"ImageBlock-module-scss-module__0Ef4dq__imageCaption\",\"children\":[[\"$\",\"p\",\"p-0\",{\"children\":\"Migration story in Tines\"}]]}]]}]\n74:[\"$\",\"$L3b\",\"63\",{\"level\":3,\"id\":\"the-art-of-switching-over-orchestrating-failover\",\"children\":[[\"$\",\"strong\",\"0\",{\"children\":\"The art of switching over: orchestrating failover\"}]]}]\n75:[\"$\",\"p\",\"64\",{\"children\":[\"One of the most critical parts of our migration was the actual “failover” to the target deployment / database. This was the moment when we switched all application traffic from the old region to the new one. This was triggered via the story using the \",[\"$\",\"code\",\"1\",{\"children\":\"switchover\"}],\" command input. In our context, failover meant deliberately making the source deployment unavailable for writes, so that all new requests would be routed to the destination (new region) deployment.\"]}]\n76:[\"$\",\"p\",\"65\",{\"children\":[\"To achieve this, we put the source database into \",[\"$\",\"strong\",\"1\",{\"children\":\"READ ONLY mode\"}],\" using the \",[\"$\",\"$L3c\",\"3\",{\"to\":\"https://github.com/shayonj/pg_easy_replicate?tab=readme-ov-file#performing-switchover\",\"children\":[\"pg_easy_replicate switchover command\"]}],\". This meant any write attempts (INSERT, UPDATE, DELETE) would fail, causing the application to return 5xx errors for those operations.\"]}]\n77:[\"$\",\"p\",\"66\",{\"children\":[\"Our Lambda@Edge function was designed to detect these 5xx responses from the source environment. When it saw them, it would automatically reroute requests to the new region deployment.\"]}]\n78:[\"$\",\"p\",\"67\",{\"children\":[\"This approach ensured a clean cutover by preventing new data from being written to the old deployment, while Lambda@Edge automated the requests failover. As a result, downtime was minimal and the transition appeared nearly instantaneous to users.\"]}]\n79:[\"$\",\"figure\",\"68\",{\"className\":\"ImageBlock-module-scss-module__0Ef4dq__imageBlock\",\"children\":[[\"$\",\"img\",null,{\"src\":\"https://www.datocms-assets.com/55802/1762223261-switchover-drawio.png?auto=format%2Ccompress\u0026fit=max\u0026q=90\u0026w=2000\u0026auto=format,compress\u0026q=90\u0026w=2000\u0026fit=max\",\"alt\":\"\",\"width\":1411,\"height\":361,\"loading\":\"lazy\"}],[\"$\",\"figcaption\",null,{\"className\":\"ImageBlock-module-scss-module__0Ef4dq__imageCaption\",\"children\":[[\"$\",\"p\",\"p-0\",{\"children\":\"Slack output of switchover monitoring\"}]]}]]}]\n7a:[\"$\",\"$L3b\",\"69\",{\"level\":2,\"id\":\"what-we-learned\",\"children\":[[\"$\",\"strong\",\"0\",{\"children\":\"What we learned\"}]]}]\n7b:[\"$\",\"p\",\"70\",{\"children\":[[\"$\",\"code\",\"0\",{\"children\":[\"$\",\"strong\",null,{\"children\":\"What went well\"}]}]]}]\n7c:[\"$\",\"p\",\"71\",{\"children\":[\"Our logical replication strategy was robust. We achieved zero data loss, real-time monitoring gave us the visibility we needed throughout, and schema synchronization worked without a hitch. In addition we improved the design and reliability of a number of core product areas including the delay and throttle modes event transform action.\"]}]\n7d:[\"$\",\"figure\",\"72\",{\"className\":\"ImageBlock-module-scss-module__0Ef4dq__imageBlock\",\"children\":[[\"$\",\"img\",null,{\"src\":\"https://www.datocms-assets.com/55802/1762223304-image-3.png?auto=format%2Ccompress\u0026fit=max\u0026q=90\u0026w=2000\u0026auto=format,compress\u0026q=90\u0026w=2000\u0026fit=max\",\"alt\":\"\",\"width\":324,\"height\":102,\"loading\":\"lazy\"}],null]}]\n7e:[\"$\",\"p\",\"73\",{\"children\":[\"Beyond the success of this migration, the frameworks and automation we built will benefit future projects. With more than 100 PostgreSQL Aurora clusters running in our cloud, the tools, processes, and lessons from this experience will be a huge help when it comes time to upgrade or make large-scale changes across our entire fleet.\"]}]\n7f:[\"$\",\"p\",\"74\",{\"children\":[\"We also created a reusable VPC peering setup, making it easy to securely connect databases across regions and pave the way for future use cases. Lambda@Edge request routing also exceeded our expectations. It handled some pretty complex routing scenarios, provided seamless failover, and kept webhook processing running smoothly during the entire migration. You can see clearly in our Honeycomb graph that after the switchover, when the source stack started returning 500 responses (yellow line), requests were seamlessly re-routed to the new stack (green line).\"]}]\n80:[\"$\",\"figure\",\"75\",{\"className\":\"ImageBlock-module-scss-module__0Ef4dq__imageBlock\",\"children\":[[\"$\",\"img\",null,{\"src\":\"https://www.datocms-assets.com/55802/1762871536-image-4.png?auto=format%2Ccompress\u0026fit=max\u0026q=90\u0026w=2000\u0026auto=format,compress\u0026q=90\u0026w=2000\u0026fit=max\",\"alt\":\"\",\"width\":829,\"height\":330,\"loading\":\"lazy\"}],[\"$\",\"figcaption\",null,{\"className\":\"ImageBlock-module-scss-module__0Ef4dq__imageCaption\",\"children\":[[\"$\",\"p\",\"p-0\",{\"children\":\"Honeycomb output of monitoring webhook requests during the switchover.\"}]]}]]}]\n81:[\"$\",\"p\",\"76\",{\"children\":[[\"$\",\"code\",\"0\",{\"children\":[\"$\",\"strong\",null,{\"children\":\"The tricky parts\"}]}]]}]\n82:[\"$\",\"p\",\"77\",{\"children\":[\"Of course, not everything was smooth sailing. The infrastructure complexity was real! What initially seemed like a straightforward migration quickly revealed a web of interdependencies. Solving one problem often uncovered two more, and our project timeline stretched as new edge cases emerged.\"]}]\n83:[\"$\",\"p\",\"78\",{\"children\":[\"Some of the most challenging moments came from the database side. For example, we ran into a challenging issue days before the migration: a database migration had run on the source stack but not on the target as a result of a blindspot in our deployment disabling funditonality. Since logical replication doesn’t support \",[\"$\",\"$L3c\",\"1\",{\"to\":\"https://www.commandprompt.com/education/postgresql-data-definition-language-ddl/\",\"children\":[\"DDL\"]}],\"(Data Definition Language) changes, the replication subscription worker kept dying and retrying. We had to pause deploys and lock the schema versions on both stacks. Even then, Rails’ \",[\"$\",\"code\",\"3\",{\"children\":\"schema_migrations\"}],\" table was already synced, so Rails thought the migration had already run on the target and wouldn’t retry it. We ended up adding the missing columns manually, which finally allowed replication to resume. By that point, we’d accumulated nearly 5GB of WAL, so we had to wait for the backlog to clear before we could safely switch over. Keeping an eye on the oldest slot lag metric became essential.\"]}]\n84:[\"$\",\"p\",\"79\",{\"children\":[\"We also ran into an unexpected issue with the BLOB data synchronization job. On migration day, the job that syncs attachments between stacks failed due to unexpected JSON parsing issues, something we hadn’t seen in testing. We ended up “\",[\"$\",\"$L3c\",\"1\",{\"to\":\"https://dev.to/ayushn21/applying-monkey-patches-in-rails-1bj1\",\"children\":[\"monkey patching\"]}],\"” the job via the Rails console in real time, and quickly followed up with a permanent fix after the migration due to time constraints.\"]}]\n85:[\"$\",\"p\",\"80\",{\"children\":[\"Request routing brought its own set of hurdles. Lambda@Edge and CloudFront had their quirks, and handling gzipped content was more challenging than we anticipated. Additionally, we did not support WebSocket requests during the migration. However, since nothing critical in our system relied on WebSockets, we were able to tolerate this limitation without impacting core functionality. Finally, the system we built for this migration now requires ongoing monitoring and maintenance. Good documentation became essential, and with our apps and infrastructure always evolving, maintaining this process would not be straight forward.\"]}]\n86:[\"$\",\"$L3b\",\"81\",{\"level\":2,\"id\":\"conclusion\",\"children\":[[\"$\",\"strong\",\"0\",{\"children\":\"Conclusion\"}]]}]\n87:[\"$\",\"p\",\"82\",{\"children\":[\"This migration taught us that in modern cloud systems, there's no such thing as a \\\"simple\\\" database migration. The complexity of maintaining zero downtime while moving critical customer data pushed us to innovate and improve our infrastructure in ways we hadn't anticipated.\"]}]\n88:[\"$\",\"p\",\"83\",{\"children\":[\"The systems and code we built now serve as building blocks for future improvements at Tines. While the journey was far more complex than initially expected, it left us with more robust infrastructure and valuable lessons about managing large-scale cloud systems.\"]}]\n89:[\"$\",\"p\",\"84\",{\"children\":[\"Final thought: database migrations, it turns out, are less about moving live data and more about making sure nobody notices you're moving live data.\"]}]\n8a:[\"$\",\"$L8b\",null,{}]\n"])</script></body></html>