For AI agents: see the complete documentation index at /llms.txt.
Store channel metadata
Updated
Use metadata to enhance channel features in Signaling clients.
The Signaling storage service enables you to store and share contextual channel data in your app, such as props, announcements, member lists, and relationship chains. When channel metadata is set, updated, or deleted, the SDK triggers a storage event notification. Other users in the channel receive this notification within 100ms and use the information according to your business logic.
Understand the tech
Use channel metadata to store and share channel level information such as room attributes, group announcements, and auction item price updates. A set of channel metadata for a specific channel facilitates business-level data storage and real-time notifications. Each channel has only one set of channel metadata, but each set may contain multiple metadata items. For relevant restrictions, refer to the API usage restrictions. Each metadata item has key, value, and revision properties.
Channel metadata is stored permanently in the Signaling database. The data persists even after a channel is destroyed. You must explicitly delete it to remove it from the database. This feature impacts your storage billing. Refer to Pricing for details.
The storage service is available for both message channels and stream channels. Use the channelType parameter in the storage event to determine the channel type.
Prerequisites
Ensure that you have:
- Integrated the Signaling SDK in your project.
- Implemented the framework functionality from the SDK quickstart page.
- Enabled storage in Storage configuration.
Implement channel metadata storage
The section shows you how to implement channel metadata storage in your Signaling app.
Set channel metadata
To create a new metadata item for the channel, or to update the value of an existing item, call setChannelMetadata. This method creates a new item in the channel metadata if the specified key does not exist, or overwrites the associated value if a metadata item with the specified key already exists.
The following example saves a set of metadata items for a message channel named channel1. Signaling adds timestamp and authorUid information to each metadata item it stores.
const properties = {
key : "Quantity",
value : "20"
};
const announcement = {
key : "Announcement",
value : "Welcome to our shop!"
};
const price = {
key : "T-shirt",
value : "100"
};
const data = [properties, announcement, price];
const options = { addTimeStamp : true, addUserId : true };
try {
const result = await rtm.storage.setChannelMetadata("channel1", "MESSAGE", data, options);
console.log(JSON.stringify(result));
} catch (status) {
console.log(JSON.stringify(status));
}The sample code relies on the await/async programming model. Enclose it within an asynchronous function for proper execution. When the call is successful, the SDK returns the following data structure:
{
"timestamp": "", // Timestamp of successful operation
"channelName": "channel1", // Channel name
"channelType": "MESSAGE", // Channel type
"totalCount": 3 // Number of Metadata Items
}Additionally, Signaling triggers a storage event notification of event type UPDATE within 100 ms to inform other channel members.
Get channel metadata
To retrieve all metadata items associated with a specific channel, call getChannelMetadata by specifying the channel name and the channel type. Refer to the following example:
try {
const result = await rtm.storage.getChannelMetadata("channel1","MESSAGE");
console.log(JSON.stringify(result));
} catch (status) {
console.log(JSON.stringify(status));
}Signaling SDK returns the following data structure:
{
totalCount: 3,
majorRevision: 734874892,
metadata:{
"Quantity":{
value:"20",
revision:734874888,
updated:1688978391900,
authorUid:"Tony"
},
"Announcement":{
value:"Welcome to our Shop!",
revision:734874333,
updated:1688978391800,
authorUid:"Tomas"
},
"T-shirt":{
value:"100",
revision:734874222,
updated:168897839100,
authorUid:"Adam"
}
},
channelName: "channel1",
channelType: "MESSAGE"
}Update channel metadata
To modify existing metadata items for a specified channel, call updateChannelMetadata. If the metadata item does not exist, an error is returned. This method is useful for business use-cases that require permission control on creating new metadata items. For example, consider the following use-cases:
- In an e-commerce auction, only administrators or product owners are authorized to list new products and set new attributes, while bidders may only modify price attributes.
- In a gaming environment, only the administrator may define permissions as room properties.
The following example updates the value of a metadata item:
const price = {
key : "T-shirt",
value : "299"
};
const data = [price];
const options = { addTimeStamp : true, addUserId : true };
try {
const result = await rtm.storage.updateChannelMetadata("channel1", "MESSAGE", data, options);
console.log(JSON.stringify(result));
} catch (status) {
console.log(JSON.stringify(status));
}When the call is successful, the SDK returns the following data structure:
{
"timestamp": "", // Timestamp of successful operation
"channelName": "channel1", // Channel name
"channelType": "MESSAGE", // Channel type
"totalCount": 3 // Number of Metadata Items
}Additionally, Signaling triggers a storage event notification of event type UPDATE within 100 ms to inform other channel members.
Delete channel metadata
To delete metadata items that are no longer required, call removeChannelMetadata. Refer to the following sample code:
const announcement = {
key : "Announcement",
};
const data = [announcement];
const options = {
data : data,
};
try {
const result = await rtm.storage.removeChannelMetadata("channel1", "MESSAGE", options);
console.log(JSON.stringify(result));
} catch (status) {
console.log(JSON.stringify(status));
}Setting the value for a metadata item that is being deleted has no effect.
Additionally, Signaling triggers a storage event notification of event type UPDATE within 100 ms to inform other channel members.
To delete the entire set of metadata for a channel, do not specify any metadata items when calling removeChannelMetadata. Refer to the following sample code:
const options = {};
try {
const result = await rtm.storage.removeChannelMetadata("channel1", "MESSAGE", options);
console.log(JSON.stringify(result));
} catch (status) {
console.log(JSON.stringify(status));
}Once channel metadata is deleted, it cannot be recovered. If you need data restoration, back up the metadata before deleting it.
Receive storage event notifications
To receive storage event notifications, implement an event listener. See event listeners for details. In addition, set the withMetadata parameter to true when subscribing to or joining a channel.
Event notification mode
Currently, Signaling only supports the full data update mode. This means that when user or channel metadata is updated, the data field in the event notification contains all the attribute data of the user or the channel.
Additional storage features
To help resolve issues arising from concurrent updates to storage, Signaling offers version control and locking features.
Version control
The Compare and Set (CAS) version control feature in channel metadata storage provides two independent version control parameters. Set one or more of these values according to the needs of your business use-case:
-
majorRevisionproperty inMetadataOptionsmethod: Enable version number verification of the entire set of channel metadata. -
revisionproperty of aMetadataItem[]: Enable version number verification of a single metadata item.
When setting channel metadata, or a single channel metadata item, use the revision attribute to enable or disable version control as follows:
-
To disable CAS verification, use the default value of
-1for therevisionparameter. If the channel metadata or channel metadata item already exists, the value is overwritten. If it does not exist, the SDK creates a metadata item and updates the value. -
To enable CAS verification, set the
majorRevisionor therevisionparameter to a positive integer. If the channel metadata or channel metadata item already exists, the SDK updates the corresponding value after successfully verifying the revision number. If the specified revision number does not match the latest revision number in the database, the SDK returns an error code.
The following sample shows how to use majorRevision and revision to update channel metadata and a metadata item:
const properties = {
key : "Quantity",
value : "30",
revision : 734874888
};
const announcement = {
key : "Announcement",
value : "Welcome to our shop!"
};
const price = {
key : "T-shirt",
value : "101",
revision : 734874222
};
const data = [properties, announcement, price];
const options = { addTimeStamp : true, addUserId : true, majorRevision : 734874892 };
try {
const result = await rtm.storage.updateChannelMetadata("channel1", "MESSAGE", data, options);
console.log(JSON.stringify(result));
} catch (status) {
console.log(JSON.stringify(status));
}In the above example, CAS verification for channel metadata and metadata items is enabled by setting majorRevision and revision parameters to positive integers. Upon receiving the update call request, Signaling first verifies the provided major revision number against the latest value in the database. If there's a mismatch, it returns an error; if the values match, Signaling verifies the revision number for each metadata item using a similar logic.
When using version control, monitor storage event notifications to retrieve updated values for majorRevision and revision to ensure that the latest revision values are used for subsequent operations.
Following are some sample use-cases where version control is useful:
-
In the bidding use-case, if multiple users bid on a product at the same time, the first bidder succeeds, while others receive an error. Users obtain the latest price information to update their bids.
-
In the red envelope grabbing use-case, the red envelope may only be grabbed once. The first user succeeds, while the rest receive an error.
Locks
Locks enable users to gain exclusive access to critical resources, resolving contention issues with shared resources. For instance, consider a use-case where only one administrator is allowed in a channel at a time, and only the administrator can manage channel metadata by setting, deleting, and modifying it.
Compared to CAS, which controls the version of channel metadata, locks offer a higher level of control. They determine whether a user has the authority to call the setChannelMetadata, updateChannelMetadata, and removeChannelMetadata interfaces. Without acquiring the lock, a user cannot perform operations on channel metadata.
The following code demonstrates using a lock to update channel metadata. The user calling updateChannelMetadata must acquire the lock first for the call to succeed.
const properties = {
key : "Quantity",
value : "40"
};
const announcement = {
key : "Announcement",
value : "Welcome to our Shop!"
};
const price = {
key : "T-shirt",
value : "300"
};
const data = [properties, announcement, price];
const options = { addTimeStamp : ture, addUserId : ture, lockName : "manage" };
try {
const result = await rtm.storage.updateChannelMetadata("channel1", "MESSAGE", data, options);
console.log(JSON.stringify(result));
} catch (status) {
console.log(JSON.stringify(status));
}For more information on setting, acquiring, releasing, revoking, and removing locks, see Locks.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
API reference
The Signaling storage service enables you to store and share contextual channel data in your app, such as props, announcements, member lists, and relationship chains. When channel metadata is set, updated, or deleted, the SDK triggers a storage event notification. Other users in the channel receive this notification within 100ms and use the information according to your business logic.
Understand the tech
Use channel metadata to store and share channel level information such as room attributes, group announcements, and auction item price updates. A set of channel metadata for a specific channel facilitates business-level data storage and real-time notifications. Each channel has only one set of channel metadata, but each set may contain multiple metadata items. For relevant restrictions, refer to the API usage restrictions. Each metadata item has key, value, and revision properties.
Channel metadata is stored permanently in the Signaling database. The data persists even after a channel is destroyed. You must explicitly delete it to remove it from the database. This feature impacts your storage billing. Refer to Pricing for details.
The storage service is available for both message channels and stream channels. Use the channelType parameter in the storage event to determine the channel type.
Prerequisites
Ensure that you have:
- Integrated the Signaling SDK in your project.
- Implemented the framework functionality from the SDK quickstart page.
- Enabled storage in Storage configuration.
Implement channel metadata storage
The section shows you how to implement channel metadata storage in your Signaling app.
Set channel metadata
To create a new metadata item for the channel, or to update the value of an existing item, call setChannelMetadata. This method creates a new item in the channel metadata if the specified key does not exist, or overwrites the associated value if a metadata item with the specified key already exists.
The following example saves a set of metadata items for a message channel named channel1. Signaling adds timestamp and authorUid information to each metadata item it stores.
Metadata metadata = new Metadata();
metadata.getItems().add(new MetadataItem("Quantity", "20"));
metadata.getItems().add(new MetadataItem("Announcement", "Welcome to our shop!"));
metadata.getItems().add(new MetadataItem("T-shirt", "100"));
mRtmClient.getStorage().setChannelMetadata("channel1", RtmChannelType.MESSAGE, metadata, new MetadataOptions(true, true), nullptr, new ResultCallback<Void>() {
@Override
public void onSuccess(Void responseInfo) {
log(CALLBACK, "set channel metadata success");
}
@Override
public void onFailure(ErrorInfo errorInfo) {
log(ERROR, errorInfo.toString());
}
});val metadata = Metadata()
metadata.items.add(MetadataItem("Quantity", "20"))
metadata.items.add(MetadataItem("Announcement", "Welcome to our shop!"))
metadata.items.add(MetadataItem("T-shirt", "100"))
mRtmClient.getStorage().setChannelMetadata("channel1", RtmChannelType.MESSAGE, metadata, MetadataOptions(true, true), null, object : ResultCallback<Void> {
override fun onSuccess(responseInfo: Void?) {
log(CALLBACK, "set channel metadata success")
}
override fun onFailure(errorInfo: ErrorInfo) {
log(ERROR, errorInfo.toString())
}
})The onSuccess callback notifies you of the successful completion of the storage operation. Additionally, Signaling triggers an onStorageEvent notification of event type UPDATE within 100 ms to inform other channel members.
Get channel metadata
To retrieve all metadata items associated with a specific channel, call getChannelMetadata by specifying the channel name and the channel type. Refer to the following example:
mRtmClient.getStorage().getChannelMetadata("channel1", RtmChannelType.MESSAGE, new ResultCallback<Metadata>() {
@Override
public void onSuccess(Metadata data) {
log(CALLBACK, "get channel metadata success");
log(INFO, "metadata major revision: " + data.getMajorRevision());
for (MetadataItem item : data.getItems()) {
log(INFO, item.toString());
}
}
@Override
public void onFailure(ErrorInfo errorInfo) {
log(ERROR, errorInfo.toString());
}
});mRtmClient.getStorage().getChannelMetadata("channel1", RtmChannelType.MESSAGE, object : ResultCallback<Metadata> {
override fun onSuccess(data: Metadata) {
log(CALLBACK, "get channel metadata success")
log(INFO, "metadata major revision: " + data.majorRevision)
for (item in data.items) {
log(INFO, item.toString())
}
}
override fun onFailure(errorInfo: ErrorInfo) {
log(ERROR, errorInfo.toString())
}
})Signaling SDK returns the following data structure:
{
majorRevision: 734874892,
metadata:{
{ key:"Quantity",
value:"20",
revision:734874888,
updateTs:1688978391900,
authorUid:"Tony"
},
{
key:"Announcement",
value:"Welcome to our Shop!",
revision:734874333,
updated:1688978391800,
authorUid:"Tomas"
},
{
key:"T-shirt",
value:"100",
revision:734874222,
updated:168897839100,
authorUid:"Adam"
}
}
}Update channel metadata
To modify existing metadata items for a specified channel, call updateChannelMetadata. If the metadata item does not exist, an error is returned. This method is useful for business use-cases that require permission control to create new metadata items. For example, consider the following use-cases:
- In an e-commerce auction, only administrators or product owners are authorized to list new products and set new attributes, while bidders may only modify price attributes.
- In a gaming environment, only the administrator may define permissions as room properties.
The following example updates the value of a metadata item:
Metadata metadata = new Metadata();
metadata.getItems().add(new MetadataItem("T-shirt", "299"));
MetadataOptions options = new MetadataOptions();
options.setRecordTs(true);
options.setRecordUserId(true);
mRtmClient.getStorage().updateChannelMetadata("channel1", RtmChannelType.MESSAGE, metadata, options, "", new ResultCallback<Void>() {
@Override
public void onSuccess(Void responseInfo) {
log(CALLBACK, "Channel metadata updated successfully");
}
@Override
public void onFailure(ErrorInfo errorInfo) {
log(ERROR, errorInfo.toString());
}
});val metadata = Metadata()
metadata.items.add(MetadataItem("T-shirt", "299"))
val options = MetadataOptions()
options.setRecordTs(true)
options.setRecordUserId(true)
mRtmClient.getStorage().updateChannelMetadata("channel1", RtmChannelType.MESSAGE, metadata, options, "", object : ResultCallback<Void> {
override fun onSuccess(responseInfo: Void) {
log(CALLBACK, "Channel metadata updated successfully")
}
override fun onFailure(errorInfo: ErrorInfo) {
log(ERROR, errorInfo.toString())
}
})The onSuccess callback notifies you of the successful completion of the storage operation. Additionally, Signaling triggers an onStorageEvent notification of event type UPDATE within 100 ms to inform other channel members.
Delete channel metadata
To delete metadata items that are no longer required, call removeChannelMetadata. Refer to the following sample code:
Metadata metadata = new Metadata();
MetadataItem announcement = new MetadataItem();
announcement.setKey("Announcement");
metadata.getItems().add(announcement);
mRtmClient.getStorage().removeChannelMetadata("channel1", RtmChannelType.MESSAGE, metadata, new MetadataOptions(false, false), "", new ResultCallback<Void>() {
@Override
public void onSuccess(Void responseInfo) {
log(CALLBACK, "remove channel metadata success");
}
@Override
public void onFailure(ErrorInfo errorInfo) {
log(ERROR, errorInfo.toString());
}
});val metadata = Metadata()
val announcement = MetadataItem()
announcement.setKey("Announcement")
metadata.items.add(announcement)
mRtmClient.getStorage().removeChannelMetadata("channel1", RtmChannelType.MESSAGE, metadata, MetadataOptions(false, false), "", object : ResultCallback<Void> {
override fun onSuccess(responseInfo: Void) {
log(CALLBACK, "remove channel metadata success")
}
override fun onFailure(errorInfo: ErrorInfo) {
log(ERROR, errorInfo.toString())
}
})Setting the value for a metadata item that is being deleted has no effect.
The onSuccess callback notifies you of the successful completion of the storage operation. Additionally, Signaling triggers an onStorageEvent notification of event type UPDATE within 100 ms to inform other channel members.
To delete the entire set of metadata for a channel, do not specify any metadata items when calling removeChannelMetadata. Refer to the following sample code:
Metadata metadata = new Metadata();
mRtmClient.getStorage().removeChannelMetadata("channel1", RtmChannelType.MESSAGE, metadata, new MetadataOptions(true, true), "", new ResultCallback<Void>() {
@Override
public void onSuccess(Void responseInfo) {
log(CALLBACK, "remove channel metadata success");
}
@Override
public void onFailure(ErrorInfo errorInfo) {
log(ERROR, errorInfo.toString());
}
});val metadata = Metadata()
mRtmClient.getStorage().removeChannelMetadata("channel1", RtmChannelType.MESSAGE, metadata, MetadataOptions(true, true), "", object : ResultCallback<Void> {
override fun onSuccess(responseInfo: Void) {
log(CALLBACK, "remove channel metadata success")
}
override fun onFailure(errorInfo: ErrorInfo) {
log(ERROR, errorInfo.toString())
}
})Once channel metadata is deleted, it cannot be recovered. If you need data restoration, back up the metadata before deleting it.
Receive storage event notifications
A storage event notification returns the StorageEvent data structure, which includes the RtmStorageEventType parameter.
To receive storage event notifications, implement an event listener. See event listeners for details. In addition, set the withMetadata parameter to true when subscribing to or joining a channel.
Event notification mode
Currently, Signaling only supports the full data update mode. This means that when user or channel metadata is updated, the data field in the event notification contains all the attribute data of the user or the channel.
Additional storage features
To help resolve issues arising from concurrent updates to storage, Signaling offers version control and locking features.
Version control
Signaling integrates compare-and-set (CAS) version control to manage metadata updates. CAS is a concurrency control mechanism to ensure that updates to a shared resource occur only if the resource is in an expected state. The mechanism works as follows:
- The client reads the current version of a data item.
- Before making an update, the client compares the current version with the last read version number.
- If the versions match, the client proceeds with the update and increments the version number. If they do not match, the update is aborted.
The following are some sample use-cases where CAS version control is useful:
-
In a bidding use-case, if multiple users bid on a product at the same time, the first bidder succeeds while others receive an error. Users obtain the latest price information to update their bids.
-
In a red envelope grabbing use-case, the red envelope may only be grabbed once. The first user succeeds, while the rest receive an error.
The CAS version control feature provides two independent version control parameters. Set one or more of these values according to the needs of your business use-case:
-
majorRevisionparameter in thesetMajorRevisionmethod: Enable version number verification of the entire set of channel metadata. -
revisionparameter of aMetadataItem: Enable version number verification of a single metadata item.
When setting channel metadata, or a single channel metadata item, use the revision attribute to enable or disable version control as follows:
-
To disable CAS verification, use the default value of
-1for therevisionparameter. -
To enable CAS verification, set the
majorRevisionor therevisionparameter to a positive integer. The SDK updates the corresponding value after successfully verifying the revision number. If the specified revision number does not match the latest revision number in the database, the SDK returns an error.
The following sample shows how to use majorRevision and revision to update channel metadata and a metadata item:
Metadata metadata = new Metadata();
metadata.setMajorRevision(734874892);
metadata.getItems().add(new MetadataItem("Quantity", "30", 734874888));
metadata.getItems().add(new MetadataItem("Announcement", "Welcome to our shop!"));
metadata.getItems().add(new MetadataItem("T-shirt", "101", 734874222));
MetadataOptions options = new MetadataOptions()
options.setRecordTs(true);
options.setRecordUserId(true);
mRtmClient.getStorage().updateChannelMetadata("channel1", RtmChannelType.MESSAGE, metadata, options, "", new ResultCallback<Void>() {
@Override
public void onSuccess(Void responseInfo) {
log(CALLBACK, "update channel metadata success");
}
@Override
public void onFailure(ErrorInfo errorInfo) {
log(ERROR, errorInfo.toString());
}
});val metadata = Metadata()
metadata.setMajorRevision(734874892)
metadata.items.add(MetadataItem("Quantity", "30", 734874888))
metadata.items.add(MetadataItem("Announcement", "Welcome to our shop!"))
metadata.items.add(MetadataItem("T-shirt", "101", 734874222))
val options = MetadataOptions()
options.setRecordTs(true)
options.setRecordUserId(true)
mRtmClient.getStorage().updateChannelMetadata("channel1", RtmChannelType.MESSAGE, metadata, options, "", object : ResultCallback<Void> {
override fun onSuccess(responseInfo: Void) {
log(CALLBACK, "update channel metadata success")
}
override fun onFailure(errorInfo: ErrorInfo) {
log(ERROR, errorInfo.toString())
}
})In this example, CAS verification for channel metadata and metadata items is enabled by setting majorRevision and revision parameters to positive integers. Upon receiving the update call request, Signaling first verifies the provided major revision number against the latest value in the database. If there's a mismatch, it returns an error; if the values match, Signaling verifies the revision number for each metadata item using a similar logic.
When using version control, monitor onStorageEvent notifications to retrieve updated values for majorRevision and revision to ensure that the latest revision values are used for subsequent operations.
Locks
Locks enable users to gain exclusive access to critical resources, resolving contention issues with shared resources. For instance, consider a use-case where only one administrator is allowed in a channel at a time, and only the administrator can manage channel metadata by setting, deleting, and modifying it.
Compared to CAS, which controls the version of channel metadata, locks offer a higher level of control. They determine whether a user has the authority to call the setChannelMetadata, updateChannelMetadata, and removeChannelMetadata interfaces. Without acquiring the lock, a user cannot perform operations on channel metadata.
The following code demonstrates using a lock to update channel metadata. The user calling updateChannelMetadata must acquire the lock first for the call to succeed.
Metadata metadata = new Metadata();
metadata.getItems().add(new MetadataItem("Quantity", "40"));
metadata.getItems().add(new MetadataItem("Announcement", "Welcome to our Shop!"));
metadata.getItems().add(new MetadataItem("T-shirt", "300"));
MetadataOptions options = new MetadataOptions()
options.setRecordTs(true);
options.setRecordUserId(true);
String lockName = "manage";
mRtmClient.getStorage().updateChannelMetadata("channel1", RtmChannelType.MESSAGE, metadata, options, lockName, new ResultCallback<Void>() {
@Override
public void onSuccess(Void responseInfo) {
log(CALLBACK, "update channel metadata success");
}
@Override
public void onFailure(ErrorInfo errorInfo) {
log(ERROR, errorInfo.toString());
}
});val metadata = Metadata()
metadata.items.add(MetadataItem("Quantity", "40"))
metadata.items.add(MetadataItem("Announcement", "Welcome to our Shop!"))
metadata.items.add(MetadataItem("T-shirt", "300"))
val options = MetadataOptions()
options.setRecordTs(true)
options.setRecordUserId(true)
val lockName = "manage"
mRtmClient.getStorage().updateChannelMetadata("channel1", RtmChannelType.MESSAGE, metadata, options, lockName, object : ResultCallback<Void> {
override fun onSuccess(responseInfo: Void) {
log(CALLBACK, "update channel metadata success")
}
override fun onFailure(errorInfo: ErrorInfo) {
log(ERROR, errorInfo.toString())
}
})For more information on setting, acquiring, releasing, revoking, and removing locks, see Locks.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
API reference
The Signaling storage service enables you to store and share contextual channel data in your app, such as props, announcements, member lists, and relationship chains. When channel metadata is set, updated, or deleted, the SDK triggers a storage event notification. Other users in the channel receive this notification within 100ms and use the information according to your business logic.
Understand the tech
Use channel metadata to store and share channel level information such as room attributes, group announcements, and auction item price updates. A set of channel metadata for a specific channel facilitates business-level data storage and real-time notifications. Each channel has only one set of channel metadata, but each set may contain multiple metadata items. For relevant restrictions, refer to the API usage restrictions. Each metadata item has key, value, and revision properties.
Channel metadata is stored permanently in the Signaling database. The data persists even after a channel is destroyed. You must explicitly delete it to remove it from the database. This feature impacts your storage billing. Refer to Pricing for details.
The storage service is available for both message channels and stream channels. Use the channelType parameter in the storage event to determine the channel type.
Prerequisites
Ensure that you have:
- Integrated the Signaling SDK in your project.
- Implemented the framework functionality from the SDK quickstart page.
- Enabled storage in Storage configuration.
Implement channel metadata storage
The section shows you how to implement channel metadata storage in your Signaling app.
Set channel metadata
To create a new metadata item for the channel, or to update the value of an existing item, call setChannelMetadata. This method creates a new item in the channel metadata if the specified key does not exist, or overwrites the associated value if a metadata item with the specified key already exists.
The following example saves a set of metadata items for a message channel named channel1. Signaling adds timestamp and authorUid information to each metadata item it stores.
// Retrieve metadata
let metadata = AgoraRtmMetadata()!
// Set metadata items
let properties = AgoraRtmMetadataItem()
properties.key = "Quantity"
properties.value = "20"
let announcement = AgoraRtmMetadataItem()
announcement.key = "Announcement"
announcement.value = "Welcome to our shop!"
let price = AgoraRtmMetadataItem()
price.key = "T-shirt"
price.value = "100"
// Create an array of metadata items
let itemArray = [properties, announcement, price]
metadata.items = itemArray
let metadataOpt = AgoraRtmMetadataOptions()
metadataOpt.recordUserId = true
metadataOpt.recordTs = true
rtm.getStorage()?.setChannelMetadata(channelName: "channel1", channelType: .message, data: metadata, options: metadataOpt, lock: nil) { response, errorInfo in
if errorInfo == nil {
print("setChannelMetadata success!!")
} else {
if let errorCode = errorInfo?.errorCode, let reason = errorInfo?.reason {
print("setChannelMetadata failed, errorCode: \\(errorCode), reason: \\(reason)")
}
}
}When the call is successful, the SDK returns the AgoraRtmCommonResponse data structure. Additionally, Signaling triggers an didReceiveStorageEvent notification of event type update within 100 ms to inform other channel members.
// Initialize a new AgoraRtmMetadata object
AgoraRtmMetadata* metadata = [[AgoraRtmMetadata alloc] init];
// Set metadata items
AgoraRtmMetadataItem* properties = [[AgoraRtmMetadataItem alloc] init];
properties.key = @"Quantity";
properties.value = @"20";
AgoraRtmMetadataItem* announcement = [[AgoraRtmMetadataItem alloc] init];
announcement.key = @"Announcement";
announcement.value = @"Welcome to our shop!";
AgoraRtmMetadataItem* price = [[AgoraRtmMetadataItem alloc] init];
price.key = @"T-shirt";
price.value = @"100";
NSArray *item_array = [NSArray arrayWithObjects:properties, announcement, price];
metadata.items = item_array;
AgoraRtmMetadataOptions* metadata_opt = [[AgoraRtmMetadataOptions alloc] init];
metadata_opt.recordUserId = true;
metadata_opt.recordTs = true;
[[rtm getStorage] setChannelMetadata:@"channel1" channelType:AgoraRtmChannelTypeMessage data:metadata options:metadata_opt lock:nil completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
if (errorInfo == nil) {
NSLog(@"setChannelMetadata success!!");
} else {
NSLog(@"setChannelMetadata failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
}
}];When the call is successful, the SDK returns the AgoraRtmCommonResponse data structure. Additionally, Signaling triggers an didReceiveStorageEvent notification of event type AgoraRtmStorageEventTypeUpdate within 100 ms to inform other channel members.
Get channel metadata
To retrieve all metadata items associated with a specific channel, call getChannelMetadata by specifying the channel name and the channel type. Refer to the following example:
rtm.getStorage()?.getChannelMetadata(channelName: "channel1",, channelType: .message) { response, errorInfo in
if errorInfo == nil {
print("getChannelMetadata success!!")
if let data = response?.data {
}
} else {
if let errorCode = errorInfo?.errorCode, let reason = errorInfo?.reason {
print("getChannelMetadata failed, errorCode: \\(errorCode), reason: \\(reason)")
}
}
}[[rtm getStorage] getChannelMetadata:@"channel1" channelType:AgoraRtmChannelTypeMessage completion:^(AgoraRtmGetMetadataResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
if (errorInfo == nil) {
NSLog(@"getChannelMetadata success!!");
AgoraRtmMetadata* data = response.data; //get storage data;
} else {
NSLog(@"getChannelMetadata failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
}
}];Signaling SDK returns the following data structure:
{
majorRevision: 734874892,
metadata:{
"Quantity":{
value:"20",
revision:734874888,
updated:1688978391900,
authorUid:"Tony"
},
"Announcement":{
value:"Welcome to our Shop!",
revision:734874333,
updated:1688978391800,
authorUid:"Tomas"
},
"T-shirt":{
value:"100",
revision:734874222,
updated:168897839100,
authorUid:"Adam"
}
}
}Update channel metadata
To modify existing metadata items for a specified channel, call updateChannelMetadata. If the metadata item does not exist, an error is returned. This method is useful for business use-cases that require permission control on creating new metadata items. For example, consider the following use-cases:
- In an e-commerce auction, only administrators or product owners are authorized to list new products and set new attributes, while bidders may only modify price attributes.
- In a gaming environment, only the administrator may define permissions as room properties.
The following example updates the value of a metadata item:
// Retrieve metadata
let metadata = AgoraRtmMetadata()!
// Set a metadata item
let price = AgoraRtmMetadataItem()
price.key = "T-shirt"
price.value = "299"
// Create an array of metadata items
let itemArray = [price]
metadata.items = itemArray
let metadataOpt = AgoraRtmMetadataOptions()
metadataOpt.recordUserId = true
metadataOpt.recordTs = true
// Update channel metadata
rtm.getStorage()?.updateChannelMetadata(channelName: "channel1", channelType: .message, data: metadata, options: metadataOpt, lock: nil) { response, errorInfo in
if errorInfo == nil {
print("updateChannelMetadata success!!")
} else {
if let errorCode = errorInfo?.errorCode, let reason = errorInfo?.reason {
print("updateChannelMetadata failed, errorCode: \\(errorCode), reason: \\(reason)")
}
}
}When the call is successful, the SDK returns the AgoraRtmCommonResponse data structure. Additionally, Signaling triggers an didReceiveStorageEvent notification of event type update within 100 ms to inform other channel members.
// Initialize a new AgoraRtmMetadata object
AgoraRtmMetadata* metadata = [[AgoraRtmMetadata alloc] init];
// Set metadata items
AgoraRtmMetadataItem* price = [[AgoraRtmMetadataItem alloc] init];
price.key = @"T-shirt";
price.value = @"299";
NSArray *item_array = [NSArray arrayWithObjects:price];
metadata.items = item_array;
AgoraRtmMetadataOptions* metadata_opt = [[AgoraRtmMetadataOptions alloc] init];
metadata_opt.recordUserId = true;
metadata_opt.recordTs = true;
[[rtm getStorage] updateChannelMetadata:@"channel1" channelType:AgoraRtmChannelTypeMessage data:metadata options:metadata_opt lock:nil completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
if (errorInfo == nil) {
NSLog(@"updateChannelMetadata success!!");
} else {
NSLog(@"updateChannelMetadata failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
}
}];When the call is successful, the SDK returns the AgoraRtmCommonResponse data structure. Additionally, Signaling triggers an didReceiveStorageEvent notification of event type AgoraRtmStorageEventTypeUpdate within 100 ms to inform other channel members.
Delete channel metadata
To delete metadata items that are no longer required, call removeChannelMetadata. Refer to the following sample code:
let metadata = AgoraRtmMetadata()!
let announcement = AgoraRtmMetadataItem()
announcement.key = "Announcement"
let itemArray = [announcement]
metadata.items = itemArray
rtm.getStorage()?.removeChannelMetadata(channelName: "channel1", channelType: .message, data: metadata, options: nil, lock: nil) { response, errorInfo in
if errorInfo == nil {
print("removeChannelMetadata success!!")
} else {
if let errorCode = errorInfo?.errorCode, let reason = errorInfo?.reason {
print("removeChannelMetadata failed, errorCode: \\(errorCode), reason: \\(reason)")
}
}
}Setting the value for a metadata item that is being deleted has no effect.
When the call is successful, the SDK returns the AgoraRtmCommonResponse data structure. Additionally, Signaling triggers an didReceiveStorageEvent notification of event type update within 100 ms to inform other channel members.
AgoraRtmMetadata* metadata = [[AgoraRtmMetadata alloc] init];
AgoraRtmMetadataItem* announcement = [[AgoraRtmMetadataItem alloc] init];
announcement.key = @"Announcement";
NSArray *item_array = [NSArray arrayWithObjects:announcement];
metadata.items = item_array;
[[rtm getStorage] removeChannelMetadata:@"channel1" channelType:AgoraRtmChannelTypeMessage data:metadata options:nil lock:nil completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
if (errorInfo == nil) {
NSLog(@"removeChannelMetadata success!!");
} else {
NSLog(@"removeChannelMetadata failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
}
}];Setting a value for a metadata item that is being deleted has no effect.
When the call is successful, the SDK returns the AgoraRtmCommonResponse data structure. Additionally, Signaling triggers an didReceiveStorageEvent notification of event type AgoraRtmStorageEventTypeUpdate within 100 ms to inform other channel members.
To delete the entire set of metadata for a channel, do not specify any metadata items when calling removeChannelMetadata. Refer to the following sample code:
let metadata = AgoraRtmMetadata()!
let announcement = AgoraRtmMetadataItem()
announcement.key = "Announcement"
let itemArray = [announcement]
metadata.items = itemArray
rtm.getStorage()?.removeChannelMetadata(channelName: "channel1", channelType: .message, data: metadata, options: nil, lock: nil) { response, errorInfo in
if errorInfo == nil {
print("removeChannelMetadata success!!")
} else {
if let errorCode = errorInfo?.errorCode, let reason = errorInfo?.reason {
print("removeChannelMetadata failed, errorCode: \(errorCode), reason: \(reason)")
}
}
}AgoraRtmMetadata* metadata = [[AgoraRtmMetadata alloc] init];;
[[rtm getStorage] removeChannelMetadata:@"channel1" channelType:AgoraRtmChannelTypeMessage data:metadata options:nil lock:nil completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
if (errorInfo == nil) {
NSLog(@"removeChannelMetadata success!!");
} else {
NSLog(@"removeChannelMetadata failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
}
}];Once channel metadata is deleted, it cannot be recovered. To implement data restoration, back up the metadata before deleting it.
Receive storage event notifications
A storage event notification returns the AgoraRtmStorageEvent data structure, which includes the AgoraRtmStorageEventType parameter.
To receive storage event notifications, implement an event listener. See event listeners for details. In addition, add metadata to the features parameter under options when subscribing to or joining a channel.
Event notification mode
Currently, Signaling only supports the full data update mode. This means that when user or channel metadata is updated, the data field in the event notification contains all the attribute data of the user or the channel.
Additional storage features
To help resolve issues arising from concurrent updates to storage, Signaling offers version control and locking features.
Version control
The Compare and Set (CAS) version control feature in channel metadata storage provides two independent version control parameters. Set one or more of these values according to the needs of your business use-case:
-
majorRevisionparameter in thesetMajorRevisionmethod: Enable version number verification of the entire set of channel metadata. -
revisionparameter of aAgoraRtmMetadataItem: Enable version number verification of a single metadata item.
When setting channel metadata, or a single channel metadata item, use the revision attribute to enable or disable version control as follows:
-
To disable CAS verification, use the default value of
-1for therevisionparameter. If the channel metadata or channel metadata item already exists, the value is overwritten. If it does not exist, the SDK creates a metadata item and updates the value. -
To enable CAS verification, set the
majorRevisionor therevisionparameter to a positive integer. If the channel metadata or channel metadata item already exists, the SDK updates the corresponding value after successfully verifying the revision number. If the specified revision number does not match the latest revision number in the database, the SDK returns an error code.
The following sample shows how to use majorRevision and revision to update channel metadata and a metadata item:
let metadata = AgoraRtmMetadata()
let properties = AgoraRtmMetadataItem()
properties.key = "Quantity"
properties.value = "30"
properties.revision = 734874888
let announcement = AgoraRtmMetadataItem()
announcement.key = "Announcement"
announcement.value = "Welcome to our shop!"
let price = AgoraRtmMetadataItem()
price.key = "T-shirt"
price.value = "101"
price.revision = 734874222
let itemArray = [properties, announcement, price]
metadata.items = itemArray
metadata.majorRevision = 734874892
let metadataOpt = AgoraRtmMetadataOptions()
metadataOpt.recordUserId = true
metadataOpt.records = true
rtm.getStorage()?.updateChannelMetadata(channelName: "channel1", channelType: .message, data: metadata, options: metadataOpt, lock: nil) { response, errorInfo in
if errorInfo == nil {
print("updateChannelMetadata success!!")
} else {
if let errorCode = errorInfo?.errorCode, let reason = errorInfo?.reason {
print("updateChannelMetadata failed, errorCode: \\(errorCode), reason: \\(reason)")
}
}
}AgoraRtmMetadata* metadata = [[AgoraRtmMetadata alloc] init];
AgoraRtmMetadataItem* properties = [[AgoraRtmMetadataItem alloc] init];
properties.key = @"Quantity";
properties.value = @"30";
properties.revision = 734874888;
AgoraRtmMetadataItem* announcement = [[AgoraRtmMetadataItem alloc] init];
announcement.key = @"Announcement";
announcement.value = @"Welcome to our shop!";
AgoraRtmMetadataItem* price = [[AgoraRtmMetadataItem alloc] init];
price.key = @"T-shirt";
price.value = @"101";
price.revision = 734874222;
NSArray *item_array = [NSArray arrayWithObjects:properties, announcement, price];
metadata.items = item_array;
metadata.majorRevision = 734874892; // set marjor revision
AgoraRtmMetadataOptions* metadata_opt = [[AgoraRtmMetadataOptions alloc] init];
metadata_opt.recordUserId = true;
metadata_opt.recordTs = true;
[[rtm getStorage] updateChannelMetadata:@"channel1" channelType:AgoraRtmChannelTypeMessage data:metadata options:metadata_opt lock:nil completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
if (errorInfo == nil) {
NSLog(@"updateChannelMetadata success!!");
} else {
NSLog(@"updateChannelMetadata failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
}
}];In the above example, CAS verification for channel metadata and metadata items is enabled by setting majorRevision and revision parameters to positive integers. Upon receiving the update call request, Signaling first verifies the provided major revision number against the latest value in the database. If there's a mismatch, it returns an error. If the values match, Signaling verifies the revision number for each metadata item using a similar logic.
When using version control, monitor didReceiveStorageEvent notifications to retrieve updated values for majorRevision and revision to ensure that the latest revision values are used for subsequent operations.
Following are some sample use-cases where version control is useful:
-
In a bidding use-case, if multiple users bid on a product at the same time, the first bidder succeeds, while others receive an error. Users obtain the latest price information to update their bids.
-
In a red envelope grabbing use-case, the red envelope may only be grabbed once. The first user succeeds, while the rest receive an error.
Locks
Locks enable users to gain exclusive access to critical resources, resolving contention issues with shared resources. For instance, consider a use-case where only one administrator is allowed in a channel at a time, and only the administrator can manage channel metadata by setting, deleting, and modifying it.
Compared to CAS, which controls the version of channel metadata, locks offer a higher level of control. They determine whether a user has the authority to call the setChannelMetadata, updateChannelMetadata, and removeChannelMetadata interfaces. Without acquiring the lock, a user cannot perform operations on channel metadata.
The following code demonstrates using a lock to update channel metadata. The user calling updateChannelMetadata must acquire the lock first for the call to succeed.
let metadata = AgoraRtmMetadata()
let properties = AgoraRtmMetadataItem()
properties.key = "Quantity"
properties.value = "30"
properties.revision = 734874888
let announcement = AgoraRtmMetadataItem()
announcement.key = "Announcement"
announcement.value = "Welcome to our shop!"
let price = AgoraRtmMetadataItem()
price.key = "T-shirt"
price.value = "101"
price.revision = 734874222
let itemArray = [properties, announcement, price]
metadata.items = itemArray
metadata.majorRevision = 734874892
let metadataOpt = AgoraRtmMetadataOptions()
metadataOpt.recordUserId = true
metadataOpt.recordTs = true
rtm.getStorage()?.updateChannelMetadata(channelName: "channel1", channelType: .message, data: metadata, options: metadataOpt, lock: "manage") { response, errorInfo in
if errorInfo == nil {
print("updateChannelMetadata success!!")
} else {
if let errorCode = errorInfo?.errorCode, let reason = errorInfo?.reason {
print("updateChannelMetadata failed, errorCode: \\(errorCode), reason: \\(reason)")
}
}
}AgoraRtmMetadata* metadata = [[AgoraRtmMetadata alloc] init];
AgoraRtmMetadataItem* properties = [[AgoraRtmMetadataItem alloc] init];
properties.key = @"Quantity";
properties.value = @"40";
AgoraRtmMetadataItem* announcement = [[AgoraRtmMetadataItem alloc] init];
announcement.key = @"Announcement";
announcement.value = @"Welcome to our shop!";
AgoraRtmMetadataItem* price = [[AgoraRtmMetadataItem alloc] init];
price.key = @"T-shirt";
price.value = @"300";
NSArray *item_array = [NSArray arrayWithObjects:properties, announcement, price];
metadata.items = item_array;
AgoraRtmMetadataOptions* metadata_opt = [[AgoraRtmMetadataOptions alloc] init];
metadata_opt.recordUserId = true;
metadata_opt.recordTs = true;
[[rtm getStorage] updateChannelMetadata:@"channel1" channelType:AgoraRtmChannelTypeMessage data:metadata options:metadata_opt lock:@"manage" completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
if (errorInfo == nil) {
NSLog(@"updateChannelMetadata success!!");
} else {
NSLog(@"updateChannelMetadata failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
}
}];For more information on setting, acquiring, releasing, revoking, and removing locks, see Locks.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
API reference
The Signaling storage service enables you to store and share contextual channel data in your app, such as props, announcements, member lists, and relationship chains. When channel metadata is set, updated, or deleted, the SDK triggers a storage event notification. Other users in the channel receive this notification within 100ms and use the information according to your business logic.
Understand the tech
Use channel metadata to store and share channel level information such as room attributes, group announcements, and auction item price updates. A set of channel metadata for a specific channel facilitates business-level data storage and real-time notifications. Each channel has only one set of channel metadata, but each set may contain multiple metadata items. For relevant restrictions, refer to the API usage restrictions. Each metadata item has key, value, and revision properties.
Channel metadata is stored permanently in the Signaling database. The data persists even after a channel is destroyed. You must explicitly delete it to remove it from the database. This feature impacts your storage billing. Refer to Pricing for details.
The storage service is available for both message channels and stream channels. Use the channelType parameter in the storage event to determine the channel type.
Prerequisites
Ensure that you have:
- Integrated the Signaling SDK in your project.
- Implemented the framework functionality from the SDK quickstart page.
- Enabled storage in Storage configuration.
Implement channel metadata storage
The section shows you how to implement channel metadata storage in your Signaling app.
Set channel metadata
To create a new metadata item for the channel, or to update the value of an existing item, call setChannelMetadata. This method creates a new item in the channel metadata if the specified key does not exist, or overwrites the associated value if a metadata item with the specified key already exists.
The following example saves a set of metadata items for a message channel named channel1. Signaling adds timestamp and authorUid information to each metadata item it stores.
// Retrieve metadata
let metadata = AgoraRtmMetadata()!
// Set metadata items
let properties = AgoraRtmMetadataItem()
properties.key = "Quantity"
properties.value = "20"
let announcement = AgoraRtmMetadataItem()
announcement.key = "Announcement"
announcement.value = "Welcome to our shop!"
let price = AgoraRtmMetadataItem()
price.key = "T-shirt"
price.value = "100"
// Create an array of metadata items
let itemArray = [properties, announcement, price]
metadata.items = itemArray
let metadataOpt = AgoraRtmMetadataOptions()
metadataOpt.recordUserId = true
metadataOpt.recordTs = true
rtm.getStorage()?.setChannelMetadata(channelName: "channel1", channelType: .message, data: metadata, options: metadataOpt, lock: nil) { response, errorInfo in
if errorInfo == nil {
print("setChannelMetadata success!!")
} else {
if let errorCode = errorInfo?.errorCode, let reason = errorInfo?.reason {
print("setChannelMetadata failed, errorCode: \\(errorCode), reason: \\(reason)")
}
}
}When the call is successful, the SDK returns the AgoraRtmCommonResponse data structure. Additionally, Signaling triggers an didReceiveStorageEvent notification of event type update within 100 ms to inform other channel members.
// Initialize a new AgoraRtmMetadata object
AgoraRtmMetadata* metadata = [[AgoraRtmMetadata alloc] init];
// Set metadata items
AgoraRtmMetadataItem* properties = [[AgoraRtmMetadataItem alloc] init];
properties.key = @"Quantity";
properties.value = @"20";
AgoraRtmMetadataItem* announcement = [[AgoraRtmMetadataItem alloc] init];
announcement.key = @"Announcement";
announcement.value = @"Welcome to our shop!";
AgoraRtmMetadataItem* price = [[AgoraRtmMetadataItem alloc] init];
price.key = @"T-shirt";
price.value = @"100";
NSArray *item_array = [NSArray arrayWithObjects:properties, announcement, price];
metadata.items = item_array;
AgoraRtmMetadataOptions* metadata_opt = [[AgoraRtmMetadataOptions alloc] init];
metadata_opt.recordUserId = true;
metadata_opt.recordTs = true;
[[rtm getStorage] setChannelMetadata:@"channel1" channelType:AgoraRtmChannelTypeMessage data:metadata options:metadata_opt lock:nil completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
if (errorInfo == nil) {
NSLog(@"setChannelMetadata success!!");
} else {
NSLog(@"setChannelMetadata failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
}
}];When the call is successful, the SDK returns the AgoraRtmCommonResponse data structure. Additionally, Signaling triggers an didReceiveStorageEvent notification of event type AgoraRtmStorageEventTypeUpdate within 100 ms to inform other channel members.
Get channel metadata
To retrieve all metadata items associated with a specific channel, call getChannelMetadata by specifying the channel name and the channel type. Refer to the following example:
rtm.getStorage()?.getChannelMetadata(channelName: "channel1",, channelType: .message) { response, errorInfo in
if errorInfo == nil {
print("getChannelMetadata success!!")
if let data = response?.data {
}
} else {
if let errorCode = errorInfo?.errorCode, let reason = errorInfo?.reason {
print("getChannelMetadata failed, errorCode: \\(errorCode), reason: \\(reason)")
}
}
}[[rtm getStorage] getChannelMetadata:@"channel1" channelType:AgoraRtmChannelTypeMessage completion:^(AgoraRtmGetMetadataResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
if (errorInfo == nil) {
NSLog(@"getChannelMetadata success!!");
AgoraRtmMetadata* data = response.data; //get storage data;
} else {
NSLog(@"getChannelMetadata failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
}
}];Signaling SDK returns the following data structure:
{
majorRevision: 734874892,
metadata:{
"Quantity":{
value:"20",
revision:734874888,
updated:1688978391900,
authorUid:"Tony"
},
"Announcement":{
value:"Welcome to our Shop!",
revision:734874333,
updated:1688978391800,
authorUid:"Tomas"
},
"T-shirt":{
value:"100",
revision:734874222,
updated:168897839100,
authorUid:"Adam"
}
}
}Update channel metadata
To modify existing metadata items for a specified channel, call updateChannelMetadata. If the metadata item does not exist, an error is returned. This method is useful for business use-cases that require permission control on creating new metadata items. For example, consider the following use-cases:
- In an e-commerce auction, only administrators or product owners are authorized to list new products and set new attributes, while bidders may only modify price attributes.
- In a gaming environment, only the administrator may define permissions as room properties.
The following example updates the value of a metadata item:
// Retrieve metadata
let metadata = AgoraRtmMetadata()!
// Set a metadata item
let price = AgoraRtmMetadataItem()
price.key = "T-shirt"
price.value = "299"
// Create an array of metadata items
let itemArray = [price]
metadata.items = itemArray
let metadataOpt = AgoraRtmMetadataOptions()
metadataOpt.recordUserId = true
metadataOpt.recordTs = true
// Update channel metadata
rtm.getStorage()?.updateChannelMetadata(channelName: "channel1", channelType: .message, data: metadata, options: metadataOpt, lock: nil) { response, errorInfo in
if errorInfo == nil {
print("updateChannelMetadata success!!")
} else {
if let errorCode = errorInfo?.errorCode, let reason = errorInfo?.reason {
print("updateChannelMetadata failed, errorCode: \\(errorCode), reason: \\(reason)")
}
}
}When the call is successful, the SDK returns the AgoraRtmCommonResponse data structure. Additionally, Signaling triggers an didReceiveStorageEvent notification of event type update within 100 ms to inform other channel members.
// Initialize a new AgoraRtmMetadata object
AgoraRtmMetadata* metadata = [[AgoraRtmMetadata alloc] init];
// Set metadata items
AgoraRtmMetadataItem* price = [[AgoraRtmMetadataItem alloc] init];
price.key = @"T-shirt";
price.value = @"299";
NSArray *item_array = [NSArray arrayWithObjects:price];
metadata.items = item_array;
AgoraRtmMetadataOptions* metadata_opt = [[AgoraRtmMetadataOptions alloc] init];
metadata_opt.recordUserId = true;
metadata_opt.recordTs = true;
[[rtm getStorage] updateChannelMetadata:@"channel1" channelType:AgoraRtmChannelTypeMessage data:metadata options:metadata_opt lock:nil completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
if (errorInfo == nil) {
NSLog(@"updateChannelMetadata success!!");
} else {
NSLog(@"updateChannelMetadata failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
}
}];When the call is successful, the SDK returns the AgoraRtmCommonResponse data structure. Additionally, Signaling triggers an didReceiveStorageEvent notification of event type AgoraRtmStorageEventTypeUpdate within 100 ms to inform other channel members.
Delete channel metadata
To delete metadata items that are no longer required, call removeChannelMetadata. Refer to the following sample code:
let metadata = AgoraRtmMetadata()!
let announcement = AgoraRtmMetadataItem()
announcement.key = "Announcement"
let itemArray = [announcement]
metadata.items = itemArray
rtm.getStorage()?.removeChannelMetadata(channelName: "channel1", channelType: .message, data: metadata, options: nil, lock: nil) { response, errorInfo in
if errorInfo == nil {
print("removeChannelMetadata success!!")
} else {
if let errorCode = errorInfo?.errorCode, let reason = errorInfo?.reason {
print("removeChannelMetadata failed, errorCode: \\(errorCode), reason: \\(reason)")
}
}
}Setting the value for a metadata item that is being deleted has no effect.
When the call is successful, the SDK returns the AgoraRtmCommonResponse data structure. Additionally, Signaling triggers an didReceiveStorageEvent notification of event type update within 100 ms to inform other channel members.
AgoraRtmMetadata* metadata = [[AgoraRtmMetadata alloc] init];
AgoraRtmMetadataItem* announcement = [[AgoraRtmMetadataItem alloc] init];
announcement.key = @"Announcement";
NSArray *item_array = [NSArray arrayWithObjects:announcement];
metadata.items = item_array;
[[rtm getStorage] removeChannelMetadata:@"channel1" channelType:AgoraRtmChannelTypeMessage data:metadata options:nil lock:nil completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
if (errorInfo == nil) {
NSLog(@"removeChannelMetadata success!!");
} else {
NSLog(@"removeChannelMetadata failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
}
}];Setting a value for a metadata item that is being deleted has no effect.
When the call is successful, the SDK returns the AgoraRtmCommonResponse data structure. Additionally, Signaling triggers an didReceiveStorageEvent notification of event type AgoraRtmStorageEventTypeUpdate within 100 ms to inform other channel members.
To delete the entire set of metadata for a channel, do not specify any metadata items when calling removeChannelMetadata. Refer to the following sample code:
let metadata = AgoraRtmMetadata()!
let announcement = AgoraRtmMetadataItem()
announcement.key = "Announcement"
let itemArray = [announcement]
metadata.items = itemArray
rtm.getStorage()?.removeChannelMetadata(channelName: "channel1", channelType: .message, data: metadata, options: nil, lock: nil) { response, errorInfo in
if errorInfo == nil {
print("removeChannelMetadata success!!")
} else {
if let errorCode = errorInfo?.errorCode, let reason = errorInfo?.reason {
print("removeChannelMetadata failed, errorCode: \(errorCode), reason: \(reason)")
}
}
}AgoraRtmMetadata* metadata = [[AgoraRtmMetadata alloc] init];;
[[rtm getStorage] removeChannelMetadata:@"channel1" channelType:AgoraRtmChannelTypeMessage data:metadata options:nil lock:nil completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
if (errorInfo == nil) {
NSLog(@"removeChannelMetadata success!!");
} else {
NSLog(@"removeChannelMetadata failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
}
}];Once channel metadata is deleted, it cannot be recovered. To implement data restoration, back up the metadata before deleting it.
Receive storage event notifications
A storage event notification returns the AgoraRtmStorageEvent data structure, which includes the AgoraRtmStorageEventType parameter.
To receive storage event notifications, implement an event listener. See event listeners for details. In addition, add metadata to the features parameter under options when subscribing to or joining a channel.
Event notification mode
Currently, Signaling only supports the full data update mode. This means that when user or channel metadata is updated, the data field in the event notification contains all the attribute data of the user or the channel.
Additional storage features
To help resolve issues arising from concurrent updates to storage, Signaling offers version control and locking features.
Version control
The Compare and Set (CAS) version control feature in channel metadata storage provides two independent version control parameters. Set one or more of these values according to the needs of your business use-case:
-
majorRevisionparameter in thesetMajorRevisionmethod: Enable version number verification of the entire set of channel metadata. -
revisionparameter of aAgoraRtmMetadataItem: Enable version number verification of a single metadata item.
When setting channel metadata, or a single channel metadata item, use the revision attribute to enable or disable version control as follows:
-
To disable CAS verification, use the default value of
-1for therevisionparameter. If the channel metadata or channel metadata item already exists, the value is overwritten. If it does not exist, the SDK creates a metadata item and updates the value. -
To enable CAS verification, set the
majorRevisionor therevisionparameter to a positive integer. If the channel metadata or channel metadata item already exists, the SDK updates the corresponding value after successfully verifying the revision number. If the specified revision number does not match the latest revision number in the database, the SDK returns an error code.
The following sample shows how to use majorRevision and revision to update channel metadata and a metadata item:
let metadata = AgoraRtmMetadata()
let properties = AgoraRtmMetadataItem()
properties.key = "Quantity"
properties.value = "30"
properties.revision = 734874888
let announcement = AgoraRtmMetadataItem()
announcement.key = "Announcement"
announcement.value = "Welcome to our shop!"
let price = AgoraRtmMetadataItem()
price.key = "T-shirt"
price.value = "101"
price.revision = 734874222
let itemArray = [properties, announcement, price]
metadata.items = itemArray
metadata.majorRevision = 734874892
let metadataOpt = AgoraRtmMetadataOptions()
metadataOpt.recordUserId = true
metadataOpt.records = true
rtm.getStorage()?.updateChannelMetadata(channelName: "channel1", channelType: .message, data: metadata, options: metadataOpt, lock: nil) { response, errorInfo in
if errorInfo == nil {
print("updateChannelMetadata success!!")
} else {
if let errorCode = errorInfo?.errorCode, let reason = errorInfo?.reason {
print("updateChannelMetadata failed, errorCode: \\(errorCode), reason: \\(reason)")
}
}
}AgoraRtmMetadata* metadata = [[AgoraRtmMetadata alloc] init];
AgoraRtmMetadataItem* properties = [[AgoraRtmMetadataItem alloc] init];
properties.key = @"Quantity";
properties.value = @"30";
properties.revision = 734874888;
AgoraRtmMetadataItem* announcement = [[AgoraRtmMetadataItem alloc] init];
announcement.key = @"Announcement";
announcement.value = @"Welcome to our shop!";
AgoraRtmMetadataItem* price = [[AgoraRtmMetadataItem alloc] init];
price.key = @"T-shirt";
price.value = @"101";
price.revision = 734874222;
NSArray *item_array = [NSArray arrayWithObjects:properties, announcement, price];
metadata.items = item_array;
metadata.majorRevision = 734874892; // set marjor revision
AgoraRtmMetadataOptions* metadata_opt = [[AgoraRtmMetadataOptions alloc] init];
metadata_opt.recordUserId = true;
metadata_opt.recordTs = true;
[[rtm getStorage] updateChannelMetadata:@"channel1" channelType:AgoraRtmChannelTypeMessage data:metadata options:metadata_opt lock:nil completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
if (errorInfo == nil) {
NSLog(@"updateChannelMetadata success!!");
} else {
NSLog(@"updateChannelMetadata failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
}
}];In the above example, CAS verification for channel metadata and metadata items is enabled by setting majorRevision and revision parameters to positive integers. Upon receiving the update call request, Signaling first verifies the provided major revision number against the latest value in the database. If there's a mismatch, it returns an error. If the values match, Signaling verifies the revision number for each metadata item using a similar logic.
When using version control, monitor didReceiveStorageEvent notifications to retrieve updated values for majorRevision and revision to ensure that the latest revision values are used for subsequent operations.
Following are some sample use-cases where version control is useful:
-
In a bidding use-case, if multiple users bid on a product at the same time, the first bidder succeeds, while others receive an error. Users obtain the latest price information to update their bids.
-
In a red envelope grabbing use-case, the red envelope may only be grabbed once. The first user succeeds, while the rest receive an error.
Locks
Locks enable users to gain exclusive access to critical resources, resolving contention issues with shared resources. For instance, consider a use-case where only one administrator is allowed in a channel at a time, and only the administrator can manage channel metadata by setting, deleting, and modifying it.
Compared to CAS, which controls the version of channel metadata, locks offer a higher level of control. They determine whether a user has the authority to call the setChannelMetadata, updateChannelMetadata, and removeChannelMetadata interfaces. Without acquiring the lock, a user cannot perform operations on channel metadata.
The following code demonstrates using a lock to update channel metadata. The user calling updateChannelMetadata must acquire the lock first for the call to succeed.
let metadata = AgoraRtmMetadata()
let properties = AgoraRtmMetadataItem()
properties.key = "Quantity"
properties.value = "30"
properties.revision = 734874888
let announcement = AgoraRtmMetadataItem()
announcement.key = "Announcement"
announcement.value = "Welcome to our shop!"
let price = AgoraRtmMetadataItem()
price.key = "T-shirt"
price.value = "101"
price.revision = 734874222
let itemArray = [properties, announcement, price]
metadata.items = itemArray
metadata.majorRevision = 734874892
let metadataOpt = AgoraRtmMetadataOptions()
metadataOpt.recordUserId = true
metadataOpt.recordTs = true
rtm.getStorage()?.updateChannelMetadata(channelName: "channel1", channelType: .message, data: metadata, options: metadataOpt, lock: "manage") { response, errorInfo in
if errorInfo == nil {
print("updateChannelMetadata success!!")
} else {
if let errorCode = errorInfo?.errorCode, let reason = errorInfo?.reason {
print("updateChannelMetadata failed, errorCode: \\(errorCode), reason: \\(reason)")
}
}
}AgoraRtmMetadata* metadata = [[AgoraRtmMetadata alloc] init];
AgoraRtmMetadataItem* properties = [[AgoraRtmMetadataItem alloc] init];
properties.key = @"Quantity";
properties.value = @"40";
AgoraRtmMetadataItem* announcement = [[AgoraRtmMetadataItem alloc] init];
announcement.key = @"Announcement";
announcement.value = @"Welcome to our shop!";
AgoraRtmMetadataItem* price = [[AgoraRtmMetadataItem alloc] init];
price.key = @"T-shirt";
price.value = @"300";
NSArray *item_array = [NSArray arrayWithObjects:properties, announcement, price];
metadata.items = item_array;
AgoraRtmMetadataOptions* metadata_opt = [[AgoraRtmMetadataOptions alloc] init];
metadata_opt.recordUserId = true;
metadata_opt.recordTs = true;
[[rtm getStorage] updateChannelMetadata:@"channel1" channelType:AgoraRtmChannelTypeMessage data:metadata options:metadata_opt lock:@"manage" completion:^(AgoraRtmCommonResponse * _Nullable response, AgoraRtmErrorInfo * _Nullable errorInfo) {
if (errorInfo == nil) {
NSLog(@"updateChannelMetadata success!!");
} else {
NSLog(@"updateChannelMetadata failed, errorCode %d, reason %@", errorInfo.errorCode, errorInfo.reason);
}
}];For more information on setting, acquiring, releasing, revoking, and removing locks, see Locks.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
API reference
The Signaling storage service enables you to store and share contextual channel data in your app, such as props, announcements, member lists, and relationship chains. When channel metadata is set, updated, or deleted, the SDK triggers a storage event notification. Other users in the channel receive this notification within 100ms and use the information according to your business logic.
Understand the tech
Use channel metadata to store and share channel level information such as room attributes, group announcements, and auction item price updates. A set of channel metadata for a specific channel facilitates business-level data storage and real-time notifications. Each channel has only one set of channel metadata, but each set may contain multiple metadata items. For relevant restrictions, refer to the API usage restrictions. Each metadata item has key, value, and revision properties.
Channel metadata is stored permanently in the Signaling database. The data persists even after a channel is destroyed. You must explicitly delete it to remove it from the database. This feature impacts your storage billing. Refer to Pricing for details.
The storage service is available for both message channels and stream channels. Use the channelType parameter in the storage event to determine the channel type.
Prerequisites
Ensure that you have:
- Integrated the Signaling SDK in your project.
- Implemented the framework functionality from the SDK quickstart page.
- Enabled storage in Storage configuration.
Implement channel metadata storage
The section shows you how to implement channel metadata storage in your Signaling app.
Set channel metadata
To create a new metadata item for the channel, or to update the value of an existing item, call setChannelMetadata. This method creates a new item in the channel metadata if the specified key does not exist, or overwrites the associated value if a metadata item with the specified key already exists.
The following example saves a set of metadata items for a message channel named channel1. Signaling adds timestamp and authorUid information to each metadata item it stores.
var properties = MetadataItem(
key: 'Quantity',
value: '20');
var announcement = MetadataItem(
key: 'Announcement',
value: 'Welcome to our shop!');
var price = MetadataItem(
key: 'T-shirt',
value: '200');
var metadata = [properties,announcement,price];
try{
var (status,response) = await rtmClient.getStorage.setChannelMetadata(
'channel1',
RtmChannelType.message,
metadata,
recordTs: true,
recordUserId: true);
if (status.error == true) {
print(status);
} else {
print(response);
}
} catch (e) {
print('something went wrong: $e');
}If the operation is successful, Signaling returns the following data structure:
{
channelName : 'channel1',
channelType : RtmChannelType.message,
}Signaling also triggers a storage event notification within 100 ms of type update to notify other users in the channel. For details, see event listeners.
Get channel metadata
To retrieve all metadata items associated with a specific channel, call getChannelMetadata by specifying the channel name and the channel type. Refer to the following example:
try{
var (status,response) = await rtmClient.getStorage.getChannelMetadata(
'channel1',
RtmChannelType.message);
if (status.error == true) {
print(status);
} else {
print(response);
}
} catch (e) {
print('something went wrong: $e');
}Signaling SDK returns the following data structure:
{
data:{
majorRevision: 734874892,
itemCount: 3,
items: [
{
key: 'Quantity',
value:'20',
authorUid: 'Tony',
revision: 734874888,
updateTs: 1688978391900
},{
key:'Announcement',
value:'Welcome to our Shop!',
authorUid:'Tony',
revision: 734874333,
updateTs: 1688978391800
},{
key: 'T-shirt',
value: '100',
authorUid: 'Tony',
revision: 734874222,
updateTs: 168897839100
}]
},
channelName: 'channel1',
channelType: RtmChannelType.message
}Update channel metadata
To modify existing metadata items for a specified channel, call updateChannelMetadata. If the metadata item does not exist, an error is returned. This method is useful for business use-cases that require permission control to create new metadata items. For example, consider the following use-cases:
-
In an e-commerce auction, only administrators or product owners are authorized to list new products and set new attributes, while bidders may only modify price attributes.
-
In a gaming environment, only the administrator may define permissions as room properties.
The following example updates the value of a metadata item:
var price = MetadataItem(
key: 'T-shirt',
value: '299');
var metadata = [price];
try{
var (status,response) = await rtmClient.getStorage.updateChannelMetadata(
'channel1',
RtmChannelType.message,
metadata,
recordTs: true,
recordUserId: true);
if (status.error == true) {
print(status);
} else {
print(response);
}
} catch (e) {
print('something went wrong: $e');
}If the update operation is successful, Signaling returns the following data structure:
{
channelName : 'channel1',
channelType : RtmChannelType.message,
}Signaling also triggers a storage event notification within 100 ms of type update to notify other users in the channel. For details, see event listeners.
Delete channel metadata
To delete metadata items that are no longer required, call removeChannelMetadata. Refer to the following sample code:
var announcement = MetadataItem(key: 'Announcement');
var metadata = [announcement];
try{
var (status,response) = await rtmClient.getStorage.removeChannelMetadata(
'channel1',
RtmChannelType.message,
metadata: metadata);
if (status.error == true) {
print(status);
} else {
print(response);
}
} catch (e) {
print('something went wrong: $e');
}Setting the value for a metadata item that is being deleted has no effect.
Signaling triggers a storage event notification of event type update within 100 ms to inform other users in the channel.
To delete the entire set of metadata for a channel, do not specify any metadata items when calling removeChannelMetadata. Refer to the following sample code:
try{
var (status,response) = await rtmClient.getStorage.removeChannelMetadata(
'channel1',
RtmChannelType.message);
if (status.error == true) {
print(status);
} else {
print(response);
}
} catch (e) {
print('something went wrong: $e');
}Once channel metadata is deleted, it cannot be recovered. If you need data restoration, back up the metadata before deleting it.
Receive storage event notifications
A storage event notification returns the StorageEvent data structure, which includes the RtmStorageEventType parameter.
To receive storage event notifications, implement a storage event listener. See event listeners for details. In addition, set the withMetadata parameter to true when subscribing to or joining a channel.
Event notification mode
Currently, Signaling only supports the full data update mode. This means that when user or channel metadata is updated, the data field in the event notification contains all the attribute data of the user or the channel.
Additional storage features
To help resolve issues arising from concurrent updates to storage, Signaling offers version control and locking features.
Version control
Signaling integrates compare-and-set (CAS) version control to manage metadata updates. CAS is a concurrency control mechanism to ensure that updates to a shared resource occur only if the resource is in an expected state. The mechanism works as follows:
- The client reads the current version of a data item.
- Before making an update, the client compares the current version with the last read version number.
- If the versions match, the client proceeds with the update and increments the version number. If they do not match, the update is aborted.
The following are some sample use-cases where CAS version control is useful:
-
In a bidding use-case, if multiple users bid on a product at the same time, the first bidder succeeds while others receive an error. Users obtain the latest price information to update their bids.
-
In a red envelope grabbing use-case, the red envelope may only be grabbed once. The first user succeeds, while the rest receive an error.
The CAS version control feature provides two independent version control parameters. Set one or more of these values according to the needs of your business use-case:
-
majorRevisionparameter in thesetMajorRevisionmethod: Enable version number verification of the entire set of channel metadata. -
revisionparameter of aMetadataItem: Enable version number verification of a single metadata item.
When setting channel metadata, or a single channel metadata item, use the revision attribute to enable or disable version control as follows:
-
To disable CAS verification, use the default value of
-1for therevisionparameter. -
To enable CAS verification, set the
majorRevisionor therevisionparameter to a positive integer. The SDK updates the corresponding value after successfully verifying the revision number. If the specified revision number does not match the latest revision number in the database, the SDK returns an error.
The following sample shows how to use majorRevision and revision to update channel metadata and a metadata item:
var properties = MetadataItem(
key: 'Quantity',
value: '20'
revision:734874888);
var announcement = MetadataItem(
key: 'Announcement',
value: 'Welcome to our shop!');
var price = MetadataItem(
key: 'T-shirt',
value: '200'
revision:734874222);
var metadata = [properties,announcement,price];
try{
var (status,response) = await rtmClient.getStorage.setChannelMetadata(
'channel1',
RtmChannelType.message,
metadata,
majorRevision: 734874892,
recordTs: true,
recordUserId: true);
if (status.error == true) {
print(status);
} else {
print(response);
}
} catch (e) {
print('something went wrong: $e');
}In this example, CAS verification for channel metadata and metadata items is enabled by setting majorRevision and revision parameters to positive integers. Upon receiving the update call request, Signaling first verifies the provided major revision number against the latest value in the database. If there's a mismatch, it returns an error; if the values match, Signaling verifies the revision number for each metadata item using a similar logic.
When using version control, monitor storage notifications to retrieve updated values for majorRevision and revision to ensure that the latest revision values are used for subsequent operations.
Locks
Locks enable users to gain exclusive access to critical resources, resolving contention issues with shared resources. For instance, consider a use-case where only one administrator is allowed in a channel at a time, and only the administrator can manage channel metadata by setting, deleting, and modifying it.
Compared to CAS, which controls the version of channel metadata, locks offer a higher level of control. They determine whether a user has the authority to call the setChannelMetadata, updateChannelMetadata, and removeChannelMetadata interfaces. Without acquiring the lock, a user cannot perform operations on channel metadata.
The following code demonstrates using a lock to update channel metadata. The user calling updateChannelMetadata must acquire the lock first for the call to succeed.
var properties = MetadataItem(
key: 'Quantity',
value: '20');
var announcement = MetadataItem(
key: 'Announcement',
value: 'Welcome to our shop!');
var price = MetadataItem(
key: 'T-shirt',
value: '200');
var metadata = [properties,announcement,price];
try{
var (status,response) = await rtmClient.getStorage.setChannelMetadata(
'channel1',
RtmChannelType.message,
metadata,
lockName:'manage',
recordTs: true,
recordUserId: true);
if (status.error == true) {
print(status);
} else {
print(response);
}
} catch (e) {
print('something went wrong: $e');
}For more information on setting, acquiring, releasing, revoking, and removing locks, see Locks.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
API reference
The Signaling storage service enables you to store and share contextual channel data in your app, such as props, announcements, member lists, and relationship chains. When channel metadata is set, updated, or deleted, the SDK triggers a storage event notification. Other users in the channel receive this notification within 100ms and use the information according to your business logic.
Understand the tech
Use channel metadata to store and share channel level information such as room attributes, group announcements, and auction item price updates. A set of channel metadata for a specific channel facilitates business-level data storage and real-time notifications. Each channel has only one set of channel metadata, but each set may contain multiple metadata items. For relevant restrictions, refer to the API usage restrictions. Each metadata item has key, value, and revision properties.
Channel metadata is stored permanently in the Signaling database. The data persists even after a channel is destroyed. You must explicitly delete it to remove it from the database. This feature impacts your storage billing. Refer to Pricing for details.
The storage service is available for both message channels and stream channels. Use the channelType parameter in the storage event to determine the channel type.
Prerequisites
Ensure that you have:
- Integrated the Signaling SDK in your project.
- Implemented the framework functionality from the SDK quickstart page.
- Enabled storage in Storage configuration.
Implement channel metadata storage
The section shows you how to implement channel metadata storage in your Signaling app.
Set channel metadata
To create a new metadata item for the channel, or to update the value of an existing item, call setChannelMetadata. This method creates a new item in the channel metadata if the specified key does not exist, or overwrites the associated value if a metadata item with the specified key already exists.
The following example saves a set of metadata items for a stream channel. Signaling adds timestamp and authorUid information to each metadata item it stores.
Metadata metadata;
std::vector<agora::rtm::MetadataItem> items;
MetadataItem item0("Quantity", "20");
MetadataItem item1("Announcement", "Welcome to our shop!");
MetadataItem item2("T-shirt", "100");
items.emplace_back(item0);
items.emplace_back(item1);
items.emplace_back(item2);
MetadataOptions options;
options.recordTs = true;
options.recordUserId = true;
uint64_t requestId;
rtmClient->getStorage()->setChannelMetadata("channel1", RTM_CHANNEL_TYPE_MESSAGE, metadata, options, "lockName", requestId);After you call this method, the SDK triggers the onSetChannelMetadataResult callback to return the call result.
// Asynchronous callback
class RtmEventHandler : public IRtmEventHandler {
void onSetChannelMetadataResult(const uint64_t requestId, const char *channelName, RTM_CHANNEL_TYPE channelType, RTM_ERROR_CODE errorCode) override {
if (errorCode != RTM_ERROR_OK) {
// set channel metadata failed
} else {
// set channel metadata success
}
}
};Additionally, Signaling triggers an onStorageEvent notification of event type RTM_STORAGE_EVENT_TYPE_UPDATE within 100 ms to inform other channel members.
Get channel metadata
To retrieve all metadata items associated with a specific channel, call getChannelMetadata by specifying the channel name and the channel type. Refer to the following example:
uint64_t requestId;
rtmClient->getStorage()->getChannelMetadata("channel1", RTM_CHANNEL_TYPE_MESSAGE, requestId);After you call this method, the SDK triggers the onGetChannelMetadataResult callback to return the call result.
// Asynchronous callback
class RtmEventHandler : public IRtmEventHandler {
void onGetChannelMetadataResult(const uint64_t requestId, const char *channelName, RTM_CHANNEL_TYPE channelType, const Metadata& data, RTM_ERROR_CODE errorCode) override {
if (errorCode != RTM_ERROR_OK) {
printf("GetChannelMetadata failed error is %d reason is %s\n", errorCode, getErrorReason(errorCode));
} else {
printf("GetChannelMetadata success %d channel: %s, channel type: %d\n");
for (int i = 0 ; i < data.itemCount; i++) {
printf("key: %s value: %s revision: %lld\n", data.items[i].key, data.items[i].value, data.items[i].revision);
}
}
}
};Signaling SDK returns the following data structure:
{
majorRevision: 734874892,
metadata: {
"Quantity": {
value: "20",
revision: 734874888,
updated: 1688978391900,
authorUid: "Tony"
},
"Announcement": {
value: "Welcome to our Shop!",
revision: 734874333,
updated: 1688978391800,
authorUid: "Tomas"
},
"T-shirt": {
value: "100",
revision: 734874222,
updated: 168897839100,
authorUid: "Adam"
}
}
}Update channel metadata
To modify existing metadata items for a specified channel, call updateChannelMetadata. If the metadata item does not exist, an error is returned. This method is useful for business use-cases that require permission control on creating new metadata items. For example, consider the following use-cases:
- In an e-commerce auction, only administrators or product owners are authorized to list new products and set new attributes, while bidders may only modify price attributes.
- In a gaming environment, only the administrator may define permissions as room properties.
The following example updates the value of a metadata item:
Metadata metadata;
std::vector<agora::rtm::MetadataItem> items;
items.push_back(MetadataItem("T-shirt", "299"));
metadata.items = items.data();
metadata.itemCount = items.size();
MetadataOptions options;
options.recordTs = true;
options.recordUserId = true;
uint64_t requestId;
rtmClient->getStorage()->updateChannelMetadata("channel1", RTM_CHANNEL_TYPE_MESSAGE, metadata, options, "lockName", requestId);After you call this method, the SDK triggers the onUpdateChannelMetadataResult callback to return the call result.
// Asynchronous callback
class RtmEventHandler : public IRtmEventHandler {
void onUpdateChannelMetadataResult(const uint64_t requestId, const char *channelName, RTM_CHANNEL_TYPE channelType, RTM_ERROR_CODE errorCode) override {
if (errorCode != RTM_ERROR_OK) {
printf("UpdateChannelMetadata failed error is %d reason is %s\n", errorCode, getErrorReason(errorCode));
} else {
printf("UpdateChannelMetadata success\n");
}
}
};Additionally, Signaling triggers an onStorageEvent notification of event type RTM_STORAGE_EVENT_TYPE_UPDATE within 100 ms to inform other channel members.
Delete channel metadata
To delete metadata items that are no longer required, call removeChannelMetadata. Refer to the following sample code:
Metadata metadata;
std::vector<agora::rtm::MetadataItem> items;
MetadataItem item;
item.key = "Announcement";
items.push_back(item);
metadata.items = items.data();
metadata.itemCount = items.size();
MetadataOptions options;
options.recordTs = true;
options.recordUserId = true;
uint64_t requestId;
rtmClient->getStorage()->removeChannelMetadata("channel1", RTM_CHANNEL_TYPE_MESSAGE, metadata, options, "lockName", requestId);Setting the value for a metadata item that is being deleted has no effect.
After you call this method, the SDK triggers the onRemoveChannelMetadataResult callback to return the call result.
// Asynchronous callback
class RtmEventHandler : public IRtmEventHandler {
void onRemoveChannelMetadataResult(const uint64_t requestId, const char *channelName, RTM_CHANNEL_TYPE channelType, RTM_ERROR_CODE errorCode) override {
if (errorCode != RTM_ERROR_OK) {
printf("RemoveChannelMetadata failed error is %d reason is %s\n", errorCode, getErrorReason(errorCode));
} else {
printf("RemoveChannelMetadata success\n");
}
}
};Additionally, Signaling triggers an onStorageEvent notification of event type RTM_STORAGE_EVENT_TYPE_UPDATE within 100 ms to inform other channel members.
To delete the entire set of metadata for a channel, do not specify any metadata items when calling removeChannelMetadata. Refer to the following sample code:
Metadata metadata;
uint64_t requestId;
rtmClient->getStorage()->removeChannelMetadata("channel1", RTM_CHANNEL_TYPE_MESSAGE, metadata, MetadataOptions(), "lockName", requestId);Once channel metadata is deleted, it cannot be recovered. If you need data restoration, back up the metadata before deleting it.
Receive storage event notifications
A storage event notification returns the StorageEvent data structure, which includes the RTM_STORAGE_EVENT_TYPE parameter.
To receive storage event notifications, implement an event listener. See event listeners for details. In addition, set the withMetadata parameter to true when subscribing to or joining a channel.
Event notification mode
Currently, Signaling only supports the full data update mode. This means that when user or channel metadata is updated, the data field in the event notification contains all the attribute data of the user or the channel.
Additional storage features
To help resolve issues arising from concurrent updates to storage, Signaling offers version control and locking features.
Version control
The Compare and Set (CAS) version control feature in channel metadata storage provides two independent version control parameters. Set one or more of these values according to the needs of your business use-case:
-
majorRevisionparameter in thesetMajorRevisionmethod: Enable version number verification of the entire set of channel metadata. -
revisionparameter of aMetadataItem: Enable version number verification of a single metadata item.
When setting channel metadata, or a single channel metadata item, use the revision attribute to enable or disable version control as follows:
-
To disable CAS verification, use the default value of
-1for therevisionparameter. If the channel metadata or channel metadata item already exists, the value is overwritten. If it does not exist, the SDK creates a metadata item and updates the value. -
To enable CAS verification, set the
majorRevisionor therevisionparameter to a positive integer. If the channel metadata or channel metadata item already exists, the SDK updates the corresponding value after successfully verifying the revision number. If the specified revision number does not match the latest revision number in the database, the SDK returns an error code.
The following sample shows how to use majorRevision and revision to update channel metadata and a metadata item:
Metadata metadata;
metadata.majorRevision = 734874892;
std::vector<MetadataItem> items;
items.emplace_back(MetadataItem("Quantity", "30", 734874888));
items.emplace_back(MetadataItem("Announcement", "Welcome to our shop!"));
items.emplace_back(MetadataItem("T-shirt", "101", 734874222));
metadata.items = items.data();
metadata.itemCount = items.size();
MetadataOptions options;
options.recordTs = true;
options.recordUserId = true;
uint64_t requestId;
rtmClient->getStorage()->updateChannelMetadata("channel1", RTM_CHANNEL_TYPE_MESSAGE, metadata, options, "lockName", requestId);In the above example, CAS verification for channel metadata and metadata items is enabled by setting majorRevision and revision parameters to positive integers. Upon receiving the update call request, Signaling first verifies the provided major revision number against the latest value in the database. If there's a mismatch, it returns an error; if the values match, Signaling verifies the revision number for each metadata item using a similar logic.
When using version control, monitor onStorageEvent notifications to retrieve updated values for majorRevision and revision to ensure that the latest revision values are used for subsequent operations.
Following are some sample use-cases where version control is useful:
-
In the bidding use-case, if multiple users bid on a product at the same time, the first bidder succeeds, while others receive an error. Users obtain the latest price information to update their bids.
-
In the red envelope grabbing use-case, the red envelope may only be grabbed once. The first user succeeds, while the rest receive an error.
Locks
Locks enable users to gain exclusive access to critical resources, resolving contention issues with shared resources. For instance, consider a use-case where only one administrator is allowed in a channel at a time, and only the administrator can manage channel metadata by setting, deleting, and modifying it.
Compared to CAS, which controls the version of channel metadata, locks offer a higher level of control. They determine whether a user has the authority to call the setChannelMetadata, updateChannelMetadata, and removeChannelMetadata interfaces. Without acquiring the lock, a user cannot perform operations on channel metadata.
The following code demonstrates using a lock to update channel metadata. The user calling updateChannelMetadata must acquire the lock first for the call to succeed.
Metadata metadata;
std::vector<MetadataItem> items;
items.emplace_back(MetadataItem("Quantity", "40"));
items.emplace_back(MetadataItem("Announcement", "Welcome to our shop!"));
items.emplace_back(MetadataItem("T-shirt", "300"));
metadata.items = items.data();
metadata.itemCount = items.size();
MetadataOptions options;
options.recordTs = true;
options.recordUserId = true;
uint64_t requestId;
std::string lockName = "manage";
rtm_client->getStorage()->updateChannelMetadata("channel1", RTM_CHANNEL_TYPE_MESSAGE, metadata, options, lockName, requestId);For more information on setting, acquiring, releasing, revoking, and removing locks, see Locks.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
API reference
The Signaling storage service enables you to store and share contextual channel data in your app, such as props, announcements, member lists, and relationship chains. When channel metadata is set, updated, or deleted, the SDK triggers a storage event notification. Other users in the channel receive this notification within 100ms and use the information according to your business logic.
Understand the tech
Use channel metadata to store and share channel level information such as room attributes, group announcements, and auction item price updates. A set of channel metadata for a specific channel facilitates business-level data storage and real-time notifications. Each channel has only one set of channel metadata, but each set may contain multiple metadata items. For relevant restrictions, refer to the API usage restrictions. Each metadata item has key, value, and revision properties.
Channel metadata is stored permanently in the Signaling database. The data persists even after a channel is destroyed. You must explicitly delete it to remove it from the database. This feature impacts your storage billing. Refer to Pricing for details.
The storage service is available for both message channels and stream channels. Use the channelType parameter in the storage event to determine the channel type.
Prerequisites
Ensure that you have:
- Integrated the Signaling SDK in your project.
- Implemented the framework functionality from the SDK quickstart page.
- Enabled storage in Storage configuration.
Implement channel metadata storage
The section shows you how to implement channel metadata storage in your Signaling app.
Set channel metadata
To create a new metadata item for the channel, or to update the value of an existing item, call setChannelMetadata. This method creates a new item in the channel metadata if the specified key does not exist, or overwrites the associated value if a metadata item with the specified key already exists.
The following example saves a set of metadata items for a stream channel. Signaling adds timestamp and authorUid information to each metadata item it stores.
Metadata metadata;
std::vector<agora::rtm::MetadataItem> items;
MetadataItem item0("Quantity", "20");
MetadataItem item1("Announcement", "Welcome to our shop!");
MetadataItem item2("T-shirt", "100");
items.emplace_back(item0);
items.emplace_back(item1);
items.emplace_back(item2);
MetadataOptions options;
options.recordTs = true;
options.recordUserId = true;
uint64_t requestId;
rtmClient->getStorage()->setChannelMetadata("channel1", RTM_CHANNEL_TYPE_MESSAGE, metadata, options, "lockName", requestId);After you call this method, the SDK triggers the onSetChannelMetadataResult callback to return the call result.
// Asynchronous callback
class RtmEventHandler : public IRtmEventHandler {
void onSetChannelMetadataResult(const uint64_t requestId, const char *channelName, RTM_CHANNEL_TYPE channelType, RTM_ERROR_CODE errorCode) override {
if (errorCode != RTM_ERROR_OK) {
// set channel metadata failed
} else {
// set channel metadata success
}
}
};Additionally, Signaling triggers an onStorageEvent notification of event type RTM_STORAGE_EVENT_TYPE_UPDATE within 100 ms to inform other channel members.
Get channel metadata
To retrieve all metadata items associated with a specific channel, call getChannelMetadata by specifying the channel name and the channel type. Refer to the following example:
uint64_t requestId;
rtmClient->getStorage()->getChannelMetadata("channel1", RTM_CHANNEL_TYPE_MESSAGE, requestId);After you call this method, the SDK triggers the onGetChannelMetadataResult callback to return the call result.
// Asynchronous callback
class RtmEventHandler : public IRtmEventHandler {
void onGetChannelMetadataResult(const uint64_t requestId, const char *channelName, RTM_CHANNEL_TYPE channelType, const Metadata& data, RTM_ERROR_CODE errorCode) override {
if (errorCode != RTM_ERROR_OK) {
printf("GetChannelMetadata failed error is %d reason is %s\n", errorCode, getErrorReason(errorCode));
} else {
printf("GetChannelMetadata success %d channel: %s, channel type: %d\n");
for (int i = 0 ; i < data.itemCount; i++) {
printf("key: %s value: %s revision: %lld\n", data.items[i].key, data.items[i].value, data.items[i].revision);
}
}
}
};Signaling SDK returns the following data structure:
{
majorRevision: 734874892,
metadata: {
"Quantity": {
value: "20",
revision: 734874888,
updated: 1688978391900,
authorUid: "Tony"
},
"Announcement": {
value: "Welcome to our Shop!",
revision: 734874333,
updated: 1688978391800,
authorUid: "Tomas"
},
"T-shirt": {
value: "100",
revision: 734874222,
updated: 168897839100,
authorUid: "Adam"
}
}
}Update channel metadata
To modify existing metadata items for a specified channel, call updateChannelMetadata. If the metadata item does not exist, an error is returned. This method is useful for business use-cases that require permission control on creating new metadata items. For example, consider the following use-cases:
- In an e-commerce auction, only administrators or product owners are authorized to list new products and set new attributes, while bidders may only modify price attributes.
- In a gaming environment, only the administrator may define permissions as room properties.
The following example updates the value of a metadata item:
Metadata metadata;
std::vector<agora::rtm::MetadataItem> items;
items.push_back(MetadataItem("T-shirt", "299"));
metadata.items = items.data();
metadata.itemCount = items.size();
MetadataOptions options;
options.recordTs = true;
options.recordUserId = true;
uint64_t requestId;
rtmClient->getStorage()->updateChannelMetadata("channel1", RTM_CHANNEL_TYPE_MESSAGE, metadata, options, "lockName", requestId);After you call this method, the SDK triggers the onUpdateChannelMetadataResult callback to return the call result.
// Asynchronous callback
class RtmEventHandler : public IRtmEventHandler {
void onUpdateChannelMetadataResult(const uint64_t requestId, const char *channelName, RTM_CHANNEL_TYPE channelType, RTM_ERROR_CODE errorCode) override {
if (errorCode != RTM_ERROR_OK) {
printf("UpdateChannelMetadata failed error is %d reason is %s\n", errorCode, getErrorReason(errorCode));
} else {
printf("UpdateChannelMetadata success\n");
}
}
};Additionally, Signaling triggers an onStorageEvent notification of event type RTM_STORAGE_EVENT_TYPE_UPDATE within 100 ms to inform other channel members.
Delete channel metadata
To delete metadata items that are no longer required, call removeChannelMetadata. Refer to the following sample code:
Metadata metadata;
std::vector<agora::rtm::MetadataItem> items;
MetadataItem item;
item.key = "Announcement";
items.push_back(item);
metadata.items = items.data();
metadata.itemCount = items.size();
MetadataOptions options;
options.recordTs = true;
options.recordUserId = true;
uint64_t requestId;
rtmClient->getStorage()->removeChannelMetadata("channel1", RTM_CHANNEL_TYPE_MESSAGE, metadata, options, "lockName", requestId);Setting the value for a metadata item that is being deleted has no effect.
After you call this method, the SDK triggers the onRemoveChannelMetadataResult callback to return the call result.
// Asynchronous callback
class RtmEventHandler : public IRtmEventHandler {
void onRemoveChannelMetadataResult(const uint64_t requestId, const char *channelName, RTM_CHANNEL_TYPE channelType, RTM_ERROR_CODE errorCode) override {
if (errorCode != RTM_ERROR_OK) {
printf("RemoveChannelMetadata failed error is %d reason is %s\n", errorCode, getErrorReason(errorCode));
} else {
printf("RemoveChannelMetadata success\n");
}
}
};Additionally, Signaling triggers an onStorageEvent notification of event type RTM_STORAGE_EVENT_TYPE_UPDATE within 100 ms to inform other channel members.
To delete the entire set of metadata for a channel, do not specify any metadata items when calling removeChannelMetadata. Refer to the following sample code:
Metadata metadata;
uint64_t requestId;
rtmClient->getStorage()->removeChannelMetadata("channel1", RTM_CHANNEL_TYPE_MESSAGE, metadata, MetadataOptions(), "lockName", requestId);Once channel metadata is deleted, it cannot be recovered. If you need data restoration, back up the metadata before deleting it.
Receive storage event notifications
A storage event notification returns the StorageEvent data structure, which includes the RTM_STORAGE_EVENT_TYPE parameter.
To receive storage event notifications, implement an event listener. See event listeners for details. In addition, set the withMetadata parameter to true when subscribing to or joining a channel.
Event notification mode
Currently, Signaling only supports the full data update mode. This means that when user or channel metadata is updated, the data field in the event notification contains all the attribute data of the user or the channel.
Additional storage features
To help resolve issues arising from concurrent updates to storage, Signaling offers version control and locking features.
Version control
The Compare and Set (CAS) version control feature in channel metadata storage provides two independent version control parameters. Set one or more of these values according to the needs of your business use-case:
-
majorRevisionparameter in thesetMajorRevisionmethod: Enable version number verification of the entire set of channel metadata. -
revisionparameter of aMetadataItem: Enable version number verification of a single metadata item.
When setting channel metadata, or a single channel metadata item, use the revision attribute to enable or disable version control as follows:
-
To disable CAS verification, use the default value of
-1for therevisionparameter. If the channel metadata or channel metadata item already exists, the value is overwritten. If it does not exist, the SDK creates a metadata item and updates the value. -
To enable CAS verification, set the
majorRevisionor therevisionparameter to a positive integer. If the channel metadata or channel metadata item already exists, the SDK updates the corresponding value after successfully verifying the revision number. If the specified revision number does not match the latest revision number in the database, the SDK returns an error code.
The following sample shows how to use majorRevision and revision to update channel metadata and a metadata item:
Metadata metadata;
metadata.majorRevision = 734874892;
std::vector<MetadataItem> items;
items.emplace_back(MetadataItem("Quantity", "30", 734874888));
items.emplace_back(MetadataItem("Announcement", "Welcome to our shop!"));
items.emplace_back(MetadataItem("T-shirt", "101", 734874222));
metadata.items = items.data();
metadata.itemCount = items.size();
MetadataOptions options;
options.recordTs = true;
options.recordUserId = true;
uint64_t requestId;
rtmClient->getStorage()->updateChannelMetadata("channel1", RTM_CHANNEL_TYPE_MESSAGE, metadata, options, "lockName", requestId);In the above example, CAS verification for channel metadata and metadata items is enabled by setting majorRevision and revision parameters to positive integers. Upon receiving the update call request, Signaling first verifies the provided major revision number against the latest value in the database. If there's a mismatch, it returns an error; if the values match, Signaling verifies the revision number for each metadata item using a similar logic.
When using version control, monitor onStorageEvent notifications to retrieve updated values for majorRevision and revision to ensure that the latest revision values are used for subsequent operations.
Following are some sample use-cases where version control is useful:
-
In the bidding use-case, if multiple users bid on a product at the same time, the first bidder succeeds, while others receive an error. Users obtain the latest price information to update their bids.
-
In the red envelope grabbing use-case, the red envelope may only be grabbed once. The first user succeeds, while the rest receive an error.
Locks
Locks enable users to gain exclusive access to critical resources, resolving contention issues with shared resources. For instance, consider a use-case where only one administrator is allowed in a channel at a time, and only the administrator can manage channel metadata by setting, deleting, and modifying it.
Compared to CAS, which controls the version of channel metadata, locks offer a higher level of control. They determine whether a user has the authority to call the setChannelMetadata, updateChannelMetadata, and removeChannelMetadata interfaces. Without acquiring the lock, a user cannot perform operations on channel metadata.
The following code demonstrates using a lock to update channel metadata. The user calling updateChannelMetadata must acquire the lock first for the call to succeed.
Metadata metadata;
std::vector<MetadataItem> items;
items.emplace_back(MetadataItem("Quantity", "40"));
items.emplace_back(MetadataItem("Announcement", "Welcome to our shop!"));
items.emplace_back(MetadataItem("T-shirt", "300"));
metadata.items = items.data();
metadata.itemCount = items.size();
MetadataOptions options;
options.recordTs = true;
options.recordUserId = true;
uint64_t requestId;
std::string lockName = "manage";
rtm_client->getStorage()->updateChannelMetadata("channel1", RTM_CHANNEL_TYPE_MESSAGE, metadata, options, lockName, requestId);For more information on setting, acquiring, releasing, revoking, and removing locks, see Locks.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
API reference
The Signaling storage service enables you to store and share contextual channel data in your app, such as props, announcements, member lists, and relationship chains. When channel metadata is set, updated, or deleted, the SDK triggers a storage event notification. Other users in the channel receive this notification within 100ms and use the information according to your business logic.
Understand the tech
Use channel metadata to store and share channel level information such as room attributes, group announcements, and auction item price updates. A set of channel metadata for a specific channel facilitates business-level data storage and real-time notifications. Each channel has only one set of channel metadata, but each set may contain multiple metadata items. For relevant restrictions, refer to the API usage restrictions. Each metadata item has key, value, and revision properties.
Channel metadata is stored permanently in the Signaling database. The data persists even after a channel is destroyed. You must explicitly delete it to remove it from the database. This feature impacts your storage billing. Refer to Pricing for details.
The storage service is available for both message channels and stream channels. Use the channelType parameter in the storage event to determine the channel type.
Prerequisites
Ensure that you have:
- Integrated the Signaling SDK in your project.
- Implemented the framework functionality from the SDK quickstart page.
- Enabled storage in Storage configuration.
Implement channel metadata storage
The section shows you how to implement channel metadata storage in your Signaling app.
Set channel metadata
To create a new metadata item for the channel, or to update the value of an existing item, call SetChannelMetadataAsync. This method creates a new item in the channel metadata if the specified key does not exist, or overwrites the associated value if a metadata item with the specified key already exists.
The following example saves a set of metadata items for a message channel named channel1. Signaling adds timestamp and authorUid information to each metadata item it stores.
var metadata = new RtmMetadata();
var apple = new MetadataItem()
{
key = "Apple",
value = "100",
};
var banana = new MetadataItem()
{
key = "Banana",
value = "200",
};
metadata.metadataItems = new MetadataItem[] { apple, banana };
metadata.metadataItemsSize = 2;
var metadataOptions = new MetadataOptions()
{
recordUserId = true,
recordTs = true
};
var lockName = "";
var (status,response) = await rtmClient.GetStorage().SetChannelMetadataAsync("channel1", RTM_CHANNEL_TYPE.MESSAGE, metadata, metadataOptions, lockName);
if (status.Error)
{
Debug.Log(string.Format("{0} is failed, ErrorCode: {1}, due to: {2}", status.Operation, status.ErrorCode, status.Reason));
}
else
{
Debug.Log(string.Format("Set Channel :{0} metadata success! Channel Type is :{1}! ", response.ChannelName, response.ChannelType));
}Signaling triggers an OnStorageEvent notification of event type UPDATE within 100 ms to inform other channel members.
Get channel metadata
To retrieve all metadata items associated with a specific channel, call GetChannelMetadataAsync by specifying the channel name and the channel type. Refer to the following example:
var (status,respones) = await rtmClient.GetStorage().GetChannelMetadataAsync("channel_name", RTM_CHANNEL_TYPE.MESSAGE);
if (status.Error)
{
Debug.Log(string.Format("{0} is failed, ErrorCode: {1}, due to: {2}", status.Operation, status.ErrorCode, status.Reason));
}
else
{
Debug.Log(string.Format("Get Channel :{0} metadata success! Channel Type is :{1}! ", response.ChannelName, response.ChannelType));
var data = response.data;
if (data.metadataItemsSize != 0)
{
Debug.Log(string.Format("Channel Metadata Major Revision is :{0} ! ", data.majorRevision));
for ( int i =0; i < data.metadataItemsSize; i++)
{
Debug.Log(string.Format("The {0}'th iterms Key is:{1}, Value is {2} ! ", i, data.metadataItems[i].key, data.metadataItems[i].value));
}
}
}Signaling SDK returns the following data structure:
{
majorRevision: 734874892,
metadata:{
{ key:"Quantity",
value:"20",
revision:734874888,
updateTs:1688978391900,
authorUid:"Tony"
},
{
key:"Announcement",
value:"Welcome to our Shop!",
revision:734874333,
updated:1688978391800,
authorUid:"Tomas"
},
{
key:"T-shirt",
value:"100",
revision:734874222,
updated:168897839100,
authorUid:"Adam"
}
}
}Update channel metadata
To modify existing metadata items for a specified channel, call UpdateChannelMetadataAsync. If the metadata item does not exist, an error is returned. This method is useful for business use-cases that require permission control on creating new metadata items. For example, consider the following use-cases:
- In an e-commerce auction, only administrators or product owners are authorized to list new products and set new attributes, while bidders may only modify price attributes.
- In a gaming environment, only the administrator may define permissions as room properties.
The following example updates the value of a metadata item:
var metadata = new RtmMetadata();
var apple = new MetadataItem()
{
key = "Apple",
value = "120",
};
var banana = new MetadataItem()
{
key = "Banana",
value = "220",
};
metadata.metadataItems = new MetadataItem[] { apple, banana };
metadata.metadataItemsSize = 2;
var options = new MetadataOptions()
{
recordUserId = true,
recordTs = true
};
var lockName = "";
var (status, response) = await rtmClient.GetStorage().UpdateChannelMetadataAsync(
"channel_name",
RTM_CHANNEL_TYPE.MESSAGE,
metadata,
options,
lockName
);
if (status.Error)
{
Debug.LogError(string.Format("{0} failed, ErrorCode: {1}, due to: {2}", status.Operation, status.ErrorCode, status.Reason));
}
else
{
Debug.Log(string.Format("Update Channel :{0} metadata success! Channel Type is :{1}! ", response.ChannelName, response.ChannelType));
}Signaling triggers an OnStorageEvent notification of event type UPDATE within 100 ms to inform other channel members.
Delete channel metadata
To delete metadata items that are no longer required, call RemoveChannelMetadataAsync. Refer to the following sample code:
var metadata = new RtmMetadata();
var metadataItem = new MetadataItem()
{
key = "Apple",
};
metadata.metadataItems = new MetadataItem[] { metadataItem };
metadata.metadataItemsSize = 1;
var options = new MetadataOptions() { };
var (status,response) = await rtmClient.GetStorage().RemoveChannelMetadataAsync("channel_name", RTM_CHANNEL_TYPE.MESSAGE, options, "");
if (status.Error)
{
Debug.Log(string.Format("{0} is failed, ErrorCode: {1}, due to: {2}", status.Operation, status.ErrorCode, status.Reason));
}
else
{
Debug.Log(string.Format("Remove Channel :{0} metadata success! Channel Type is :{1}! ", response.ChannelName, response.ChannelType));
}Setting the value for a metadata item that is being deleted has no effect.
Signaling triggers an OnStorageEvent notification of event type UPDATE within 100 ms to inform other channel members.
To delete the entire set of metadata for a channel, do not specify any metadata items when calling RemoveChannelMetadataAsync. Refer to the following sample code:
var metadata = new RtmMetadata();
var options = new MetadataOptions() { };
var (status,response) = await rtmClient.GetStorage().RemoveChannelMetadataAsync("channel_name", RTM_CHANNEL_TYPE.MESSAGE, options,"");
if (status.Error)
{
Debug.Log(string.Format("{0} is failed, ErrorCode: {1}, due to: {2}", status.Operation, status.ErrorCode, status.Reason));
}
else
{
Debug.Log(string.Format("Remove Channel :{0} metadata success! Channel Type is :{1}! ", response.ChannelName, response.ChannelType));
}Once channel metadata is deleted, it cannot be recovered. If you need data restoration, back up the metadata before deleting it.
Receive storage event notifications
A storage event notification returns the StorageEvent data structure, which includes the RTM_STORAGE_EVENT_TYPE parameter.
To receive storage event notifications, implement an event listener. See event listeners for details. In addition, set the withMetadata parameter to true when subscribing to or joining a channel.
Event notification mode
Currently, Signaling only supports the full data update mode. This means that when user or channel metadata is updated, the data field in the event notification contains all the attribute data of the user or the channel.
Additional storage features
To help resolve issues arising from concurrent updates to storage, Signaling offers version control and locking features.
Version control
The Compare and Set (CAS) version control feature in channel metadata storage provides two independent version control parameters. Set one or more of these values according to the needs of your business use-case:
-
majorRevisionproperty in theRtmMetadataclass: Enable version number verification of the entire set of channel metadata. -
revisionproperty of aMetadataItem: Enable version number verification of a single metadata item.
When setting channel metadata, or a single channel metadata item, use the revision attribute to enable or disable version control as follows:
-
To disable CAS verification, use the default value of
-1for therevisionparameter. If the channel metadata or channel metadata item already exists, the value is overwritten. If it does not exist, the SDK creates a metadata item and updates the value. -
To enable CAS verification, set the
majorRevisionor therevisionparameter to a positive integer. If the channel metadata or channel metadata item already exists, the SDK updates the corresponding value after successfully verifying the revision number. If the specified revision number does not match the latest revision number in the database, the SDK returns an error code.
The following sample shows how to use majorRevision and revision to update channel metadata and a metadata item:
var metadata = new RtmMetadata();
metadata.majorRevision = 174298270 ;
var apple = new MetadataItem()
{
key = "Apple",
value = "120",
revision = 174298200,
};
var banana = new MetadataItem()
{
key = "Banana",
value = "220",
revision = 174298100,
};
metadata.metadataItems = new MetadataItem[] { apple, banana };
metadata.metadataItemsSize = 2;
var options = new MetadataOptions()
{
recordUserId = true,
recordTs = true
};
var lockName = "lockName";
var (status,response) = await rtmClient.GetStorage().UpdateChannelMetadataAsync("channel_name", RTM_CHANNEL_TYPE.MESSAGE, data, options, lockName);
if (status.Error)
{
Debug.Log(string.Format("{0} is failed, ErrorCode: {1}, due to: {2}", status.Operation, status.ErrorCode, status.Reason));
}
else
{
Debug.Log(string.Format("Remove Channel :{0} metadata success! Channel Type is :{1}! ", response.ChannelName, response.ChannelType));
}In the above example, CAS verification for channel metadata and metadata items is enabled by setting majorRevision and revision parameters to positive integers. Upon receiving the update call request, Signaling first verifies the provided major revision number against the latest value in the database. If there's a mismatch, it returns an error; if the values match, Signaling verifies the revision number for each metadata item using a similar logic.
When using version control, monitor OnStorageEvent notifications to retrieve updated values for majorRevision and revision to ensure that the latest revision values are used for subsequent operations.
Following are some sample use-cases where version control is useful:
-
In the bidding use-case, if multiple users bid on a product at the same time, the first bidder succeeds, while others receive an error. Users obtain the latest price information to update their bids.
-
In the red envelope grabbing use-case, the red envelope may only be grabbed once. The first user succeeds, while the rest receive an error.
Locks
Locks enable users to gain exclusive access to critical resources, resolving contention issues with shared resources. For instance, consider a use-case where only one administrator is allowed in a channel at a time, and only the administrator can manage channel metadata by setting, deleting, and modifying it.
Compared to CAS, which controls the version of channel metadata, locks offer a higher level of control. They determine whether a user has the authority to call the SetChannelMetadataAsync, UpdateChannelMetadataAsync, and RemoveChannelMetadataAsync interfaces. Without acquiring the lock, a user cannot perform operations on channel metadata.
The following code demonstrates using a lock to update channel metadata. The user calling UpdateChannelMetadataAsync must acquire the lock first for the call to succeed.
var metadata = new RtmMetadata();
var apple = new MetadataItem()
{
key = "Apple",
value = "120",
};
var banana = new MetadataItem()
{
key = "Banana",
value = "220",
};
metadata.metadataItems = new MetadataItem[] { apple, banana };
metadata.metadataItemsSize = 2;
var options = new MetadataOptions()
{
recordUserId = true,
recordTs = true
};
var lockName = "lockName";
var (status, response) = await rtmClient.GetStorage().UpdateChannelMetadataAsync(
"channel_name",
RTM_CHANNEL_TYPE.MESSAGE,
metadata,
options,
lockName
);
if (status.Error)
{
Debug.LogError(string.Format("{0} failed, ErrorCode: {1}, due to: {2}", status.Operation, status.ErrorCode, status.Reason));
}
else
{
Debug.Log(string.Format("Update Channel :{0} metadata success! Channel Type is :{1}! ", response.ChannelName, response.ChannelType));
}For more information on setting, acquiring, releasing, revoking, and removing locks, see Locks.
Reference
This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.
