Author Archives: Justin Laing

Bike Shops: Buy Local Now

We’re excited to be working with SmartEtailing on the new Buy Local Now initiative. It has the potential to send customers to your store to buy products instead of shopping online.

Here’s how it works:

  1. A consumer finds the manufacturer web page for the product they’re interested in. For example CycleOps Fluid2 Trainer.
  2. The consumer sees the “Buy Local Now” button and clicks it.
  3. Shops in their local area are displayed. At the top are shops who are integrated with Buy Local Now and have the item in stock. Below are local shops who either don’t have it in stock or aren’t integrated.
  4. The consumer clicks “Buy Now” from one of the local shops.
  5. They are directed to the shop website and page for that product where they can purchase the product and select to pick it up in store (if their Ecommerce system supports that feature).
Buy Local Now Brand Product Page
Buy Local Now Shops Map
Buy Local Now Shop Page
Buy Local Now Brand Product PageBuy Local Now Shops MapBuy Local Now Shop Page

Integrated With MerchantOS

Good news! If you have a MerchantOS account you’re half way there. If you also have a SmartEtailing site with POS sync turned on then you’ve already crossed the finish line. Here’s what you can do right now if you want to get Buy Local Now:

We’d Love To Know What You Think

Please give us feedback on Buy Local Now and our integration with SmartEtailing.

AWS DynamoDB For Session Redundancy And Failover

This article will go over what DynamoDB is and how we use it to backup our session data. It allows us to failover from one datacenter (AWS region) to another without losing session data and logging people out of our system.

Amazon Web Services (AWS) DynamoDB

DynamoDB is a cloud NoSQL database hosted by Amazon. You simply create a table and set the read/write capacity you want and Amazon takes care of the rest. No servers to manage or scale. Pretty awesome. Actually it’s so awesome that Amazon DynamoDB is the fastest growing new service in the history of AWS.

What’s is DynamoDB good at?

  • No hassle data store that is performant, scalable, and reliable.
  • Quick reads/writes. You decide the performance you need and pay for that.
  • Relatively cheap.
  • Access data via a key.
  • Store data that is in the 1-10KB range per item (you can store more but it gets expensive).

What’s does DynamoDB suck at?

  • It’s not a relational database. You can’t query it with joins or complex selects.
  • It’s not as fast as memcache (in our experience).
  • For large items or extremely high throughput it can get expensive (compared to running your own memcache service for example).
  • There is no simple way to back up your data (they do have a process by which you can get data to S3 but it’s pretty complicated and involves two other totally separate services from AWS).

Where can you read more about DynamoDB?

Using DynamoDB For Session Backup

In the blog post Scalable Session Handling in PHP Using Amazon DynamoDB they cover how to implement session handling for PHP using DynamoDB. I experimented with this, but what I found was it was too slow for our needs. Our session reads with DynamoDB were taking 20+ms. Our memcache session reads are an order of magnitude faster than that. We also already have memcache session handling implemented and working beautifully.

Why Do We Need To Backup Our Sessions?

For some applications it might be acceptable to log everyone out if you failed from one datacenter to another (heck lots of applications run in only one datacenter and don’t have any failover). Our application is mission critical for our customers – if we’re down they’re losing money. We run completely redundant setups in two different AWS regions on EC2.

If there’s a problem we have to switch customers from one datacenter to the other. Not having the sessions backed up in a way that both datacenters can access would mean everyone using our system would be logged out. This is a real hassle for our users because sometimes the stores are operating with a manager login and associates just have PINs to switch to their profile. If they get logged out they have to have the manager come by and log back in. What if the manager is out on lunch? Out of luck.

We previously stored our session backup in our MySQL database. But this became unscalable as the number of concurrent sessions grew. A few months ago we turned off our MySQL session storage and have been running just on memcache sessions. It improved performance, but it meant we might log everyone out if we had to switch datacenters.

DynamoDB To The Rescue

I looked at a number of different NoSQL type solutions for our session backup. DynamoDB made it to the top of the list because of it’s easy of management, scalability and price.

The basic concept is:

  • Session are stored and read from Memcache (every page hit)
  • Every 15 minutes we write the session to DynamoDB (each session stores its time since last DynamoDB write)
  • If we can’t find a session in Memcache (datacenter failure, or Memcache reboot) we look for it in DynamoDB.
  • Result: Users aren’t logged out if we switch datacenters or reboot Memcache.

DynamoDB PHP Code Samples

I gleaned a lot of this code from AWS blog post on PHP DynamoDB sessions.

readDynamoDB

This reads our session data out of DynamoDB when we need it. We call this from our custom session reading function (see session_set_save_handler)

function readDynamoDB($ses_id)
{
  $this->initDynamoDB();
  $result = '';
  $response = $this->_dynamodb->get_item(
    array( 'TableName' => self::DYNAMODB_TABLE,
           'Key' => array('HashKeyElement' => $this->_dynamodb->attribute($ses_id)),
           'ConsistentRead' => true, )
         );

  $node_name = 'Item';
  if ($response->isOK())
  {
    $item = array();
    // Get the data from the DynamoDB response
    if ($response->body->{$node_name})
    {
      foreach ($response->body->{$node_name}->children() as $key => $value)
      {
        $item[$key] = (string) current($value);
      }
    }
    if (isset($item['expires']) && isset($item['data']))
    {
      // Check the expiration date before using
      if ($item['expires'] > time())
      {
        $result = $item['data'];
      }
      else
      {
        $this->deleteDynamoDB($ses_id);
      }
    }
  }
  return $result;
}

writeDynamoDB

This reads our session data out of DynamoDB when we need it. We call this from our custom session reading function (see session_set_save_handler)

function writeDynamoDB($ses_id,$data,$expire_minutes)
{
  $this->initDynamoDB();
  // Write the session data to DynamoDB
  $response = $this->_dynamodb->put_item(
    array( 'TableName' => self::DYNAMODB_TABLE,
           'Item' => $this->_dynamodb->attributes(
             array( self::DYNAMODB_HASH => $ses_id,
                    'expires' => time() + ($expire_minutes*60),
                    'data' => $data,
                   )
            ),
    )
  );
  return $response->isOK();
}

deleteDynamoDB

This delete our session data from DynamoDB when we are done with it. We call this from our custom session destroy and gc function (see session_set_save_handler)

function deleteDynamoDB($ses_id)
{
  $this->initDynamoDB();
  $delete_options = array( 'TableName' => self::DYNAMODB_TABLE,
                           'Key' => array('HashKeyElement' => $this->_dynamodb->attribute($ses_id)),
                         );
  // Send the delete request to DynamoDB
  $response = $this->_dynamodb->delete_item($delete_options);
  return $response->isOK();
}

Customer Hero Hiring Process

We just finished up hiring for our Customer Hero position. I thought I’d break down our process. We’ve had some great results from the process and hopefully it’ll help you when you decide to hire your next employee(s).

The More People You Interview The Better

We’ve learned that the more people you interview the better your final candidates. Filtering based on resumes or applications down to just a select few gives random results. Instead we do a one pass filter using a written application and then we do a whole lot of phone interviews and quite a few in person interviews. Here’s what it looks like by the numbers:

  • 900 visitors to the customer hero job description and application. We posted to craigslist, Facebook, and twitter.
  • 158 people filled out our fairly lengthy application.
  • 79 applicants were invited to schedule a phone interview.
  • But only 64 actually scheduled phone interviews.
  • Out of 64 ten minute phone interviews we passed 21 candidates and invited them to interview in person.
  • Out of 20 in person interviews; 11 failed, 4 failed but were kept on a possible future hires list, 3 were asked back for a second in person interview, 1 was so outstanding they were hired on the spot.
  • We had a second in person interview and had a clear winner out of the 3 we asked back.

The Written Application

The first step is a written application. It helps us filter out people who:

  • Don’t write well.
  • Can’t find information on our website.
  • Expect a pay range that doesn’t align with the job.

We are pretty lenient with how we filter these application. As I said before we error on the side of interviewing more people over the phone.

The Phone Interview

This interview is very short. We give about 10 minutes for each interview. Often it’s over in 1 minute because the interviewee failed one of our first few questions.

We are looking for a few things in the phone interview:

  • Do they know who we are and what we do (at a high level)?
  • Excellent phone presence and personality.
  • Pay range, full time, when can they start (we don’t want to move on to the in person interview if any of these don’t line up).

See my blog post Why You Failed Your Phone Interview: The Harsh Truth from last year.

In Person Interview

This is a pretty quick interview like the phone interview. We ask pretty standard interview questions. What are we looking for in this interview?

  • Personality: I’m looking for someone that is easy to talk to, has a positive attitude, and generally makes people feel good.
  • Wants to do the job we are hiring for: We need to see that you’ll be happy doing the job we are hiring for. It’s great if you have ambitions about the future but we don’t want to hire someone that won’t be happy until they move up.
  • Isn’t in it just for the perks. Yes we have fun at MerchantOS. But if the only reason you’re interested in the job is because it’s a ‘hoot’ working here it doesn’t instill a lot of confidence that you can do a great job and work hard. We are a business.
  • Technical ability; we don’t have very strict technical knowledge/skill requirements but we do want you to be familiar with computers and technology in general.
  • Can take the heat. Doing phone support can be a stressful job. We want to see that you handle stress well. Everyone gets nervous in an in person interview, but some people handle that better than others.

At this point we either fail, hire, or ask back to a second in person interview.

Meet The Team: 2nd In Person Interview

If we can’t decide between a couple great candidates we’ll ask them back for another in person interview. In this interview we basically have the candidate go around to members of the support team and ask some questions (provided). We then get a sense for what the group thinks. Usually there is a clear preference for one candidate over the others from the group. This part of the process is more a “we could hire any of these people but we can only hire one” filter. I’m happy with who ever the group chooses.

A Long Process But We Get The Cream Of The Crop

This whole process took us 2 months: from posting the job to getting the new people in. That’s a long haul, but it’s worth it. In the end I know our support team is made up of some really awesome people. They are the top 5% (or better) of job seekers.

Welcome Aboard: Heather and Zach

We’ll introduce our new team members in more detail in a few weeks. But we are stoked to have Heather and Zach joining the team.

Want Our Interview Questions For Your Hiring Process?

I’m happy to share our interview questions and job application if you are hiring. I don’t like to share them on the web publicly because our next round of candidates may read them and distort our process. But I’m happy to share them with other employers.

Developers: API 2.0 Change On Wed 8/8

If you don’t know what an API is; ignore this and carry on.

If you’ve written an integration to the MerchantOS API or have had an outside developer write an integration take note! The API will change to version 2.0 this Wednesday night (8/8/12). Your integration may break if you are not prepared.

Here are the primary changes we are making in version 2.0 and how you can work around them to keep your current integration working:

  • shallow by default: Currently you can specify a shallow parameter on our API to have only the requested object, but none of its children or related objects returned. We are going to be setting this as the default behavior. To receive related objects you will need to specify a “load_relations” parameter with the related objects to load (see below).
  • readonly by default: By default all read (GET) requests will be using our readonly backend. For applications that are only reading or aggregating data there will be no changes. For applications that write data and then read that data out, be aware that there is a replication delay between our servers where the write requests (PUT, POST, DELETE) are processed and the servers where reads (GET) happen.
    • It is possible to override this behavior by setting the readonly parameter to false (readonly=false). Only do this if you need to read data immediately after writing it. Abuse of non-readonly reads can result in your application being rate limited or blacklisted.
    • Responses from writes are always realtime. When you write data you get the object that was created/updated back as a response. That data is always a real time response to your write. No need to worry about readonly=true.
  • load_relations search parameter: As mentioned above a new search parameter is being added that allows you to load related objects dynamically. Using this option you can load only the related objects that you need to get information from. All other related objects will not be loaded, allowing our API to respond to your request more quickly. load_relations=all will have the same behavior as the 1.0 version of the API.
  • JSON format changes: We are also improving our JSON emitter to improve its performance. In addition we are changing how single objects are returned in JSON format. Currently they are returned as bare objects. After the API version 2.0 changes they will be returned in an array the same way that multiple objects are returned. We hope that this change will simplify parsing JSON from our API.
    • We are also improving the layout of tags in our JSON responses so that they are not as verbose. Again we hope this will improve client parsing.

Workarounds and Fixes:

  • Version=1: The easiest work around is to set your version to 1. Adding a GET parameter of version=1 will accomplish this. You can also send a version header in the form of a specially formated Accept header like: application/vnd.merchantos-v1+xml (version 1 with xml output). Another example would be: application/vnd.merchantos-v2+json (version 2 with JSON output)
  • load_relations=all: Set load_relations=all as GET parameter to your query to load it with all relations just liket he default behavior in version 1.0. If you have an integration that needs to read a lot of data from us we recommend you do not use load_relations=all. Instead use load_relations=[set of relations you really need]. That will allow us to handle more of your requests.
  • readonly=false: If you need to write data and then read with those changes applied in real time set readonly=false as a GET parameter. As noted above this can get your application/IP rate limited or blocked if you abuse it.

OAuth 2.0

The API changes also include OAuth 2.0 for authentication. This allows us to set a per client rate limit. If you need a higher rate limit than 10 requests per minute you will need to implement OAuth 2.0 for your API authentication. We will then review your integration and increase your rate limit if you meet our quality standards.

Eventually we may phase out other authentication methods so it’s a good idea to implement OAuth 2.0 now. It also makes for a better user experience if you have end users setting up your integration (no more cutting and pasting API keys).

API Version 1.0 Sunset

We will sunset version 1.0 of the API when we release version 3.0. You should plan to make the changes necessary for version 2.0 compatibility even if you plan to use version 1.0 for now.

Why The Changes?

We’ve had some big challenges with our API around scaling. It’s easy for a programmer to write a loop and eat all our server resources. These changes are moving us towards a more scalable API.

Subscribe To The Developer Group

If you have an integration that uses our API, please subscribe to our Google Group.

Weekly Rewind: January 13, 2012

This week brought with it a flurry of activity. We expanded our head quarters, moving the Dev team into a cool new office downstairs. Keep an ear open and an eye out for fresh new developments and updates to the software!

In the coming week, Murdoc is going to tell us all about his holiday trip to Germany, and Chris is going to give us another installment of the Workaround series, this time he’ll be covering coupon items.

We had a party!

As you’ve read in recent posts, we’ve been hiring a lot this past year, doubling the size of the company, in fact. We recently had to rent more office space downstairs in order to accomodate the entire development team. In typical MerchantOS fashion, we had a party to celebrate the opening of the MerchantOS Annex!

You know it's for real when there's a sign.

You know it's for real when there's a sign.

Roderick, Justin, and Luke hard at play.

Roderick, Justin, and Luke hard at play.

Now that Kim Jong Il is gone, Murdoc is the most ruthless dictator in the world.

Now that Kim Jong Il is gone, Murdoc is the most ruthless dictator in the world.

Chris ever so gently removes a piece...

Chris ever so gently removes a piece...

...we hired him for his other great skills.

...we hired him for his other great skills.

A cooler, some brews, waves - perfection.

A cooler, some brews, waves - perfection.

Justin makes his move...

Justin makes his move...

Luke's office - complete with Cow-related pop art!

Luke's office - complete with Cow-related pop art!

Luke's desk. What else?

Luke's desk. What else?

You know it's for real when there's a sign.Roderick, Justin, and Luke hard at play.Now that Kim Jong Il is gone, Murdoc is the most ruthless dictator in the world.Chris ever so gently removes a piece......we hired him for his other great skills.A cooler, some brews, waves - perfection.Justin makes his move...Luke's office - complete with Cow-related pop art!Luke's desk. What else?

Now back to improving our point of sale software!

MerchantOS New Year’s Resolutions

This time of year is always a natural time to reflect on the past, and set goals for the future. In this vein, I’ve asked some of the folks at our company to think about their resolutions for MerchantOS in 2012.

A Look Back At 2011

We accomplished a lot this year and added a number of awesome folks to our team. In 2011 MerchantOS…

  • Reached the 1,000 customer mark. We now have over 1,300 retail locations using our point of sale software. Thank You!
  • Processed our One Billionth dollar in transactions. We averaged $1.4 milion per day in our point of sale system.
  • Added 7 full time employees, a half timer, and an intern.
  • Released 28 versions of our software with over 400 individual improvements and fixes.
  • 525,570 minutes of up time, 10 slow, 20 minutes of downtime for a subset of customers. 99.994% uptime overall.
  • Secured a bank loan for 2012 expansion.
  • 2 awesome parties, many ping pong matches, beer-pong games, and trips to the bar downstairs. Along with 2 babies born.

Seven 2012 Apocalyptic Resolutions

It may be the end of the world, but that doesn’t mean we can’t still make plans:

1. Focus on making our point of sale software awesome:

I want to take our point of sale to the next level. Making it easier and more pleasant to use while adding functionality. This is a difficult balancing act but we have a good plan for how to accomplish it and I’m excited about the challenge.

2. Have Fun:

I started this company because I didn’t like the other jobs I had. There weren’t fun. It wasn’t rewarding the way I wanted it to be. In 2012 I want to make sure that everyone that works here (and hopefully our customers) have some fun, enjoy their work, and feel valued.

3. Provide the best dang support:

We spent a great deal of effort searching for awesome support heroes to hire this year (adding Ciara, Ryan, Chris, Vanessa, and Jered). We try to treat our employees like we’d want to be treated ourselves. And we ask our employees to do the same for our customers. We have fun. We also take our jobs seriously and really care about our customers and the product we give them. That’s our recipe for awesome support.

4. Hire awesome people:

We are what the people who work here make us. If we want to be awesome we have to hire awesome. We’ve already got 2 (a programmer and designer) lined for the start of the year. We’ll probably add a few more before the year is over. I’m going to make sure we take the time to hire the right people.

5. Stock pile food, gold, and guns…

To fight the zombies. Oh we might also need this.

6. Sign up another 1,000 retail locations:

This is a high bar to reach for. With purely natural growth and without any change in our marketing strategy, I’d predict we will add somewhere around 700. How will we get those extra 300?

We have a bank loan so we can spend more on Adwords, that will help. We are optimizing our sign-up process. Working on our website marketing. We want to improve the look and feel of our application while we make it easier to use and more powerful. And I’ll just give you a hint there might be something with Mobile and web based accounting systems (no promises! It’s software development, nothing is guaranteed).

7. Build a product we are proud of.

Most of all in 2012 I want to build a product we are proud of. I want to write excellent code, create awesome user interfaces, build solid and reliable server and network infrastructure, and provide awesome support.

I’m Proud Of Our Employees, And Thankful To Our Customers

Without the great people that work for us and all our customers who support us, we wouldn’t be able to take on these goals. So thank you for making this all possible!

Best Wishes In 2012!

Weekly Rewind: December 12, 2011

It’s been kind of a slow holiday week here at MerchantOS with folks in and out on their holiday vacations. In the office it’s been steady, but not too busy. As I mentioned in earlier posts, we’ve been growing a lot, doubling the size of our team just this year. Our growth is starting to stress the capacity of our current offices, and as of the 1st, our Development team will be moving to an office on the floor below us.

On the blog front, there were a couple of cool posts this week. Like a lot of people, we took the opportunity provided by the refreshing of the calendar to take stock and lay out some goals for the coming year in our awesome New Year’s Resolutions post.

Another cool blog post was ‘Doing What We Can’, which detailed our, until now, secret corporate giving program. See, turns out that we’ve been giving away our software and customer support to a bunch of non-profits over the years. So I put together a little showcase of all those great organizations.

Check out those great blog posts, and stay tuned for more! We’ve got some exciting plans for awesome content in the New Year, and we’d love to hear input from you. If you’re a customer, what do you want to hear about? What’s helpful? Drop us a line!

Link Roundup

The end of the year is always fun on the interwebs because it brings a ton of fun ‘Best of’ and ‘Top 10′ content from everywhere. We love this stuff, and I pulled together some of the coolest content I could find for our final Weekly Rewind Link Roundup of 2011. Enjoy, and Happy New Year!

Happy New Year!

Have a safe and fun weekend!

Doing what we can

The MerchantOS Corporate Giving Program

I recently learned that for about five years now, MerchantOS has been quietly giving it’s software away to non-profit organizations all over the country that do good work in their local communities. We have no personal connection to them, we just like that they’re giving back, and like the idea that we can support their work. Since it’s my job to highlight all the cool things we do at MOS, I put together the following showcase, partly to show off the cool orgs we support, and partly to give props to VP Christina Gilpin, who originally thought up the idea, and ran with it, right after she was hired five years ago. I caught up with Christina, and asked her how the idea originated in the first place. She said it just seemed natural -

“…these non profits do so much for their communities…and if we can help them be even that much more successful, then that is just awesome” Continue reading