Is Apple’s one-size-fits-all approach starting to fail?

My mother-in-law cracked me up when she asked “What’s the matter with Apple, how come they only make things in one or two sizes?” She’s as non-techie as they come and I think she nailed an important point. I’ve joked about this with friends and colleagues and even mocked about it in presentations. Yet, ever since the Apple September 10th announcement I’ve been wondering: can Apple innovate enough to stay a true technological leader, or are they starting to enter an era of simply copying others and wrapping ‘improvements’ under a luxury brand umbrella? I’m leaning towards to latter. After seeing Tim Cook’s presentation on September 10th, my opinion has started to solidify even more.

Factoid: Everyone will agree, for better or for worse, we are in an era of choice. Look at the variety of Androids. Think about the sheer number of cable and satellite TV channels, the variety of TV shows aimed at a dizzying variety of demographics, music has splintered from the days of major rock bands into hundreds of niche, self published indie groups.  Satellite radio. Most major car manufacturers now have dozens of car models and many of them you can significantly customize. And, even grocery stores now have a ridiculous number of choices for a lot of items that use to only have several manufacturers or producers.

So, what about Apple?

Apple has so far resisted playing to the status quo set by IBM, Microsoft and now Android of allowing endless variations of their products to fit a variety of needs and wants. It wasn’t that long ago, for example, that Dell Computer was a leader in selling desktop and laptop computers. That era has passed and I don’t believe Apple wants to follow in these giants footsteps. Perhaps there is some sort of evolutionary cycle that these leading public tech companies follow, like natural boom and bust cycles that we see in economics, neighborhoods, cities and world economies that Apple won’t be able to escape.

This leads me right back to the September 10th announcement.  Apple seems to have entered an era of incremental improvements in hardware and software: new colors (big whoop), finger print reader (Moto Atrix had that) and faster hardware.  But, the biggest pressure I think they’ll start facing is they are now behind the curve in allowing people to have choices. Real choices…not just new colors for custom cases made of soft silicon rubber. Choices are the way they world is headed right now. Case in point, how long have iPhones had 4-inch screens? How many tens of millions of larger Android screen phones have been sold? Can Apple simply ignore this and stick to their one-size-fits-all guns if the board of directors starts seeing missed opportunities and potentially lower sales?

Android, in comparison to iOS, has more varieties of sizes, manufacturers, shapes and colors than there are grapes for making wine. Buying an Android is like shopping for clothes at any mainstream department store. In addition to a bijillion patterns and colors, you have sizes like XS, SM, M, L, XL, XXL, etc. And then there is relaxed fit, straight fit and athletic fit along with different collar sizes.  This is brilliant from a consumer standpoint, and yes it’s a nightmare for application developers and IT shops that support them. But, developers and IT folks only represent a small fraction of an enormous world-wide porous marketplace full of more consumers, cultures and tastes than perhaps existed in history.

Buying a Mac is like walking into a top-notch art gallery. As you glide along into the next majestic room, shuffling your feet in hushed respect you can hear the mac genius say in an elegant foreign accent, “…and here on this masterfully carved solid white marble pedestal, fabulously embellished with an aluminum case and crystal clear retina view screen, and protected from theft by 12 visible and hidden security features is, ladies and gentlemen…the (audience gasps) Apple Macbook Pro.”

But, at some point even art galleries change up their exhibits as people’s tastes and interests change.  Even galleries have to innovate to stay ahead of the times. Can Apple change? Can Apple adapt to a new era of endless choices? Can Apple reinvent itself?

How to use social media if you are in a disaster area

If you are directly affected by a disaster, rather than a curious bystander, there a number of important things to know that will help you get vital and potentially life saving information as well as to help responders get a better picture of what’s going on. A disaster is any event that significantly affects the lives, property and infrastructure over a large geographic region such as hurricanes, floods, tornadoes, earthquakes and tsunamis.

Why blog about this? During the week of September 11, 2013 I got a firsthand, on-the-ground look at the impact of social media as a large scale flooding disaster literally unfolded before my eyes. As a geek who is a volunteer backcountry forest ranger and a former paramedic with search and rescue experience, I was well prepared to gather information for making potentially life changing decisions with my family and friends. However, I was surprised at how little information some of my friends and neighbors were able to glean in today’s world of the real-time information fire hose.

Follow authoritative sources as soon as possible. The very first thing you should do, if you haven’t followed these Twitter accounts beforehand, is search for the following websites in your area as soon as you become aware of a disaster:

  • Search for your local emergency management office, for example: “boulder office emergency management”, and here’s the an actual site . Look for Twitter feeds, facebook pages, and many authorities even allow you to sign up for emergency messages. You should sign up for all three!
  • Also search for your city police, country sheriff on Twitter using search terms like “boulder county sheriff twitter.” Here’s an example.
  • Follow your local news stations such as this 9news feed.
  • Local newspapers sometimes set up streaming social media feeds like this one.

Here’s a few actual (scary) tweets to give you an idea of the type of information to expect:

@boulderpolice : El Dorado Springs – S. #Boulder Creek is at 1600 CFS. Please go to higher ground ASAP. #BoulderFlood

@MikeNelson247 FLASH FLOOD EMERGENCY – Boulder County! Life threatening situation! Get @StormShieldApp now! #7News #cowx twitpic.com/dd3iy3 #Boulder

You may also automatically receive a Wireless Emergency Alert (WEA) in the form of a text message from your local authorities. Having heard quite a few of these messages for real, your phone will squawk the most bizarre alert tones you’ve ever imagined several times in order to get your attention. These are a very recent addition to the local, State and Federal emergency broadcast tools. Yes, you can even receive these if you are traveling and temporarily residing in an affected area.

A small time window for life and death decisions. The critical window of time starting immediately before and as the disaster event is occurring is when most of the life and death decisions are made with sparse and often contradictory information. If you are in the disaster area then getting early and continuous updates of information becomes critical. Even authorities won’t have a complete picture of what’s going on yet but they almost always issue bulletin’s, tweets and facebook posts if a disaster is imminent. It is during this period that family decision making becomes immediate, personal and potentially life changing. More authoritative information is better. Second hand rumors, innuendo, and twitter spam messages are just garbage that can be fatally distracting.

There will be an initial information lag. If you are in a disaster area, you want information. You want it now and you want it straight with no B.S. But, the truth is that authoritative information will have a lag time especially at ground zero and during the initial phases of the disaster. Authorities need precious time to gather reports, share bits of information, and brainstorm under intense pressure to try and make sense of what’s happening. Official news agencies also depend on the authorities, so their information lags as well. As long as you have an internet connection hang in there and keep monitoring.

What if the power goes out? If the power goes out, limit your smartphone and tablet usage to regular intervals, such as every 15 to 30 minutes, in order to conserve your batteries. If your car or truck is in a safe place and not affected by the disaster you can also use your car charger to recharge your phone. If you have a traditional land-line phone those can often stay alive even if the power is out.  Call someone outside of your area to someone who has power and can do information gathering for you as well as relay messages to family and friends. VoIP phones usually come with a battery backup. Conserve that battery by turning it off when you don’t need it.

Let family and friends know you are okay. Send regular updates to your family and friends via facebook and email. This makes sure they get the messages, rather than them having to sort through potentially hundreds of other tweets.

What can I tweet? There are things you can tweet that will help local authorities and other people in your area to understand the situation on the ground. Some authorities use twitter analysis tools to filter out the noise and try to get to the most important information. Here are my suggestions:

  • If you are in an immediately life threatening situation try to call 911 first. This should be a no-brainer.
  • If 911 is busy or unavailable then tweet to your local authorities your address, the nature of the emergency and the appropriate hashtag for your disaster.
  • Find out the hashtag for the disaster. You can start by doing twitter searches on the name of your area (e.g. city, county, State), or the name of the hurricane, etc. If you don’t know the hashtag, because maybe it’s very early in the stages of the disaster that one hasn’t solidified yet, then add city and state to your tweet.
  • Tweet about things that affect lives, property or infrastructure such as roads and bridges. Good examples of helpful tweets include downed power lines, creeks going over their bank, gas leaks, flooded roads, and damaged bridges.
  • Some twitter applications can allow you to share your GPS information when you tweet. If you feel comfortable doing that then check your twitter application settings and turn on location sharing.

Here’s a couple real-life tweets from local people during the Colorado flooding:

SCANNER: ponderosa trailer park off broadway in boulder getting reverse 911 to evacuate NOW #boulderflood

Iris & Bdwy severe flooding, impassable 1233am #boulderflood #cowx

What type of tweeting is not helpful? General chatter and tweets that have no location information along with a description of the problem are not helpful and make it harder for everyone to find useful information. If you want to talk among friends then use facebook or set up a Google Hangout. Here are a few examples of non-helpful tweets:

“my basement is flooding”

“Does anyone know what’s going on?”

“heavy rain here”

“the sirens are going off downtown”

What else can I do to prepare? If you live in or recently moved to a disaster prone area such as tornado alley or an area that gets hit by frequent flooding one of the best things you can do is get a weather alert app for your phone, such as this one from weather.com, or get a weather radio alert device especially if you turn off your phone at night. These days during the daytime most of us are tapped into many information sources that would alert us to problems fairly quickly. Potentially dangerous events can still happen at night and oftentimes they do.

node.js: batching parallel async http requests

Some DNS providers limit the number of parallel, asynchronous HTTP requests that you can run simultaneously. Not all providers have this feature and it is commonly called DNS RRL, or DNS Response Rate Limiting. These rate limits are typically used for the very good reason of thwarting DDoS attacks. However, these DNS management tools can also limit legitimate (non-spam) IT shops that are performing their jobs.

The good news is there is a pattern you can use within a Node.js application that let’s you use a blocking timer to wait for one batch of asynchronous parallel requests to complete before the next batch is run. Depending on your provider, this can help you help prevent DNS errors as well as incurring the wrath of your DNS provider. And, it’s still much more efficient than running synchronous requests.

Why not just use async.parallelLimit or something similar? As of the time of this writing, the async library doesn’t have batch capabilities. Therefore, the pattern I’m proposing allows you to fine tune the output HTTP request throttling by giving you the ability to set the delay that occurs between batches. Additionally, you can tokenize your batches to provide more control over handling specific tasks related to the order in which tokens are received.

The commercial use cases for this scenario include polling multiple RSS feeds, as well as JSON and xml feeds. These patterns are typical of large news feed aggregators and monitoring software.

Here is the code you’ll need to accomplish this. NOTE: you’ll want to run this code as a child process.  I’m skipping the nitty-gritty of how to use the Node async library and child processes so that this post can stay focused. You may also want to read my post on Node.js: moving intensive tasks to a child process.

Step 1. Set up a function that executes async.parallel and allows you to pass in both your data array and a token. Since async.parallel doesn’t let you inject a callback, we use a custom event emitter to announce when each batch is complete. This let’s you decouple the blocking timer task from the async task completion event.

this._async = function(/* Array */ data, /* Number */ token){
    try{
        async.parallel(data,function(err,results){
            console.log("Data retrieved! COUNT = " + results.length);
            try{
                var object = {
                    "results":results,
                    "token":token
                }
                event.emit("AsyncComplete",object);
            }
            catch(err){
                console.log("_async process.send() error: " + err.message + "\n" + err.stack);
            }
	}.bind(this._async))
     }
     catch(err){
         console.log("_async error: " + err.message + ", " + err.stack);
     }
}

Step 2. Here we set up our timer to loop in intervals of one second to wait until the current batch job completes. Once it’s complete then we fire off the next batch to async.parallel. We also listen for our custom AsyncComplete event to fire, and once that does then we take the results and build an array until we have all the tokens back. Once all tokens are received then we send the final results array back to the parent process via process.send().

this._loopArray = function(/* Array */ arr){

    var previousVal = 0;
    var segment = null;
    var length = arr.length;

    var remaining = 0; // number of tokens remaining
    var t = 0;         // number of loops counter
    var token = 0;     // token received back from async
    var count = 1;     // internal token up counter

    var totalTokens = this._isEven(arr / 10) ? arr.length / 10 : Math.ceil( arr.length /10);

    var resultsArray = [];
    var result = null;

    event.on("AsyncComplete",function(event){

        console.log("async complete " + event.token);

        result = event.results;
        resultsArray.push(result);
        token = parseInt(event.token);

        if(count == totalTokens){
            console.log("total has been reached");
            process.send(resultsArray);
        }
    }.bind(this))

    if(length > 0){
        var timer = setInterval(function(){

        console.log("t= " + t + ", token= " + token + ", " + totalTokens)

            if(t == token && count <= totalTokens){
                count++;
                if(t <= length) t+=10;
                remaining = length - t;

                if(remaining > 10){
                    segment = arr.slice(previousVal,t);
                    console.log("segment length " + segment.length)
                    previousVal = t;
                }
                else{
                    segment = arr.slice(previousVal,length);
                }

                console.log("remaining = " + remaining);

                if(segment != null && t != 0){
                    this._async(segment,t);
                }

                if(remaining <10)clearTimeout(timer);
            }

	    console.log("tick");
        }.bind(this),1000);
    }
}

Complete code snippet. Here’s all the code you’ll need for the child process.


//Retriever.js - batch processor for async.parallel requests

var http = require("http");
var async = require("async");
var Event = require("events").EventEmitter;

process.on('message',function(msg){

    this._async = function(/* Array */ data, /* Number */ token){
        try{
            async.parallel(data,function(err,results){
                console.log("Data retrieved! COUNT = " + results.length);
                try{
                    var object = {
                        "results":results,
                        "token":token
                    }
                    event.emit("AsyncComplete",object);
                }
                catch(err){
                    console.log("_async process.send() error: " + err.message + "\n" + err.stack);
                }
             }.bind(this._async))
         }
         catch(err){
             console.log("_async error: " + err.message + ", " + err.stack);
         }
    }

    this._loopArray = function(/* Array */ arr){

        var previousVal = 0;
        var segment = null;
        var length = arr.length;

        var remaining = 0; // number of tokens remaining
        var t = 0;         // number of loops counter
        var token = 0;     // token received back from async
        var count = 1;     // internal token up counter

        var totalTokens = this._isEven(arr / 10) ? arr.length / 10 : Math.ceil( arr.length /10);

        var resultsArray = [];
        var result = null;

        event.on("AsyncComplete",function(event){

            console.log("async complete " + event.token);

            result = event.results;
            resultsArray.push(result);
            token = parseInt(event.token);

            if(count == totalTokens){
                console.log("total has been reached");
                process.send(resultsArray);
            }
        }.bind(this))

        if(length > 0){
            var timer = setInterval(function(){
                console.log("t= " + t + ", token= " + token + ", " + totalTokens)

                if(t == token && count <= totalTokens){
                    count++;
                    if(t <= length) t+=10;
                    remaining = length - t;

                    if(remaining > 10){
                        segment = arr.slice(previousVal,t);
                        console.log("segment length " + segment.length)
                        previousVal = t;
                    }
                    else{
                        segment = arr.slice(previousVal,length);
                    }

                    console.log("remaining = " + remaining);

                    if(segment != null && t != 0){
                        this._async(segment,t);
                    }

                    if(remaining <10)clearTimeout(timer);
                }

	        console.log("tick");
            }.bind(this),1000);
        }
    }

    this._isEven= function(value){
        if(value%2 == 0)
            return true;
        else
            return false;
    }

    this._init = function(){
        this._loopArr(this._someArr);
    }.bind(this)()
}

process.on('uncaughtException',function(err){
    console.log("retriever.js uncaught exception: " + err.message + "\n" + err.stack);
})