1# Cross-Device Sync of KV Stores 2 3 4## When to Use 5 6KV Stores are suitable for storing service data with simple relationships. It provides higher read and write performance than the SQL database. KV stores are widely used because the simplicity of the KV data model poses fewer database version compatibility issues in distributed scenarios and simplifies conflict handling in data sync. 7 8 9## Basic Concepts 10 11Before implementing cross-device sync of KV stores, understand the following concepts: 12 13 14### Single KV Store 15 16In a single KV store, data is saved in the unit of a single entry. When data is modified locally, the data entry is updated no matter whether it has been synced. Only one copy of data is retained globally for multiple devices. The data of the latest time is kept for the same entry (with the same primary code) of multiple devices. The data in single KV stores is not differentiated by device. If the data modified on multiple devices has the same key, the value will be overwritten. For the data written or modified locally, the data with the latest time is synced to other devices. Single KV stores are used to store information, such as the Contacts and weather application data. 17 18 19 20 21### Device KV Store 22 23In a device KV store, the local device ID is added before the key of the KV pair stored by an application. In this way, the data of different devices is isolated. Data is managed by device and can be queried by device. 24 25The underlying devices manage the data by device. The device KV stores support distributed data query by device, but do not support modification of the data synced from peer devices. Device KV stores are used to store the data that needs to be accessed by device, such as the Gallery thumbnails. 26 27 28 29 30## Sync Types 31 32**DatamgrService** provides the following sync types: 33 34### Manual Sync 35 36The application calls **sync()** with the devices to be synced and the sync mode specified to trigger the sync. The sync mode can be **PULL_ONLY** (pulling remote data to the local end), **PUSH_ONLY** (pushing local data to the remote end), or **PUSH_PULL** (pushing local data to the remote end and pulling remote data to the local end). You can use the [**sync()** with the **query** parameter](../reference/apis-arkdata/js-apis-distributedKVStore.md#sync-1) to synchronize the data that meets the specified conditions. 37 38### Auto Sync 39 40In [multi-device collaboration via cross-device calls](../application-models/hop-multi-device-collaboration.md#using-cross-device-call), after an application updates data, the distributed database automatically pushes the local data to the peer ends and pulls the peer data to the local device for data sync. In this case, the application does not need to call **sync()**. 41 42 43## Working Principles 44 45After completing device discovery and authentication, the underlying communication component notifies the application that the device goes online. The **DatamgrService** then establishes an encrypted transmission channel to synchronize data between the two devices. 46 47 48### Cross-Device Data Sync Mechanism 49 50 51 52When **put()** or **delete()** is called successfully, an auto sync is triggered. The distributed data is sent to the peer device through the communication adaptation layer for sync. 53 54If **sync()** is called successfully, a manual sync is triggered to send distributed data to the peer device through the communication adaptation layer. 55 56 57### Data Change Notification Mechanism 58 59When data is added, deleted, or modified, a notification is sent to the subscriber. The notifications can be classified into the following types: 60 61- Local data change notification: subscription of the application data changes on the local device. When the data in the local KV store is added, deleted, or modified in the database, a notification is received. 62 63- Distributed data change notification: subscription of the application data changes of other devices in the network. When the data in the local KV store changes after being synced with data from another device in the same network, a notification is received. 64 65 66## Constraints 67 68- For each record in a device KV store, the key cannot exceed 896 bytes and the value cannot exceed 4 MB. 69 70- For each record in a single KV store, the key cannot exceed 1 KB and the value cannot exceed 4 MB. 71 72- The KV stores do not support custom conflict resolution policies for applications. 73 74- A maximum of 16 KV stores can be opened simultaneously for an application. 75 76- Each KV store supports a maximum of eight callbacks for subscription of data change notifications. 77 78 79## Available APIs 80 81The following table lists the APIs for cross-device data sync of the single KV store. Most of the APIs are executed asynchronously, using a callback or promise to return the result. The following table uses the callback-based APIs as an example. For more information about the APIs, see [Distributed KV Store](../reference/apis-arkdata/js-apis-distributedKVStore.md). 82 83| API| Description| 84| -------- | -------- | 85| createKVManager(config: KVManagerConfig): KVManager | Creates a **KvManager** instance to manage database objects.| 86| getKVStore<T>(storeId: string, options: Options, callback: AsyncCallback<T>): void | Obtains a KV store of the specified type.| 87| put(key: string, value: Uint8Array\|string\|number\|boolean, callback: AsyncCallback<void>): void | Inserts and updates data.| 88| on(event: 'dataChange', type: SubscribeType, listener: Callback<ChangeNotification>): void | Subscribes to data changes in the KV store.| 89| get(key: string, callback: AsyncCallback<boolean \| string \| number \| Uint8Array>): void | Queries the value of the specified key.| 90| sync(deviceIds: string[], mode: SyncMode, delayMs?: number): void | Triggers a manual sync of the KV store.| 91 92 93## How to Develop 94 95The following uses a single KV store as an example to describe how to implement cross-device data sync. The development process is as follows. 96 97 98 99> **NOTE** 100> 101> The data on a device can be synced only to the devices whose data security labels are not higher than the security level of the device. For details, see [Access Control Mechanism in Cross-Device Sync](sync-app-data-across-devices-overview.md#access-control-mechanism-in-cross-device-sync). 102 1031. Import the module. 104 105 ```ts 106 import { distributedKVStore } from '@kit.ArkData'; 107 ``` 108 1092. Request permissions. 110 111 a) Declare the **ohos.permission.DISTRIBUTED_DATASYNC** permission. For details, see [Declaring Permissions](../security/AccessToken/declare-permissions.md). 112 b) Display a dialog box to ask for user authorization when the application is started for the first time. For details, see [Requesting User Authorization](../security/AccessToken/request-user-authorization.md). 113 1143. Create a **KvManager** instance based on the specified **KvManagerConfig** object. 115 116 a) Create a **kvManagerConfig** object based on the application context. 117 b) Create a **KvManager** instance. 118 119 120 ```ts 121 // Obtain the context of the stage model. 122 import { window } from '@kit.ArkUI'; 123 import { UIAbility } from '@kit.AbilityKit'; 124 import { BusinessError } from '@kit.BasicServicesKit'; 125 126 let kvManager: distributedKVStore.KVManager | undefined = undefined; 127 128 class EntryAbility extends UIAbility { 129 onWindowStageCreate(windowStage:window.WindowStage) { 130 let context = this.context; 131 } 132 } 133 134 // Obtain the context of the FA model. 135 import { featureAbility } from '@kit.AbilityKit'; 136 import { BusinessError } from '@kit.BasicServicesKit'; 137 138 let context = featureAbility.getContext(); 139 140 // Construct a kvManager instance. 141 try { 142 const kvManagerConfig: distributedKVStore.KVManagerConfig = { 143 bundleName: 'com.example.datamanagertest', 144 context: context 145 } 146 kvManager = distributedKVStore.createKVManager(kvManagerConfig); 147 console.info('Succeeded in creating KVManager.'); 148 // Create and obtain the KV store. 149 } catch (e) { 150 let error = e as BusinessError; 151 console.error(`Failed to create KVManager. Code:${error.code},message:${error.message}`); 152 } 153 154 if (kvManager !== undefined) { 155 kvManager = kvManager as distributedKVStore.KVManager; 156 // Perform subsequent operations such as creating a KV store. 157 // ... 158 } 159 ``` 160 1614. Obtain the KV store of the specified type. 162 163 a) Declare the ID of the distributed KV store to create, for example, **'storeId'** in the sample code. 164 b) Disable the auto sync function (**autoSync:false**) to facilitate subsequent verification of the sync function. If sync is required, call the **sync()** interface. 165 166 167 ```ts 168 let kvStore: distributedKVStore.SingleKVStore | undefined = undefined; 169 try { 170 let child1 = new distributedKVStore.FieldNode('id'); 171 child1.type = distributedKVStore.ValueType.INTEGER; 172 child1.nullable = false; 173 child1.default = '1'; 174 let child2 = new distributedKVStore.FieldNode('name'); 175 child2.type = distributedKVStore.ValueType.STRING; 176 child2.nullable = false; 177 child2.default = 'zhangsan'; 178 179 let schema = new distributedKVStore.Schema(); 180 schema.root.appendChild(child1); 181 schema.root.appendChild(child2); 182 schema.indexes = ['$.id', '$.name']; 183 // The value 0 indicates the strict mode, and 1 indicates the compatible mode. 184 schema.mode = 1; 185 // Set the number of bytes to be skipped during the value check. The value range is [0, 4M-2]. 186 schema.skip = 0; 187 188 const options: distributedKVStore.Options = { 189 createIfMissing: true, 190 encrypt: false, 191 backup: false, 192 autoSync: false, 193 // If kvStoreType is left empty, a device KV store is created by default. 194 // Device KV store: kvStoreType: distributedKVStore.KVStoreType.DEVICE_COLLABORATION, 195 kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION, 196 // The schema parameter is optional. You need to set this parameter when the schema function is required, for example, when predicates are used for query. 197 schema: schema, 198 securityLevel: distributedKVStore.SecurityLevel.S3 199 }; 200 kvManager.getKVStore<distributedKVStore.SingleKVStore>('storeId', options, (err, store: distributedKVStore.SingleKVStore) => { 201 if (err) { 202 console.error(`Failed to get KVStore: Code:${err.code},message:${err.message}`); 203 return; 204 } 205 console.info('Succeeded in getting KVStore.'); 206 kvStore = store; 207 // Before performing related data operations, obtain a KV store instance. 208 }); 209 } catch (e) { 210 let error = e as BusinessError; 211 console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`); 212 } 213 if (kvStore !== undefined) { 214 kvStore = kvStore as distributedKVStore.SingleKVStore; 215 // Perform subsequent data operations, such as adding, deleting, modifying, and querying data, and subscribing to data changes. 216 // ... 217 } 218 ``` 219 2205. Subscribe to distributed data changes. To unsubscribe from the data changes, call [off('dataChange')](../reference/apis-arkdata/js-apis-distributedKVStore.md#offdatachange). 221 222 ```ts 223 try { 224 kvStore.on('dataChange', distributedKVStore.SubscribeType.SUBSCRIBE_TYPE_ALL, (data) => { 225 console.info(`dataChange callback call data: ${data}`); 226 }); 227 } catch (e) { 228 let error = e as BusinessError; 229 console.error(`An unexpected error occurred. code:${error.code},message:${error.message}`); 230 } 231 ``` 232 2336. Write data to the single KV store. 234 235 a) Construct the key and value to be written to the single KV store. 236 b) Write KV pairs to the single KV store. 237 238 239 ```ts 240 const KEY_TEST_STRING_ELEMENT = 'key_test_string'; 241 // If schema is not defined, pass in other values that meet the requirements. 242 const VALUE_TEST_STRING_ELEMENT = '{"id":0, "name":"lisi"}'; 243 try { 244 kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => { 245 if (err !== undefined) { 246 console.error(`Failed to put data. Code:${err.code},message:${err.message}`); 247 return; 248 } 249 console.info('Succeeded in putting data.'); 250 }); 251 } catch (e) { 252 let error = e as BusinessError; 253 console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`); 254 } 255 ``` 256 2577. Query data in the single KV store. 258 259 a) Construct the key to be queried from the single KV store. 260 b) Query data from the single KV store. 261 262 263 ```ts 264 try { 265 kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => { 266 if (err !== undefined) { 267 console.error(`Failed to put data. Code:${err.code},message:${err.message}`); 268 return; 269 } 270 console.info('Succeeded in putting data.'); 271 kvStore = kvStore as distributedKVStore.SingleKVStore; 272 kvStore.get(KEY_TEST_STRING_ELEMENT, (err, data) => { 273 if (err != undefined) { 274 console.error(`Failed to get data. Code:${err.code},message:${err.message}`); 275 return; 276 } 277 console.info(`Succeeded in getting data. Data:${data}`); 278 }); 279 }); 280 } catch (e) { 281 let error = e as BusinessError; 282 console.error(`Failed to get data. Code:${error.code},message:${error.message}`); 283 } 284 ``` 285 2868. Synchronize data to other devices. 287 288 Select the devices to be synced with data and the sync mode. The user needs to confirm the sync mode when the application is started for the first time. 289 290 > **NOTE** 291 > 292 > In manual sync mode, **deviceIds** can be obtained by [devManager.getAvailableDeviceListSync](../reference/apis-distributedservice-kit/js-apis-distributedDeviceManager.md#getavailabledevicelistsync). 293 294 ```ts 295 import { distributedDeviceManager } from '@kit.DistributedServiceKit'; 296 297 let devManager: distributedDeviceManager.DeviceManager; 298 try { 299 // create deviceManager 300 devManager = distributedDeviceManager.createDeviceManager(context.applicationInfo.name); 301 // deviceIds is obtained by devManager.getAvailableDeviceListSync. 302 let deviceIds: string[] = []; 303 if (devManager != null) { 304 let devices = devManager.getAvailableDeviceListSync(); 305 for (let i = 0; i < devices.length; i++) { 306 deviceIds[i] = devices[i].networkId as string; 307 } 308 } 309 try { 310 // 1000 indicates the maximum delay, in ms. 311 kvStore.sync(deviceIds, distributedKVStore.SyncMode.PUSH_ONLY, 1000); 312 } catch (e) { 313 let error = e as BusinessError; 314 console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`); 315 } 316 317 } catch (err) { 318 let error = err as BusinessError; 319 console.error("createDeviceManager errCode:" + error.code + ",errMessage:" + error.message); 320 } 321 ``` 322