Friday, 10 August 2012

Perl, Floats, and Memcached

Oh Perl, I love you, but sometimes you do drive me nuts.

The Problem:  Consider the following code:
use strict;
use JSON::XS;
use Cache::Memcached;
use Data::Dumper;
my $memd = new Cache::Memcached { 'servers' => [ "127.0.0.1:11211"], };
my $x={ "f" => [[134,1.7],[134,2]] };
print "before: \n";
print encode_json($x);
print "\n";
$memd->set("key",$x);
print "after: \n";
print encode_json($x);
print "\n";
You get this as the output:
before: 
{"f":[[134,1.7],[134,2]]}
after: 
{"f":[[134,"1.7"],[134,2]]}
Now, why is 1.7 now a string after the set() call? It's the same object I am encoding to json. So the serialization process in memcached's set() has stringified the float 1.7 into "1.7".

You can actually replicate the same behaviour without memcached entirely, if you just do a Dumper($x) in between, the after Dumper() $x will have 1.7 as a string instead of a float.

I know Perl does not have strict typing, and that you can add 0 to a string (or multiply by 1) to make a variable into numeric representation. However, the above is definitely a problem.

The Solution, is to use Storable module and freeze the object $x before the store, and then of course, thaw $x when you retrieve it back out. See below:
use strict;
use Storable qw(freeze thaw);
use Cache::Memcached;
use JSON::XS;
my $memd = new Cache::Memcached { 'servers' => [ "127.0.0.1:11211"], };
my $x={ "foo" => [[134,1.7],[134,2]] };
print "before: \n";
print encode_json($x);
print "\n";
$memd->set("key", freeze($x));
print "after: \n";
print encode_json($x);
print "\n";
my $out=$memd->get("key");
print "thawed: \n";
print encode_json(thaw($out));
print "\n";
print "memcached version: ",$Cache::Memcached::VERSION,"\n";
print "json xs version: ",$JSON::XS::VERSION,"\n";
print "storable version: ",$Storable::VERSION,"\n";
You get this as the output:
before: 
{"foo":[[134,1.7],[134,2]]}
after: 
{"foo":[[134,1.7],[134,2]]}
thawed: 
{"foo":[[134,1.7],[134,2]]}
memcached version: 1.30
json xs version: 2.26
storable version: 2.25
Voila! Everything stays as it is!