Recently, we started developing a chat feature for our app and since we are already using Parse SDK/Server, we decided to go with the Parse LiveQuery...
The thing is, there's no sufficient enough documentation on how to deploy a Parse LiveQuery server, on a separate one! We saw this: http://docs.parseplatform.org/parse-server/guide/#scalability but we didn't really figured out on how to proceed...
So we have a couple of questions:
What do we need to do, in order to create a scalable Parse LiveQuery Server? (preferably on Digital Ocean / Heroku)
How can it communicate, with our original Parse Server, where our MongoDB is stored?
I suggest using Apache Kafka
@benishak I read again the Parse documentation and saw that they suggest redis!
So if I got it right, in order to make the Parse LiveQuery servers scalable, I need to deploy somewhere a redis-server, correct? If so, is it a good idea to install it on the server where my Parse Server is deployed, or should I do it on a separate one?
I meant if you are really looking for a permanent scaleble solution and you expect your app to grow significantly, you can try Apache Kafka by replacing LiveQuery completely. With Apache Kafka you can stream real time database changes, there is mongodb connector, It is open source and free and incredibly scaleble
Otherwise you can use Redis and deploy liveQuery on many independent instances/machines but use only one central Redis for all the instances, you can then run a load balancer infront of the instances @samigos
@benishak So I need a load balancer as well? Doesn't redis cover that?
Here is our way to setup scalable Parse LiveQuery server on Heroku
Because there is one and only 'web' process on Heroku, it will divide into two Heroku apps: Main and LiveQuery.
A: Main app - All features except for LiveQuery server
_Step A1._ Setup a Parse app on Heroku
_Step A2._ Add a Heroku Redis (free plan is enough for testing)
_Step A3._ Configure Parse app, add redisURL for liveQuery
var api = new ParseServer({
...
liveQuery: {
classNames: [...],
redisURL: REDIS_URL_ON_MAIN
},
...
});
B: LiveQuery app - A scalable LiveQuery server for Main app
_Step B1._ Steup another Parse app on Heroku
_Step B2._ Configure Parse app, DO NOT set liveQuery
var api = new ParseServer({
appId: APP_ID_ON_LIVEQUERY,
masterKey: MASTER_KEY_ON_LIVEQUERY,
serverURL: SERVER_URL_ON_LIVEQUERY,
databaseURI: // (Optional) Only warning even if leave it default
});
_Step B3._ Create LiveQuery server
var app = express();
app.use(PARSE_MOUNT_ON_LIVEQUERY, api);
var httpServer = require('http').createServer(app);
httpServer.listen(PORT_ON_LIVEQUERY, function() {
/* Create HTTP server successfully */
});
ParseServer.createLiveQueryServer(httpServer, {
redisURL: REDIS_URL_ON_MAIN // Redis URL from Mani app
});
C: Client side - Swift for example
_Step C1._ Init Client instance using Client(server:applicationId:clientKey:)
let client = Client(server: SERVER_URL_ON_LIVEQUERY,
applicationId: APP_ID_ON_LIVEQUERY,
clientKey: nil)
_Step C2._ Subscribe for LiveQuery
let subscription = client.subscribe(query)
subscription.handle(Event.created, { query, object in
/* Handle CREATE event */
})
In the end, we can scale web process in LiveQuery app on Heroku ^_^
Welcome any comments on my glist
Hello @ananfang I still can't figure out on some ways.
In A: Main App, should I create new parse instance? Something like:
var api = new ParseServer({
...
liveQuery: {
classNames: [...],
redisURL: REDIS_URL_ON_MAIN
},
...
});
var app = express();
app.use(PARSE_MOUNT_ON_EXPRESS, api);
var httpServer = require('http').createServer(app);
httpServer.listen(PORT_ON_EXPRESS, function() {
/* Create HTTP server successfully */
});
so, there are 2 files, and both of them create each http instance, is this what you mean?
My use case is: create 2 instances, first is API, second is for livequery. Then I map each of them with nginx, let's say api.server.com and stream.server.com. To do this, I need 2 server instance with different port.
But I can't figure out on client because I can't set multiple Parse.serverURL on client. So,what's the best practice? Thank you
@pewh
In your settings, you just set the Client (from ParseLiveQuery library) to listen "stream.server.com" by
let client = Client(server: "stream.server.com",
applicationId: APP_ID_ON_STREAM_SERVER,
clientKey: nil)
Don't use
let client = Client.shared
Because this shared singleton instance will refer to your API Parse server.
Thanks @ananfang for the responds
By the way, I use Parse JS. Is there any equivalent of Client.shared in JS? Mine use Parse.initialize & Parse.serverURL on JS, and it seems client side didn't create instance; just global config.
Parse.initialize('appId')
Parse.serverURL = 'http://api.server.com'
@pewh
I am using Parse Client in iOS (Swift), there are two separate libraries Parse and ParseLiveQuery. In ParseLiveQuery library, I can set Client's server URL different from default Parse.
After I looked JS library, I found there is no way to set different server URL on Live Query. T___T
Why can't one have LiveQuery and main app on one Heroku instance?
So I followed the steps and created 2 Heroku Apps, one for the Parse Server and another for the LiveQuery Server following the schema for scalability. It subscribed to the query (Using Swift Client) but the events handler wasn't being called when I changed or created the object. So i tried initializing the LiveQuery Server on the Parse Server, now it works but I'm not sure which Heroku App is running the LiveQuery processes. This is how I configured them:
var api = new ParseServer({
databaseURI: databaseUri
cloud: process.env.CLOUD_CODE_MAIN ,
appId: process.env.APP_ID,
masterKey: process.env.MASTER_KEY,
serverURL: process.env.SERVER_URL,
fileKey: process.env.FILE_KEY,
javascriptKey: process.env.JAVASCRIPT_KEY,
clientKey: process.env.CLIENT_KEY,
liveQuery: {
classNames: ['MsgTest','Message'],
redisURL: process.env.REDIS_URL
});
var parseLiveQueryServer = ParseServer.createLiveQueryServer(httpServer, {
appId: process.env.APP_ID_LIVE,
masterKey: process.env.MASTER_KEY_LIVE,
serverURL: process.env.SERVER_URL_LIVE,
keyPairs: {
"javascriptKey": process.env.JAVASCRIPT_KEY_LIVE,
"clientKey": process.env.CLIENT_KEY_LIVE
},
websocketTimeout: 10 * 1000,
cacheTimeout: 60 * 600 * 1000,
logLevel: 'VERBOSE',
redisURL: process.env.REDIS_URL
});
var api = new ParseServer({
databaseURI: databaseUri,
appId: process.env.APP_ID_LIVE,
masterKey: process.env.MASTER_KEY_LIVE,
serverURL:process.env.SERVER_URL_LIVE,
clientKey: process.env.CLIENT_KEY_LIVE,
liveQuery: {
classNames: ['MsgTest','Message'],
}
});
var parseLiveQueryServer = ParseServer.createLiveQueryServer(httpServer, {
appId: process.env.APP_ID_LIVE,
masterKey: process.env.MASTER_KEY_LIVE,
serverURL: process.env.SERVER_URL_LIVE,
keyPairs: {
"javascriptKey": process.env.JAVASCRIPT_KEY_LIVE,
"clientKey": process.env.CLIENT_KEY_LIVE
},
websocketTimeout: 10 * 1000,
cacheTimeout: 60 * 600 * 1000,
logLevel: 'VERBOSE',
redisURL: process.env.REDIS_URL,
});
self.theQuery = Message.query()?.whereKey("Test", equalTo: true)
self.subscriber = Client(server: ws://process.env.SERVER_URL_LIVE , applicationId: process.env.APP_ID_LIVE, clientKey: process.env.CLIENT_KEY_LIVE
self.subscription = subscriber.subscribe(theQuery)
self.subscription.handle(Event.created, { (query, object) in
print("Item Created")
})
In the logs, the LiveQuery Server shows the connection and it "works", but in the Parse Server logs it shows a process as well and the Redis Activity is double what it is supposed to be.
Any help, comment is greatly appreciated!!! Thanks in advanced!
@Ernesto89
In your app 1, remove
var parseLiveQueryServer = ParseServer.createLiveQueryServer(httpServer, {
...
}
In your app 2, remove
liveQuery: {
classNames: ['MsgTest','Message'],
}
You can also refer to this gist
Hello. In this setup, where would my cloud code reside? Should it also be on the LiveQuery instance or the main instance?
In a nutshell, it depends where your bottlenecks are and the kind of workload you鈥檙e having. If you鈥檙e wondering, It鈥檚 probably because you haven鈥檛 seen the issue in your setup. Scaling is usually in response of a problem, if you haven鈥檛 face the problem, it鈥檚 hard to say which solution fits.
@flovilmart You are quite right. My application has not seen the volume of traffic that'll expose bottlenecks. However, I'm trying to build it to be scalable from the ground up. That's why I asked, just to be able to understand where to put the cloud codes and why. Currently, they reside on my main instance but I was wondering if this setup would allow clients to still reach the cloud code.
The only places you can put cloud code is either alongside your parse instance, where it communicates to parse-server through a local http inteface, or on a remote server, where parse-server would make HTTP calls to your 3rd party server. The latter is called 'webhooks' and is configurable via the dashboard.
The livequery server doesn't need to know about cloud code as it doesn't process any objects for read and writes, it's responsibility is just to listen for event and forward the relevant ones to the connected clients.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
For anyone who lives trouble with seting external livequery app server url in JS SDK you can use it like this
Parse.liveQueryServerURL = 'ws://XXXX'
Most helpful comment
Here is our way to setup scalable Parse LiveQuery server on Heroku
Because there is one and only 'web' process on Heroku, it will divide into two Heroku apps: Main and LiveQuery.
A: Main app - All features except for LiveQuery server
_Step A1._ Setup a Parse app on Heroku
_Step A2._ Add a Heroku Redis (free plan is enough for testing)
_Step A3._ Configure Parse app, add redisURL for liveQuery
B: LiveQuery app - A scalable LiveQuery server for Main app
_Step B1._ Steup another Parse app on Heroku
_Step B2._ Configure Parse app, DO NOT set liveQuery
_Step B3._ Create LiveQuery server
C: Client side - Swift for example
_Step C1._ Init Client instance using Client(server:applicationId:clientKey:)
_Step C2._ Subscribe for LiveQuery
In the end, we can scale web process in LiveQuery app on Heroku ^_^
Welcome any comments on my glist