Scalability and performance in Meteor - android

I've really enjoyed experimenting with Meteor recently and so far i've found it to be a really neat little app. I've decided to migrate an old application over to meteor and have already made some good progress. The application is a live score updates application. While i've been able to demonstrate the concept i've found 2 issues which may prevent me from using it.
1) Data previously existed in a mySQL database so i've written a script to export the data into .js files. For those that might want to do similar, the main part of the PHP code is here.
$handle = fopen($output_file, "w");
foreach ( $tables as $key => $table ) {
$query = "SELECT * FROM $table_prefix$table";
$res = mysql_query($query);
while ( $row = mysql_fetch_assoc($res) ) {
$newTableName = "";
$parts = explode("_", $table);
foreach ( $parts as $k => $v ) {
$newTableName .= ucfirst($v);
}
$string = $newTableName.".insert({";
$first = true;
foreach ( $row as $columnName => $value ) {
if ( !$first ) { $string .= ", "; }
$string .= "$columnName : \"$value\"";
$first = false;
}
$string .= " });";
print $string . "<br>";
}
}
While this has worked for most of the tables, i have one table in particular which contains all the event information. This separate .js file contains 3600 lines of insert statements and it would seem that when this is in the application, the application breaks. If i rename the file to be 'event.js.save' for example, then the application is fine. More specifically, with this .js file in place, when i deploy the application i get..
mac:app user$ meteor deploy <domain>.meteor.com
Deploying to <domain>.meteor.com. Bundling ...
Errors prevented deploying:
Exception while bundling application:
RangeError: Maximum call stack size exceeded
So, Question number 1 is - how much has been done to test the scalability of this appliction?
2) The second issue i have is regarding mobile performance. I've spent a long time getting a stylesheet looking good on all platforms and have been really disappointed to see how quickly the battery on a mobile goes down.
When i load a page in Safari the 'progress' animation in the top right is constantly spinning and from what i've seen 5% of battery goes in probably 10 minutes.
Question number 2 - how is the connection kept alive for browsers? Is there anything that can be done to reduce the impact on mobile browsers?
Thanks.

1:
Applications are very scalable in the case that the meteor site managed the launch just fine with a massive amount of traffic all in one day when they had not planned for it.
The reason for your error is that you can't call that many statements that are the same in a row as far as I'm aware as js thinks it is crashing. I think there are ways to change this however or get round it.
As far as testing they have done I'm unsure, personally I would import the data by itterating through the data and inserting it rather than having it as that many calls (I think that is the issue).
2:
That is a bug that it is spinning however it is constantly checking over ajax or similar methods.
In future sockets will be used which I expect will be more efficient.
Perhaps there will be a way to tune down the number of queries and network intensive it is in the future.

Providing an updated source code to the issue above :
As discussed in one of the answers, the problem seemed to be the number of insert lines and that it was Javascript breaking not Meteor. I've since updated the code and thought others might be interested to.
The following PHP provides JSON output for a table...
// we connect to example.com and port 3307
$link = mysql_connect('localhost', 'username', 'password');
if (!$link) {
die('Could not connect: ' . mysql_error());
}
$db = mysql_select_db("databasename");
$tables = array (
$_REQUEST["table"]
);
$table_prefix = "tableprefix_";
header('Cache-Control: no-cache, must-revalidate');
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Content-type: application/json');
foreach ( $tables as $key => $table ) {
$query = "SELECT * FROM $table_prefix$table";
$res = mysql_query($query);
$first = true;
print "[";
while ( $row = mysql_fetch_assoc($res) ) {
if ( !$first ) { print ", "; }
$first = false;
print ( json_encode($row) . "\n");
}
print "]";
}
mysql_close($link);
I can then use the following in the javascript to load the data.
Events = new Meteor.Collection("event");
if (Meteor.is_server) {
Meteor.startup(function () {
if (Events.find().count() === 0) {
$.getJSON('/ajax/event.json', function(data) {
$.each(data, function(key, val) {
Events.insert(val);
});
});
}
});
}

jonathanKingston said,
1: Applications are very scalable in the case that the meteor site
managed the launch just fine with a massive amount of traffic all in
one day when they had not planned for it.
Which is hilarious, because the home page for Meteor is not a Meteor app, and they had to take all Meteor demo apps off-line due to security / scalability reasons.
Looking forward to the next generation of FUD being spread by stupid programmers conned by pretty demos and Hacker News posts.

Related

Talking clock on old laptop or an Android app

I want to program a "talking clock" for my blind father and I have an very old laptop, 768 RAM. I have tried many different things, from making an app on android with kivy, without any success, because on my laptop (a different one) seems to work like a charm but when I'm "buildoze" it just doesn't play any mp3. I've tried some answers provided on SO with no success. Now I thought to try to install different things, like Python libraries for 2.79 and even Java on my old laptop, but seems that because Linux Mint 2.0 it's no longer supported can't install anything...
Does anyone have any suggestion if I can use that old laptop for such a thing or I can make an app for Android that plays mp3's?
I quite a noobie 😛
Thanks
Quick, dirty, and simple: install espeak and run something like the following in a cron job as often as you like:
dt=$(date +'%A, %B %d, %Y. The time is %l %M %p.'); espeak "Today is $dt"
EDIT: On reflection, It might be better to write a shell script and call that from cron. Assuming your father's only challenge is blindness, he probably doesn't need to be reminded of the date every, say, fifteen minutes. A script could speak the date every eight hours, perhaps, and the time as often as is useful.
EDIT AGAIN: Here's a script I threw together for fun. Give it a try.
#!/bin/bash
# If espeak isn't installed, complain and quit
command -v espeak > /dev/null 2>&1 ||\
{ echo -e "$0 requires espeak to be installed. Exiting..." ; exit 1; }
# ** Set variables
date_now=$(date +'%A, %B %d, %Y')
military_time=$(date +'%R') # 24-hour time
minute=$(date +'%M')
# We don't need espeak to say "zero zero" for the minutes
if [ $minute = "00" ]; then
time_now=$(date +'%l %p')
else
time_now=$(date +'%l %M %p')
fi
# Speak the date only if it's 9am or 5pm
[[ $military_time = "09:00" || $military_time = "17:00" ]] && espeak "Today is $date_now."
espeak "The time is $time_now."
Here's a speaking-clock in JS - just load it into a WebView or something - but you don't even need to package an app: just put it in a HTML page with a PWA manifest and save that as a home-screen icon so it will work while offline.
Click "Run code snippet" and then the button.
It will work in any modern web-browser available for Android and Linux desktops (except Opera and the stock (non-Chrome) Android Browser - it will also work in any modern Linux desktop environment - or any desktop OS, really.
I hope the code is self-explanatory.
It uses the Web Speech Synthesis API which is widely supported (except by the not-Google-Chrome Android Browser for some reason).
Note that it is not possible to make it play automatically or on-load (so need to click the button first to make speech synthesis work), as most browsers (including Chrome) block scripts from auto-playing sounds without user-interaction; the same applies to <video>, <audio>, etc.
Eta voila:
function sayTime() {
if( window.speechSynthesis.speaking ) return;
const now = new Date();
var tts = new SpeechSynthesisUtterance();
tts.text = "The time is now " + now.toLocaleTimeString();
window.speechSynthesis.speak( tts );
}
let timerId = null;
function toggleSpeakingClock() {
if( timerId ) {
window.speechSynthesis.cancel();
window.clearInterval( timerId );
timerId = null;
btn.textContent = "Click me to start";
}
else {
timerId = window.setInterval( sayTime, 1000 );
const btn = document.getElementById('btn');
btn.textContent = "Click me to stop";
}
}
document.addEventListener( 'DOMContentLoaded', function() {
const btn = document.getElementById('btn');
btn.disabled = !( typeof window.speechSynthesis === 'object' ) && ( window.speechSynthesis !== null );
if( btn.disabled ) btn.textContent = "Your browser doesn't support TTS";
} );
<button id="btn" onclick="toggleSpeakingClock()" disabled>Please wait...</button>

Angular slow to build an array on Android device

I have a simple array that takes rows from a database and assigns a distance column as a key.
let output = {};
for (let dataRow of sqllite.rows) {
output[dataRow.distance] = dataRow;
}
In testing in Chrome browser on PC, it takes less than a second to complete, but on an Android device, it just hangs.
What's the best way to handle this?
Thanks
Mark
After a stab in the dark, this fixed it. Changing from Typescript to Javascript.
let output = {};
for (var outputIndex in sqllite.rows) {
output[sqllite.rows[outputIndex].distance] = sqllite.rows.rows[outputIndex];
}
No idea why TS is slower

Android: Using server-side when working with Parse

Me and my friend are working on an app., and we wish to use Parse.com as our data base from which we can retrieve info.
We can't decide what is the best way to access the data on Parse. For the sake of the example, our app. (i.e. client side) needs something stored on the Parse data base (say some number) - should it directly run the query using the Parse API, or should it make a request to a server side, let it retrieve that number from Parse, and send it back to the client?
We know there's no definite answer, but we couldn't find answer regarding this specific situation. We read this post: When to use client-side or server-side?,
but this not exactly the same case.
I claim that we should try to seperate as much as possible from client side and data bases, and leave these queries run by someone who's in charge (server), where my friend claims this adds unnecessary complication, since it's very natural to use the tools supplied by Parse to access the data base from the client side, without the need for a protocol etc.
We'd appriciate any advice,
Thank you.
In general, go right ahead and make a normal call.
I'd encourage you to do that first in any case, to get everything working on both ends.
Then if necessary go to Cloud Code.
If you are going to do more than one platform (ie iOS and Android), cloud code can be a huge timesaver.
BUT don't forget that for simple calls, cloud code is a waste of time. "Normal" Parse calls are amazingly, incredibly, amazingly, fast and quick to work with.
There is absolutely nothing "wrong" with using normal Parse calls - so do that.
Regarding the question, when do you literally have to use a cloud code call -- you'll know, because you won't be able to do it with a normal call :)
Don't forget very often you can simply use "afterSave" or "beforeSave" in cloud code, to do a huge amount of work. You often don't literally need to go to a "custom call" in cloud code.
Here's a fantastic
Rule of thumb for Parse cloud code --------->
If you have to do "more than one thing" ... in that case you will likely have to make it a cloud code function. If you have to do "three or more things" then DEFINITELY make it a cloud code function.
That's a good rule of thumb.
(Again, as I say, often just an "afterSave" or similar works brilliantly...rather than literally writing a full custom call.)
Here's a typical example of a cloud call that saves 18 billion lines of code in all the platforms covered by the dotcom. First the cloud code...
Parse.Cloud.define("clientRequestHandleInvite", function(request, response)
{
// called from the client, to accept an invite from invitorPerson
var thisUserObj = request.user;
var invitorPersonId = request.params.invitorPersonId;
var theMode = request.params.theMode;
// theMode is likely "accept" or "ignore"
console.log( "clientRequestAcceptInvite called.... invitorPersonId " + invitorPersonId + " By user: " + thisUserObj.id );
console.log( "clientRequestAcceptInvite called.... theMode is " + theMode );
if ( invitorPersonId == undefined || invitorPersonId == "" )
{
response.error("Problem in clientRequestAcceptInvite, 'invitorPersonId' missing or blank?");
return;
}
var query = new Parse.Query(Parse.User);
query.get(
invitorPersonId,
{
success: function(theInvitorPersonObject)
{
console.log("clientRequestFriendRemove ... internal I got the userObj ...('no response' mode)");
if ( theMode == "accept" )
{
createOneNewHaf( thisUserObj, theInvitorPersonObject );
createOneNewHaf( theInvitorPersonObject, thisUserObj );
}
// in both cases "accept" or "ignore", delete the invite in question:
// and on top of that you have to do it both ways
deleteFromInvites( theInvitorPersonObject, thisUserObj );
deleteFromInvites( thisUserObj, theInvitorPersonObject );
// (those further functions exist in the cloud code)
// for now we'll just go with the trick of LETTING THOSE RUN
// so DO NOT this ........... response.success( "removal attempt underway" );
// it's a huge problem with Parse that (so far, 2014) is poorly handled:
// READ THIS:
// parse.com/questions/can-i-use-a-cloud-code-function-within-another-cloud-code-function
},
error: function(object,error)
{
console.log("clientRequestAcceptInvite ... internal unusual failure: " + error.code + " " + error.message);
response.error("Problem, internal problem?");
return;
}
}
);
}
);
If you are new to Parse it's incredibly hard to figure out how to call these from Android or iOS! Here's that one being called from Android ...
this will save you a day of messing about with HashMaps :)
private static void handleInvite( ParseUser invitor, final boolean accepted )
{
String invitorId = invitor.getObjectId();
// you must SEND IDs, NOT PARSEUSER OBJECTS to cloud code. Sucks!
String cloudKode;
cloudKode = (accepted? "accept" : "ignore");
HashMap<String, Object> dict = new HashMap<String, Object>();
dict.put( "invitorPersonId", invitorId );
dict.put( "theMode", cloudKode );
Toast.makeText(State.mainContext, "contacting...", Toast.LENGTH_SHORT).show();
ParseCloud.callFunctionInBackground(
"clientRequestHandleInvite",
dict,
new FunctionCallback<Object>()
{
#Override
public void done(Object s, ParseException e)
{
Toast.makeText(State.mainContext, "blah", Toast.LENGTH_SHORT).show();
// be careful with handling the exception on return...
}
});
}
And here's the same cloud call from iOS ... well for now, until you have to do it in SWIFT
-(void)tableView:(UITableView *)tableView
commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath
{
int thisRow = indexPath.row;
PFUser *delFriend = [self.theFriends objectAtIndex:thisRow];
NSLog(#"you wish to delete .. %#", [delFriend fullName] );
// note, this cloud call is happily is set and forget
// there's no return either way. life's like that sometimes
[PFCloud callFunctionInBackground:#"clientRequestFriendRemove"
withParameters:#{
#"removeThisFriendId":delFriend.objectId
}
block:^(NSString *serverResult, NSError *error)
{
if (!error)
{
NSLog(#"ok, Return (string) %#", serverResult);
}
}];
[self back]; // that simple
}
Note For the iOS/Swift experience, click to: How to make this Parse.com cloud code call? which includes comments from the Parse.com team. Hope it saves someone some typing, cheers

PhoneGap App: jQuery getJSON getting 404 error when getting local file with relative URL

I have an HTML/JavaScript app that I'm trying to convert to an App using PhoneGap via the Phonegap Build app
Everything works fine through the browser, and the only problem the app is having is that the call to getJSON is returning a 404 error when trying to load my local resources.
Here is the culprit:
$.getJSON( "./shapes/json/" + abbr + '.json', gotJSON(abbr) );
I have whitelisted every domain, just to be sure:
<access origin="*" />
Is this something that is not possible from the phonegap environment? Or am I doing something wrong?
If needed, I can host the files elsewhere and do a cross-domain ajax call, but I'd rather have the files right there on the device.
This is currently happening on Android, which is the only system I can test at the moment.
UPDATE:
I'm now trying:
var xhrShapes = new XMLHttpRequest(), xhrSuccess = gotJSON(abbr);
xhrShapes.open('GET', config.path + "/shapes/json/" + abbr + ".json");
xhrShapes.onreadystatechange = function(e){
if( this.readyState === 4 ){
if( this.status === xhrSuccessCode ){
xhrSuccess(JSON.parse(this.responseText));
}
}
}
xhrShapes.send();
config.path is "file:///android_asset/www" and I'm getting 0 as a success code (which indicates success for 'file://' requests). but xhrShapes.responseText is blank and everything stops at the call to JSON.parse. I feel like I'm missing something simple...
The problem had nothing to do with the code, but rather with the file names being case-sensitive... my abbr variable was uppercase, but filenames are lowercase. $.getJSON works perfectly, now that I've corrected this (though now my pride needs some repairs).

Recommended way to sync information between devices

I'm currently developing and I came up to the following question:
How can/should I store basic information, such as unlocked items and levels between devices.
Of course, I store them within preference files.
But what if the user buys a new phone and wants to continue playing on there?
(Shared-)Preferences won't be duplicated.
Thus, is there any (from google) recommended way (cloud?) to solve this issue?
I'm no expert, but the way I do this is with an online SQL database. Have the device connect to it, and save/load any data you need. I know this will not work with files, only text-based information. I also do this using Flash. If this is your route I can share some code.
Ok, so you need 3 things if you are doing this in flash: Flash (duh), PHP files, and a SQL server (I have a MYSQL server).
How it will work is flash will load php, php will do it's thing (like making changes to the database) and spit back it's results to flash.
Let's say we want to get all usernames from the database.
Database
The table name is called "Players" with a single column called "u_id"
Flash
var loader:URLLoader; //makes a loader that will "load" the php page
public function DoIt()
{
loader = new URLLoader();
loader.addEventListener(Event.COMPLETE, loaderComplete);
loader.load(new URLRequest("http://yourDomain.com/thePHPFile.php?arg=42"));
}
public function loaderComplete(e:Event):void
{
var returnXML:XML; //we are assuming the return values are in xml format, trust me, this might be easier
for each (var ListItem:XML in returnXML..Users) //you might need to change this "users" to be the root of the xml file
{
var userName:String = ListItem.u_id.toString();
//you can do what you want with the data here
}
}
PHP
Now it might not be required, but I try to make sure the PHP files and the DB is on the same host, so that I can do "localhost" as the host name
<?php
$argumentVar = $_GET["arg"]; //not using this, just showing it's possible
$connect = mysql_connect("localhost", "db_username", "db_password");
if(!$connect)
{
die("error connecting to DB" . mysql_error());
}
$db_selected = mysql_select_db("the_name_of_the_db", $connect);
if(!$db_selected)
{
die("error selecting db" . mysql_error());
}
$table_name = "Players" //this is the name of your database
$sql = "SELECT * FROM $table_name;"; //not sure if you need ending ';'
$dbresult = mysql_query($sql, $connect); //this is where all your data comes into
//and now to save all the information to a xml file
$doc = new DOMDocument('1.0');
$root = $doc->createElement('root');
$root = $doc->appendChild($root);
while($row = mysql_fetch_assoc($dbresult))
{
$occ = $doc->createElement($table_name);
$occ = $root->appendChild($occ);
foreach ($row as $fieldname => $fieldvalue)
{
$child = $doc->createElement($fieldname);
$child = $occ->appendChild($child);
$value = $doc->createTextNode($fieldvalue);
$value = $child->appendChild($value);
}
}
$xml_string = $doc->saveXML();
echo $xml_string;
?>
Ok, go play with that, it should start you on the right path.
i think the best way would be to have an online database be maintained which is regularly updated with the latest information for every registered user. So instead of storing everything locally it can be stored on a remote server and sent to the user as and when required. so as long as the user owns the account created by himself for the application, no data will be lost.

Categories

Resources