简体   繁体   English

如果是脱机优先应用程序,如何与远程数据库同步数据?

[英]How do I sync data with remote database in case of offline-first applications?

  • I am building a "TODO" application which uses Service Workers to cache the request's responses and in case a user is offline, the cached data is displayed to the user. 我正在构建一个“TODO”应用程序,它使用Service Workers来缓存请求的响应,如果用户处于脱机状态,则会向用户显示缓存的数据。
  • The Server exposes an REST-ful endpoint which has POST, PUT, DELETE and GET endpoints exposed for the resources. 服务器公开一个REST-ful端点,该端点具有为资源公开的POST,PUT,DELETE和GET端点。
  • Considering that when the user is offline and submitting a TODO item, I save that to local IndexedDB, but I can't send this POST request for the server since there is no network connection. 考虑到当用户离线并提交TODO项目时,我将其保存到本地IndexedDB,但由于没有网络连接,我无法发送此服务器的POST请求。 The same is true for the PUT, DELETE requests where a user updates or deletes an existing TODO item 对于用户更新或删除现有TODO项的PUT,DELETE请求也是如此

Questions 问题

  • What patterns are in use to sync the pending requests with the REST-ful Server when the connection is back online? 当连接重新联机时,正在使用哪些模式将待处理请求与REST-ful服务器同步?

At the moment I can think of two approaches and it depend on what storage options you are using at your backend. 目前我可以想到两种方法,它取决于您在后端使用的存储选项。

If you are using an RDBMS to backup all data: 如果您使用RDBMS备份所有数据:

The problem with offline first systems in this approach is the possibility of conflict that you may face when posting new data or updating existing data. 此方法中脱机第一系统的问题是您在发布新数据或更新现有数据时可能会遇到冲突。

As a first measure to avoid conflicts from happening you will have to generate unique IDs for all objects from your clients and in such a way that they remain unique when posted on the server and saved in a data base. 作为避免冲突发生的第一个措施,您必须为客户端中的所有对象生成唯一ID,并且这样在发布到服务器上并保存在数据库中时它们仍然是唯一的。 For this you can safely rely on UUIDs for generating unique IDs for objects. 为此,您可以安全地依赖UUID为对象生成唯一ID。 UUID guarantees uniqueness across systems in a distributed system and depending on what your language of implementation is you will have methods to generate UUIDs without any hassle. UUID保证了分布式系统中各系统的唯一性,并且根据您的实现语言,您将拥有生成UUID的方法,而不会有任何麻烦。

Design your local database such that you can use UUIDs as primary key in your local database. 设计本地数据库,以便可以将UUID用作本地数据库中的主键。 On the server end you can have both, an integer type auto incremented and indexed, primary key and a VARCHAR type to hold the UUIDs. 在服务器端,您可以同时拥有一个整数类型自动递增和索引,主键和VARCHAR类型来保存UUID。 The primary key on server uniquely identifies objects in that table while UUID uniquely identifies records across tables and databases. 服务器上的主键唯一标识该表中的对象,而UUID唯一标识表和数据库中的记录。

So when posting your object to server at the time of syncing you will have to just check if any object with the UDID is already present and take appropriate action from there. 因此,在同步时将对象发布到服务器时,您只需检查是否已存在具有UDID的任何对象,并从那里采取适当的操作。 When your are fetching objects from the server send both the primary key of the object from your table and the UDID for the objects. 从服务器获取对象时,从表中发送对象的主键和对象的UDID。 This why when you serialise the response in model objects or save them in local database you can tell the objects which have been synced from the ones which haven't as the objects that needs syncing will not have a primary key in your local database, just the UUID. 这就是为什么当您在模型对象中序列化响应或将它们保存在本地数据库中时,您可以告诉已经同步的对象,而不是需要同步的对象将在本地数据库中没有主键,只是UUID。

There may be a case when your server malfunctions and refuses to save data when you are syncing. 可能存在服务器出现故障并在同步时拒绝保存数据的情况。 In this case you can keep an integer variable in your objects that will keep a count of the number of times you have tried syncing it. 在这种情况下,您可以在对象中保留一个整数变量,该变量将保留您尝试同步它的次数。 If this number exceed by a certain value, say 3, you move on to sync the next object. 如果此数字超过某个值(例如3),则继续同步下一个对象。 Now what you do with the unsynced objects is up you the policy you have for such objects, as a solution you could discard them or keep them just locally. 现在你对未同步的对象所做的就是你拥有这些对象的策略,作为一种解决方案,你可以丢弃它们或将它们保存在本地。

If you are not using RDBMS 如果您不使用RDBMS

As an alternate approach, instead of keeping all objects you could keep transactions that each client perform locally to the server. 作为替代方法,您可以将每个客户端本地执行的事务保存到服务器,而不是保留所有对象。 Each client syncs just the transactions and the while fetching you get the current state by working all the transactions from bottom up. 每个客户端只通过事务同步,并通过自下而上处理所有事务来获取当前状态。 This is very similar to what Git uses. 这与Git使用的非常相似。 It saves changes in your repository in form of transactions like what has been added (or removed) and by whom. 它以事务的形式保存存储库中的更改,例如已添加(或删除)的内容以及由谁进行的更改。 The current state of the repository for each user is worked from the transactions. 每个用户的存储库的当前状态是从事务处理的。 This approach will not result in conflicts but as you can see its a little tricky to develop. 这种方法不会导致冲突,但你可以看到它的开发有点棘手。

What patterns are in use to sync the pending requests with the REST-ful Server when the connection is back online? 当连接重新联机时,正在使用哪些模式将待处理请求与REST-ful服务器同步?

Background Sync API will be suitable for this scenario. 后台同步API适用于此场景。 It enables web applications to synchronize data in the background. 它使Web应用程序能够在后台同步数据。 With this, it can defer actions until the user has a reliable connection, ensuring that whatever the user wants to send is actually sent. 通过这种方式,它可以推迟操作直到用户具有可靠的连接,从而确保实际发送用户想要发送的任何内容。 Even if the user navigates away or closes the browser, the action is performed and you could notify the user if desired. 即使用户导航或关闭浏览器,也会执行操作,如果需要,您可以通知用户。

Since you're saving to IndexDB, you could register for a sync event when the user add, delete or update a TODO item 由于您要保存到IndexDB,因此您可以在用户添加,删除或更新TODO项目时注册同步事件

function addTodo(todo) {
  return addToIndeDB(todo).then(() => {
    // Wait for the scoped service worker registration to get a
    // service worker with an active state
    return navigator.serviceWorker.ready;
  }).then(reg => {
    return reg.sync.register('add-todo');
  }).then(() => {
    console.log('Sync registered!');
  }).catch(() => {
    console.log('Sync registration failed :(');
  });
}

You've registered a sync event of type add-todo which you'll listen for in the service-worker and then when you get this event, you retrieve the data from the IndexDB and do a POST to your Restful API. 您已经注册了一个add-todo类型的同步事件,您将在service-worker中监听该事件,然后当您收到此事件时,您将从IndexDB检索数据并对Restful API执行POST。

self.addEventListener('sync', event => {
if (event.tag == 'add-todo') {
      event.waitUntil(
      getTodo().then(todos => {
        // Post the messages to the server
        return fetch('/add', {
          method: 'POST',
          body: JSON.stringify(todos),
          headers: { 'Content-Type': 'application/json' }
        }).then(() => {
          // Success!
        });
      })
      })
    );
   }
});

This is just an example of how you could achieve it using Background Sync. 这只是一个如何使用Background Sync实现它的示例。 Note that you'll have to handle conflict resolution on the server. 请注意,您必须在服务器上处理冲突解决方案。

You could use PouchDB on the client and Couchbase or CouchDB on the server. 您可以在客户端使用PouchDB,在服务器上使用Couchbase或CouchDB。 With PouchDB on the client, you can save data on the client and set it to automatically sync/replicate the data whenever the user is online. 使用客户端上的PouchDB,您可以将数据保存在客户端上,并将其设置为在用户联机时自动同步/复制数据。 When the database synchronizes and there are conflicting changes, CouchDB will detect this and will flag the affected document with the special attribute "_conflicts":true . 当数据库同步并且存在冲突的更改时,CouchDB将检测到此并将使用特殊属性"_conflicts":true标记受影响的文档"_conflicts":true It determines which one it'll use as the latest revision, and save the others as the previous revision of that record. 它确定将哪一个用作最新版本,并将其他版本保存为该记录的先前版本。 It does not attempt to merge the conflicting revision. 它不会尝试合并冲突的修订版。 It is up to you to dictate how the merging should be done in your application. 由您决定如何在您的应用程序中完成合并。 It's not so different from Couchbase too. 它与Couchbase没有什么不同。 See the links below for more on Conflict Resolution. 有关冲突解决的更多信息,请参阅以下链接。

I've used pouchDB and couchbase/couchdb/IBM cloudant but I've done that through Hoodie It has user authentication out-of-the box, handles conflict management, and a few more. 我已经使用了pouchDB和couchbase / couchdb / IBM cloudant,但我通过Hoodie完成了它。它具有开箱即用的用户身份验证,处理冲突管理等等。 Think of it like your backend. 把它想象成你的后端。 In your TODO application, Hoodie will be a great fit. 在你的TODO应用程序中,连帽衫将非常适合。 I've written something on how to use Hoodie, see links Below: 我写过关于如何使用连帽衫的文章,请看下面的链接:

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM