請看下面的 GIF — 它顯示了一個即時Todo-MVC 演示,跨視窗同步並平滑地進出離線模式。雖然它只是一個簡單的演示應用程式,但它展示了每個 Web 開發人員都應該了解的重要的前沿概念。這是一個Replicache演示應用程式,我將其從 Express 後端和 Web 元件前端移植到 SvelteKit,以了解背後的技術和概念。我想與您分享我的學習成果。原始碼可在 Github 上取得。

sveltekit-replicache-演示

背景和動機

Web 應用程式面臨一些根本性的難題,而大多數 Web 框架似乎都忽略了這些問題。這些問題非常困難,以至於只有很少的應用程式能夠真正很好地解決它們,並且這些應用程式在各自的領域中遙遙領先於其他應用程式。

以下是我在實際開發的商業應用程式中必須處理的一些此類問題:

  1. 讓應用程式感覺敏捷,即使它與伺服器通信,即使在緩慢或不穩定的網路上。這不僅適用於初始載入時間,也適用於應用程式載入後的互動。 SPA是解決這個問題的早期嘗試,但最終還不夠。

  2. 為使用者產生的內容(例如網站建立、電子商務、線上課程建構器)實施撤銷/重做和版本歷史記錄。

  3. 當同一用戶在多個分頁/裝置上同時開啟應用程式時,請讓應用程式正常運作。

  4. 處理執行舊版本前端的長期會話,使用者可能不想刷新以避免丟失工作。

  5. 使協作功能/多人遊戲功能正確且近乎即時地工作,包括解決衝突。

我在開發完全正常的 Web 應用程式時遇到了這些問題,沒有什麼太瘋狂的,而且我相信大多數 Web 應用程式在獲得吸引力時都會遇到部分或全部問題。

我在開始開發新產品的開發團隊中註意到的一個模式是完全忽略這些問題,即使團隊已經意識到這些問題。推理通常是這樣的:“當我們真正開始遇到這些問題時,我們會處理它。”然後,團隊將繼續選擇一些完善的框架(選擇您最喜歡的),認為這些工具肯定能為可能出現的任何常見問題提供解決方案。幾個月後,當應用程式達到一萬名活躍用戶時,現實就浮出水面:團隊必須引入部分的、不完整的解決方案,這些解決方案會增加複雜性,使系統更加緩慢和錯誤,或者重寫核心部分(之後沒有人立即這樣做)發射)。哎喲。

我感受到了這種痛苦。痛苦是真實的。

輸入“同步引擎”。

同步引擎到底是什麼?

還記得我說過有些應用程式比其他應用程式更好地解決這些問題嗎?最近著名的例子是LinearFigma 。兩者都透過技術優勢擾亂了競爭異常激烈的市場。其他例子有Super human和十年前的Trello 。當您研究他們所做的事情時,您會發現它們都集中在非常相似的模式上,並且它們都在內部開發了各自的實現。您可以在以下連結中了解他們是如何做到的(強烈推薦): FigmaLinearSuper humanTrello(系列)

在系統的核心,始終有一個同步引擎,可作為前端和後端之間的持久緩衝區。從高層次來看,它是這樣運作的:

  • 客戶端始終讀取和寫入引擎提供的本地儲存。就應用程式程式碼而言,它在記憶體中本地執行。

  • 該儲存負責樂觀地更新狀態,將資料本地保存在瀏覽器的儲存中,並與後端來回同步,包括處理潛在的複雜情況和邊緣情況。

  • 後端實作引擎的另一半,以允許拉取和推播資料、在資料變更時通知客戶端、將資料保存在資料庫中等。

同步引擎的不同實作會做出不同的權衡,但基本概念始終是相同的。

這不是一個新想法,但...

If you've been following trends in the web-dev world, you'd know that sync engines have been a centrepiece in several of them, namely: progressive web apps , offline-first apps , and the lately trending term: local-first軟體.您甚至可能研究過一些提供內建同步引擎的資料庫,例如PouchDb或具有相同功能的線上服務(例如Firestore )。我也有,但過去幾年我的整體感覺是,這些都不是切中要害的。漸進式網頁應用程式是關於用戶在主螢幕上「安裝」網站的快捷方式,就好像它們是本機應用程式一樣,儘管不需要安裝可能是網路的「好處」。 「離線優先」聽起來離線模式比線上模式更重要,但對於 99% 的網路應用程式來說,情況並非如此。 「本地優先」無疑是迄今為止最好的名字,但官方的本地優先宣言談論了點對點通信和CRDT (一個超級酷的想法,但除了協作文本編輯之外很少用於任何其他用途)。客戶端-伺服器Web 應用程式的世界正在嘗試解決像我上面描述的那樣的實際問題。諷刺的是,許多屬於當前「本地優先」浪潮一部分的工具都採用了這個名稱,但沒有採用所有原則。

最引起我注意和興趣的是「Replicache」。具體來說,我對它很感興趣,因為它不是一個自我複製的資料庫,也不是一個你必須圍繞它來建立整個應用程式的黑盒 SaaS 服務。相反,與我在這個領域遇到的任何現成解決方案相比,它提供了更多的控制、靈活性和關注點分離。

什麼是複製快取?

Replicache 是一個函式庫。在前端,它只需要很少的佈線,並且可以有效地充當普通的全局商店(想想 Zustand 或 Svelte 商店)。它有一個狀態區塊(在我們的範例中,每個清單都有自己的儲存)。它可以使用一組稱為“mutators”(認為是reducers)的用戶定義函數進行變異,例如“addItem”、“deleteItem”或任何您想要的東西,並公開一個訂閱函數(我在這裡簡化了完整的API)。

在這個熟悉的介面背後是一個強大且高效能的客戶端同步引擎,它可以處理:

  1. 初步將相關資料完整下載到客戶端。

  2. 從後端拉動和推送“突變”。突變是一個事件,指定應用哪個突變器以及哪些參數(加上一些元資料)。

- When pushing, these changes are applied optimistically on the client, and rolled back if they fail on the server. Any other pending changes would be applied on top (rebase).

- The sync mechanism also includes queuing changes if the connection is lost, retry mechanisms, applying changes in the right order, and de-duping.

  1. 將所有內容快取在記憶體中(效能)並將其保存到瀏覽器儲存(特別是 IndexedDB)以進行備份。

  2. 由於可以從同一應用程式的所有選項卡存取相同的存儲,因此引擎會處理其中的所有含義,例如當架構發生更改但某些選項卡已刷新而某些選項卡尚未刷新且仍在使用時該怎麼辦舊模式。

  3. 使用廣播通道立即保持所有選項卡同步(因為依賴共用儲存不夠快)。

  4. 處理瀏覽器決定清除本地儲存的情況。

您可能已經注意到,這裡解決了我在本文頂部列出的大部分問題。基於突變也適合撤銷/重做等功能。

為了讓所有這些都能發揮作用,後端的工作就是實作 Replicache 定義的協定。具體來說:

  1. 您需要實作推送拉取API。這些端點需要能夠像前端一樣啟動變異器(儘管它們不必執行相同的邏輯)。後端是權威的,衝突解決是由您的 mutator 實作中的程式碼完成的。

  2. 您的資料庫需要支援快照隔離並在事務內執行操作。

  3. Replicache 用戶端定期輪詢伺服器以檢查更改,但如果您希望用戶端之間接近即時同步,則需要實作「poke」機制,即通知客戶端某些內容已更改並且需要進行更改的方法。拉。這可以透過伺服器發送的事件websockets來完成。這是一個有趣的 API 設計選擇——更改永遠不會推送到客戶端;客戶總是拉他們。我相信這樣做是為了簡單且易於對系統進行推理。有一點可以肯定:他們沒有強制使用Websocket,這是件好事,因為這會使協議與HTTP(伺服器透過正常HTTP 連接發送的事件流)不相容,這將需要額外的基礎設施並帶來額外的集成挑戰。

  4. 根據版本控制策略,您可能需要實作其他操作(例如,createSpace)。

如果這對您來說並不平凡,那麼您是對的。我認為我還沒有完全理解它如何與資料庫一起操作的所有細節。我需要做一個後續專案,在其中完全重構資料庫結構和/或向範例加入有意義的功能(例如版本歷史記錄),以便更接近完全理解它。問題是,我知道在建立和維護實際生產應用程式時這種控制層級有多麼有價值。在我的書中,花一兩週的時間深入思考和設定應用程式的核心部分,如果它為建立和擴展奠定了堅實的基礎,那麼它就是一筆巨大的投資。

移植一個重要的範例

學習新事物的最好(也可以說是唯一)方法就是親自動手,親自體驗一些會影響真正應用程式的權衡和影響。當我查看Replicache 網站上的範例時,我注意到沒有 Sveltekit 的範例。自從 Svelte 3 發布以來,我一直是 Svelte 的忠實粉絲,但最近才開始使用 Sveltekit。我認為這將是一個透過實踐學習並同時建立有用的參考來實現的絕佳機會。

將現有程式碼庫移植到不同的技術具有教育意義,因為在翻譯程式碼時,您被迫理解並質疑它。在整個過程中,我經歷了多次靈光一現的時刻,因為一些起初看起來很奇怪的事情都發生了。

學習內容

斯維爾特基特

  1. Sveltekit本身並不支援 WebSockets ,即使它確實支援伺服器發送事件,但它的方式也很笨拙。 Express 很好地支援兩者。因此,我使用svelte-sse來處理伺服器發送的事件。我遇到的一個有點煩人的怪癖是,由於 svelte-sse 返回一個 Svelte 商店,而我的應用程式沒有訂閱該商店(應用程式不需要讀取該值,只需觸發如上所述的拉取),整件事情只是被編譯器優化掉了。起初我很困惑為什麼訊息沒有通過。我最終不得不針對這種行為實施解決方法。我不怪圖書館的作者;我只是怪罪圖書館的作者。他們假設一個有意義的值將發送給客戶端,但「poke」的情況並非如此。

  2. 與原始 Express 後端相比,SvelteKit 基於檔案系統的路由、載入函數、佈局和其他功能可以實現更好組織的程式碼庫和更少的樣板程式碼。不用說,在前端,Svelte 遠遠領先於 Web 元件,導致前端程式碼庫更小、更易讀,儘管它具有更多功能(原始示例 TodoMVC 缺少諸如“將所有內容標記為完成”等功能) “刪除完成”)。

  3. 總的來說,我喜歡 Sveltekit 並計劃在未來繼續使用它。如果您還沒有嘗試過,官方教學是一個很棒的介紹。

複製快取

總的來說,Replicache 給我留下了非常深刻的印象,並建議嘗試一下。在基本層面上(這是我目前要做的所有嘗試),它運作良好並兌現了所有承諾。話雖如此,以下是我的一些普遍擔憂(與待辦事項應用程式無關)以及與之相關的想法:

  • 性能相關:
- **Initial load time** (first time, before any data was ever pulled to the client) might be long when there is a lot of data to download (think tens of MBs). Productivity apps in which the user spends a lot of time after the initial load are less sensitive to this, but it is still something to watch for. Potential mitigation: partial sync (e.g., Linear only sends open issues or ones that were closed over the last week instead of sending all issues).

- **Chatty network (?)** - Initially, it seemed to me that there was a lot of chatter going back and forth between the client and the server with all the push, pull, and poke calls flying around. On deeper inspection, I realized my intuition was wrong. There is frequent communication, yes, but since the mutations are very compact and the poke calls are tiny (no payload), it amounts to much less than your normal REST/GraphQL app. Also, a browser full reload (refresh button or opening the page again in a new tab/window after it was closed) loads most of the data from the browser's storage and only needs to pull the diffs from the server, which leads me to the next point.

- **Coming back after a long period of time offline**: I haven't tested this one, but it seems like a real concern. What happens if I was working offline for a few days making updates while my team was online and also making changes? When I come back online, I could have a huge amount of diffs to push and pull. Additionally, conflict resolution could become super difficult to get right. This is a problem for every collaborative app that has an offline mode and is not unique to Replicache. The Replicache docs [warn about this situation](https://doc.replicache.dev/concepts/offline) and propose implementing "the concept of history" as a potential mitigation.

- What about **bundle size**? Replicache is [34kb gzipped](https://bundlephobia.com/package/[email protected]), and for what you get in return, it's easily worth it.

- [This page](https://doc.replicache.dev/concepts/performance) on the Replicache website makes me think that, in the general case, performance should be very good.

  • 功能相關:
- Unlike native mobile or desktop apps, it is possible for users to **lose the local copy of their work** because the browser's storage doesn't provide the same guarantees as the device's file system. Browsers can just decide to delete all the app's data under certain conditions. If the user has been online and has work that didn't have a chance to get pushed to the server, that work would be lost in such a case. Again, this problem is not unique to Replicache and affects all web apps that support offline mode, and based on what I read, it is unlikely to affect most users. It's just something to keep in mind.

- I was surprised to see that the **schema in the backend database** in the Todo example I ported doesn't have the "proper" relational definitions I would expect from a SQL database. There is no "items" table with fields for "id", "text", or "completed". The reason I would want that to exist is the same reason I want a relational database in the first place—to be able to easily slice and dice the data in my system (which I always missed down the line when I didn't have). I don't think it is a major concern since Replicache is supposed to be backend-agnostic as long as the protocol is implemented according to spec. I might try to refactor the database as a follow-up exercise to see what that means in terms of complexity and ergonomics.

- I find **version history and undo/redo** super useful and desirable in apps with user-editable content. With regards to undo/redo there is an [official package](https://github.com/rocicorp/undo) but it seems to [lack support for the multiplayer usecase](https://github.com/rocicorp/replicache/issues/1008) (which is where the problems come from). As for version-history, the Replicache documentation mentions "the concept of history" but [suggests talking to them](https://doc.replicache.dev/concepts/offline) if the need arises. That makes me think it might not be straightforward to achieve. Another idea for a follow-up task.

- **Collaborative text editing** - the existing conflict resolution approach won't work well for collaborative text editing, which requires [CRDTs](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type) or [OT](https://en.wikipedia.org/wiki/Operational_transformation). I wonder how easy it would be to integrate Replicache with something like [Yjs](https://yjs.dev/). There is an [official example repo](https://github.com/rocicorp/replicache-yjs), but I haven't looked into it yet.

  • 縮放相關:
- Since the server is stateful (holds open HTTP connections for server-sent events), I wonder how well it would scale. I've worked on production systems with >100k users that used WebSockets before, so I know it is not that big of a deal, but still something to think about.

  • 其他:
- - In theory, Replicache can be **added into existing apps** without rewriting the frontend (as long as the app already uses a similar store). The backend might be trickier. If your database doesn't support snapshot isolation, you are out of luck, and even if it does, the existing schema and your existing endpoints might need some serious rework. If you're going to use it, do it from day one (if you can).

- Replicache is **not open source** (yet! see the point below) and is [free only as long as you're small or non-commercial](https://replicache.dev/#pricing). Given the amount of work (>2 years) that went into developing it and the quality of engineering on display, it seems fair. With that said, it makes adopting Replicache more of a commitment compared to picking up a free, open library. If you are a tier 2 and up paying customer, you get a [source license](https://doc.replicache.dev/howto/source-access) so that if Replicache shuts down for some reason, your app is safe. Another option is to roll out your own sync engine, like the big boys (Linear, Figma) have done, but getting to the quality and performance that Replicache offers would be anything but easy or quick.

- **Crazy plot twist** (last minute edit): As I was about to publish this post I discovered that Replicache is going to be opened sourced in the near future and that its parent company is planning to launch a new sync-engine called "Zero". [Here is the official announcement](https://zerosync.dev/). It reads: "We will be open sourcing [Replicache](https://replicache.dev/) and [Reflect](https://reflect.net/). Once Zero is ready, we will encourage users to move."

  Ironically, Zero seems to be yet another solution that automagically syncs the backend database with the frontend database, which at least for me personally seems less attractive (because I want separation of concerns and control). With that said, these guys are experts in this domain and I am just a dude on the internet so we'll have to wait and see. In the meanwhile, I plan on playing with Replicache some more.

同步引擎應該用於所有事情嗎?

不,同步引擎不應該用於所有事情。好訊息是,您可以讓應用程式的某些部分使用它,而其他部分仍然以傳統方式提交表單並等待伺服器的回應。 SvelteKit 和其他全端框架使這種整合變得容易。

使用同步引擎的明顯情況是一個壞主意:

  1. 只有當客戶端更改很可能成功(回滾很少)並且客戶端擁有足夠的資訊來預測結果時,樂觀更新才有意義。例如,在線上測驗中,學生的答案必須傳送到伺服器進行評分,樂觀更新(因此同步引擎)是不可行的。這同樣適用於下訂單或交易股票等關鍵操作。一個好的經驗法則是,任何依賴伺服器且無法離線運作的操作都不應該依賴同步引擎。

  2. 任何處理無法安裝在使用者電腦上的龐大資料集的應用程式。例如,建立本地優先版本的 Google 或處理千兆位元組資料以產生結果的分析工具是不切實際的。然而,在部分同步就足夠的情況下,同步引擎仍然是有益的。例如,Google地圖可以在客戶端裝置上下載和快取地圖以進行離線操作,而無需始終提供全球每個位置的高解析度地圖。

關於開發人員生產力和 DX 的一句話

我的印像是,擁有同步引擎可以讓 DX(開發人員體驗)變得更好。前端工程師只需與普通商店合作即可訂閱更新,並且 UI 始終保持最新狀態。無需考慮為同步引擎控制的應用程式部分取得任何內容、呼叫 API 或伺服器操作。至於後端,我還不能說太多。看起來它不會比傳統後端難,但我不能肯定。

結束語

令人興奮的是,將網路應用程式的未來想像為全球範圍內的即時多人協作工具,無論網路條件如何,都可以可靠地工作,同時解決這些令人討厭的問題,我以過去的事情開始這篇文章。

我強烈建議網頁開發人員熟悉這些新概念,嘗試它們,甚至做出貢獻。

謝謝閱讀。如果您有任何問題或想法,請發表評論。和平。

聚苯乙烯

建立 Replicache 公司的創辦人 Aaron Boodman 的訪談非常棒。觀看並稍後感謝我。


原文出處:https://dev.to/isaachagoel/are-sync-engines-the-future-of-web-applications-1bbi


共有 0 則留言