What should I use as the document key to maintain idempotency?
I'm building a text messaging application that uses CouchDB (with PouchDB on the client) to store messages locally. Twilio (SMS provider) generates an ID for each message, and I use that as the CouchDB document ID. This way fetching messages from Twilio's API is idempotent -- if I come across the same message twice, it will only store one copy in my database.
// twilio API /messages
[
{smsid: 123, body: 'foo'},
{smsid: 456, body: 'bar'}
]
// transformed into couchdb docs
[
{id: 123, doc: {_id: 123, body: 'foo'}},
{id: 456, doc: {_id: 456, body: 'bar'}}
]
This is easy to do when fetching messages from twilio. But when the user sends an outbound message from the client application, there is no twilio ID yet because it hasn't been sent to twilio yet.
A traditional approach would involve POSTing the message to some endpoint on my server, and have the server send it to twilio, then add the record to the database once it has the smsid from twilio's response. The problem with this is (a) there's a noticeable delay from when the user presses "send" and when the message shows up in the UI, and (b) we can't take advantage of couchdb's auth system.
Instead, I have it setup so the client generates a random ID, and inserts it into the database (via pouchdb w/sync). The server then watches for new outbound records added and dispatches them to twilio.
This approach works fine, but if I GET /messages again, it's no longer idempotent -- it would create an additional record for the outbound message because I don't have a couchdb document with that message's smsid as its key (it didn't have an smsid when it was added to couchdb).
Is there a way around this or a better approach?
An idea to make this work is that you must rely on other data from each message, and ignore Twilio's smsid.
Perhaps hashing together the user id, the message body and an abrangent version of the timestamp (for example, int(UNIX-TIMESTAMP-IN-SECONDS/100) will tolerate a delay of 100 seconds between the time your server gets the message and Twilio acknowledges it).
Thanks for your replies. This was a tough one. #rnewson from #couchdb in freenode was kind enough to spend some time thinking about this one and proposed a solution that worked out great:
Message documents in couchdb use an arbitrary _id that can be generated by the server or the client
When the client sends a message, it generates an arbitrary _id and puts it into the database. The server observes this and dispatches it to twilio, then updates the database document by adding a twilio_id property to the document
I created a view to index the documents by twilio_id
When the server starts, it fetches the latest messages from twilio. In order to prevent adding duplicate records to the database, it queries the above view for each twilio id. For each match, it uses the match's _id and _rev to perform an update. For records with no matches, it generates a new arbitrary _id to perform an insert.
For anyone curious, here's the code.
Thanks again for your responses!
Related
Is there a way to track delivery report of a particular message that have been sent via FCM to android device, i found we can add delivery_receipt_requested to track delivery and i have added that my json data as follows,
{"to":"KEY",
"data":{
"data":{
"title":"test message",
"message":"sent",
"image":null}
},
"notification":{
"delivery_receipt_requested":true
}
}
and i receive a response
{"multicast_id":6417448921485349071,"success":1,"failure":0,"canonical_ids":0,"results":[{"error":"false"}]}
In php or javascript i need something like if we pass that multicast_id need to get the current status of the text. I found it was almost nightmare to get the desired result, but its not impossible is there anyway guys?
There is currently no way to manually ask the FCM server about the status of the sent message.
Based from your post, it seems you already did your homework on checking the FCM service. Implementing the delivery receipts is the only way (AFAIK) that you could attain the behavior you mentioned in your post.
Implementing the delivery receipt not only needs the delivery_receipt_requested parameter in your payload, you have to implement an XMPP server protocol as well. Along with the Upstream Messaging part on your client app (for the acknowledgement part).
i have users Uid in an 'users' array as ['uid1','uid2'] now i will be sending notifications to these users in cloud function?
exports.sendNotificationFromCr = functions.database.ref('/cr/{crUid}/notifications/{notificationid}/').onWrite(event => {
const uid = ['uid1','uid2']; // some how i get this.
// some work to send notifications
// to all tokens of uid1 and uid2.
}
here is the database structure:
users/
uid1/
name:{name}
FCM-key/
token1:true
token2:true
uid2/
...
FCM-key/
token3:true
using ['uid1','uid2'] i want to send notification to all 3 tokens in my database. how to do that?
If you're using something like firebase. Then you would want to have a notifications database model, that has the userId, the notification title, body and perhaps image, also a seen flag (true or false).
You would then post notifications either from your clients or from your cloud server code into the database. One per client/notification. If you have thousands of users you would use some sort of server-side cronjob, to offload this so that it runs outside of say your client to server API.
On the clients, you would be listening for new rows in that model filtering on the userId and when they appear, display them to the client in your UI. Once the client has seen the notification you would mark it as seen on the client.
Without knowing what platforms, code base, DB you are using it's impossible to explain in code terms how this would be done.
There are various API's for IOS and Android and Firebase that resolve this.
I'm building an app and would like some feedback on my approach to building the data sync process and API that supports it. For context, these are the guiding principles for my app/API:
Free: I do not want to charge people at all to use the app/API.
Open source: the source code for both the app and API are available to the public to use as they wish.
Decentralised: the API service that supports the app can be run by anyone on any server, and made available for use to users of the app.
Anonymous: the user should not have to sign up for the service, or submit any personal identifying information that will be stored alongside their data.
Secure: the user's data should be encrypted before being sent to the server, anyone with access to the server should have no ability to read the user's data.
I will implement an instance of the API on a public server which will be selected in the app by default. That way initial users of the app can sync their data straight away without needing to find or set up an instance of the API service. Over time, if the app is popular then users will hopefully set up other instances of the API service either for themselves or to make available to other users of the app should they wish to use a different instance (or if the primary instance runs out of space, goes down, etc). They may even access the API in their own apps. Essentially, I want them to be able to have the choice to be self sufficient and not have to necessarily rely on other's providing an instance on the service for them, for reasons of privacy, resilience, cost-saving, etc. Note: the data in question is not sensitive (i.e. financial, etc), but it is personal.
The user's sync journey works like this:
User downloads the app, and creates their data in the process of using the app.
When the user is ready to initially sync, they enter a "password" in the password field, which is used to create a complex key with which to encrypt their data. Their password is stored locally in plain text but is never sent to the server.
User clicks the "Sync" button, their data is encrypted (using their password) and sent to the specified (or default) API instance and responds by giving them a unique ID which is saved by the app.
For future syncs, their data is encrypted locally using their saved password before being sent to the API along with their unique ID which updates their synced data on the server.
When retrieving synced data, their unique ID is sent to the API which responds with their encrypted data. Their locally stored password is then used to decrypt the data for use by the app.
I've implemented the app in javascript, and the API in Node.js (restify) with MongoDB as a backend, so in practice a sync requests to the server looks like this:
1. Initial sync
POST /api/data
Post body:
{
"data":"DWCx6wR9ggPqPRrhU4O4oLN5P09onApoAULX4Xt+ckxswtFNH/QQ+Y/RgxdU+8+8/muo4jo/jKnHssSezvjq6aPvYK+EAzAoRmXenAgUwHOjbiAXFqF8gScbbuLRlF0MsTKn/puIyFnvJd..."
}
Response:
{
"id":"507f191e810c19729de860ea",
"lastUpdated":"2016-07-06T12:43:16.866Z"
}
2. Get sync data
GET /api/data/507f191e810c19729de860ea
Response:
{
"data":"DWCx6wR9ggPqPRrhU4O4oLN5P09onApoAULX4Xt+ckxswtFNH/QQ+Y/RgxdU+8+8/muo4jo/jKnHssSezvjq6aPvYK+EAzAoRmXenAgUwHOjbiAXFqF8gScbbuLRlF0MsTKn/puIyFnvJd...",
"lastUpdated":"2016-07-06T12:43:16.866Z"
}
3. Update synced data
POST /api/data/507f191e810c19729de860ea
Post body:
{
"data":"DWCx6wR9ggPqPRrhU4O4oLN5P09onApoAULX4Xt+ckxswtFNH/QQ+Y/RgxdU+8+8/muo4jo/jKnHssSezvjq6aPvYK+EAzAoRmXenAgUwHOjbiAXFqF8gScbbuLRlF0MsTKn/puIyFnvJd..."
}
Response:
{
"lastUpdated":"2016-07-06T13:21:23.837Z"
}
Their data in MongoDB will look like this:
{
"id":"507f191e810c19729de860ea",
"data":"DWCx6wR9ggPqPRrhU4O4oLN5P09onApoAULX4Xt+ckxswtFNH/QQ+Y/RgxdU+8+8/muo4jo/jKnHssSezvjq6aPvYK+EAzAoRmXenAgUwHOjbiAXFqF8gScbbuLRlF0MsTKn/puIyFnvJd...",
"lastUpdated":"2016-07-06T13:21:23.837Z"
}
Encryption is currently implemented using CryptoJS's AES implementation. As the app provides the user's password as a passphrase to the AES "encrypt" function, it generates a 256-bit key which which to encrypt the user's data, before being sent to the API.
That about sums up the sync process, it's fairly simple but obviously it needs to be secure and reliable. My concerns are:
As the MongoDB ObjectID is fairly easy to guess, it is possible that a malicious user could request someone else's data (as per step 2. Get sync data) by guessing their ID. However, if they are successful they will only retrieve encrypted data and will not have the key with which to decrypt it. The same applies for anyone who has access to the database on the server.
Given the above, is the CryptoJS AES implementation secure enough so that in the real possibility that a user's encrypted data is retrieved by a malicious user, they will not realistically be able to decrypt the data?
Since the API is open to anyone and doesn't audit or check the submitted data, anyone could potentially submit any data they wish to be stored in the service, for example:
Post body:
{
"data":"This is my anyold data..."
}
Is there anything practical I can do to guard against this whilst adhering to the guiding principles above?
General abuse of the service such as users spamming initial syncs (step 1 above) over and over to fill up the space on the server; or some user's using disproportionately large amounts of server space. I've implemented some features to guard against this, such as logging IPs for initial syncs for one day (not kept any longer than that) in order to limit a single IP to a set number of initial syncs per day. Also I'm limiting the post body size for syncs. These options are configurable in the API however, so if a user doesn't like these limitations on a public API instance, they can host their own instance and tweak the settings to their liking.
So that's it, I would appreciate anyone who has any thoughts or feedback regarding this approach given my guiding principles. I couldn't find any examples where other apps have attempted a similar approach, so if anyone knows of any and can link to them I'd be grateful.
I can't really comment on whether specific AES algorithms/keys are secure or not, but assuming they are (and the keys are generated properly), it should not be a problem if other users can access the encrypted data.
You can maybe protect against abuse, without requiring other accounts, by using captchas or similar guards against automatic usage. If you require a catcha on new accounts, and set limits to all accounts on data volume and call frequency, you should be ok.
To guard against accidental clear-text data, you might generate a secondary key for each account, and then check on the server with the public secondary key whether the messages can be decrypted. Something like this:
data = secondary_key(user_private_key(cleartext))
This way the data will always be encrypted, and in worst case the server will be able to read it, but others wouldn't.
A few comments to your API :) If you're already using HTTP and POST, you don't really need an id. The POST usually returns a URI that points to the created data. You can then GET that URI, or PUT it to change:
POST /api/data
{"data": "..."}
Response:
Location: /api/data/12345
{"data": "...", "lastmodified": "..." }
To change it:
PUT /api/data/12345
{"data": "..."}
You don't have to do it this way, but it might be easier to implement on the client side, and maybe even help with caching and cache invalidation.
I've got a working Websockets example, where clients receive messages from the server.
I'm not sure how I should send old messages to clients when they connect.
Example:
Each client supplies their name when they connect
The server responds with "[name] just connected" (to all clients)
Any new clients would NOT get these messages
I'm wondering if there's any way clients can receive old messages (either all of them, or messages in the last 5 minutes would be acceptable).
I suspect I may have to capture this information myself, store it somewhere (like a database) and send the messages to new clients myself. Is that right, or am I missing something?
If anyone has pseudo code, or a link to an example of how others have implemented this, that would be handy.
You could do something like this:
Each message should have an id -> muid (Message Unique ID)
Each time a client send s a message, it gets an ACK from the server along with the muid for the sent message.
Each time a new message is received in the server side, a muid is assigned, sent with the ACK and also sent with the message to every connected user. This way the view will be able to present, for every user, the same sequence at some point in the time.
Each time a new user connects it sends the last muid it has received so the server knows where this user stopped receiving messages. The server could then send as many old messages as you want, depending on the kind of storage you implement:
Full history: I would recommend a database storage with proper indexing
Last N messages: Depending on the size of N you could simply store the last N messages in a fixed size Array and send them, all or the needed chunk, on each reconnection. Keep in mind that this will consume memory so, storing last 1024 messages for 1024 different chats would eat quite a bit of memory, specially if messages are of unlimited size.
Hope it helps
You will have to capture it by your own and store it on server... once user connects you will have to name that data to all connected clients and the messages which you have stored back to the user who has connected. So, you will have to code to broadcast the data to users
By the way what are you using server side? (Node, Erlang , etc)
You can check following link if you are using node.js
http://martinsikora.com/nodejs-and-websocket-simple-chat-tutorial
I am currently playing with Faye.js. Upon subscription to a channel I want that specific client to receive an object that would be irrelevant to anybody already in the channel.
How can this be achieved?
More detail:
The object is an array of the last 20 chat comments in the room. Anybody already in the room would have received this object already or been a part of the chats and so it is not required for these to receive it.
Thank you in advance.
The only way I've come across is to generate a client-side GUID. When the client connects, it announces itself to the others through a 'public' channel, you can then use the GUID to send messages directly to this client.
For example, take the piece of code from the answer of this previous question to generate something that looks like a UUID.
You can then do something on the client-side like this:
var guid = guidGenerator();
client.subscribe('/privChannel_' + guid, onPrivateMessage);
client.subscribe('/pubChannel', onPublicMessage);
client.publish('/announce', { 'myId': guid });
function onPrivateMessage() {
// do something
};
function onPublicMessage() {
// do something
};
Your server should always subscribe to the '/announce' channel, and when any message is posted in that channel it should store that id so that it can identify that particular client. Then, the server can use this id to publish to a channel only this client should be subscribed to.
Note however that this is not a good idea for sensitive data. Other clients could also subscribe to '/announce' and farm the guids for malicious purposes.