Showing posts with label perl. Show all posts
Showing posts with label perl. Show all posts

Monday, April 25, 2011

Damn Data::UUID

The Data::UUID module has the annoying behavior that it will not fail if you provide an invalid namespace when creating a v3 UUID. It will generate a UUID, but depending on the circumstances it may generate a different UUID in subsequent calls. Consider the following example:
#!/usr/bin/perl

use strict;
use warnings;

use Data::UUID;
use UUID::Tiny;

sub v3_data_uuid {
    my $namespace = shift;
    my $name = shift;
    my $ug = shift;
    $ug = new Data::UUID unless defined $ug; 
    return lc($ug->create_from_name_str($namespace, $name));
}

sub v3_uuid_tiny {
    my $namespace = shift;
    my $name = shift;
    return UUID_to_string(create_UUID(UUID_V3, $namespace, $name));
}

# Generate a v3 UUID using Data::UUID
print "Data::UUID\n";
my $ug = new Data::UUID;
print '  1. ', lc($ug->create_from_name_str(UUID_NS_DNS, 'abc')), "\n";
print '  2. ', lc($ug->create_from_name_str(UUID_NS_DNS, 'abc')), "\n";
print '  3. ', v3_data_uuid(UUID_NS_DNS, 'abc'), "\n";
print '  4. ', v3_data_uuid(UUID_NS_DNS, 'abc'), "\n";
print '  5. ', v3_data_uuid(UUID_NS_DNS, 'abc', $ug), "\n";

# Generate a v3 UUID using UUID::Tiny
print "UUID::Tiny\n";
print '  1. ', UUID_to_string(create_UUID(UUID_V3, UUID_NS_DNS, 'abc')), "\n";
print '  2. ', UUID_to_string(create_UUID(UUID_V3, UUID_NS_DNS, 'abc')), "\n";
print '  3. ', v3_uuid_tiny(UUID_NS_DNS, 'abc'), "\n";
print '  4. ', v3_uuid_tiny(UUID_NS_DNS, 'abc'), "\n";

# Generate a v3 UUID using Data::UUID with an invalid namespace
print "Data::UUID - bad namespace\n";
my $namespace = 'namespace';
print '  1. ', lc($ug->create_from_name_str($namespace, 'abc')), "\n";
print '  2. ', lc($ug->create_from_name_str($namespace, 'abc')), "\n";
print '  3. ', v3_data_uuid($namespace, 'abc'), "\n";
print '  4. ', v3_data_uuid($namespace, 'abc'), "\n";
print '  5. ', v3_data_uuid($namespace, 'abc', $ug), "\n";

# Generate a v3 UUID using UUID::Tiny with an invalid namespace
print "UUID::Tiny - bad namespace\n";
print '  1. ', UUID_to_string(create_UUID(UUID_V3, $namespace, 'abc')), "\n";
This example also uses UUID::Tiny as a point of comparison. The output that I get when running this example is:
$ ./uuid.pl 
Data::UUID
  1. 1fc38bb9-aae5-3dba-9894-38925088c9c0
  2. 1fc38bb9-aae5-3dba-9894-38925088c9c0
  3. 1fc38bb9-aae5-3dba-9894-38925088c9c0
  4. 1fc38bb9-aae5-3dba-9894-38925088c9c0
  5. 1fc38bb9-aae5-3dba-9894-38925088c9c0
UUID::Tiny
  1. 5bd670ce-29c8-3369-a8a1-10ce44c7259e
  2. 5bd670ce-29c8-3369-a8a1-10ce44c7259e
  3. 5bd670ce-29c8-3369-a8a1-10ce44c7259e
  4. 5bd670ce-29c8-3369-a8a1-10ce44c7259e
Data::UUID - bad namespace
  1. 15da4c6e-29ae-3f6c-a7ed-ec36373d4e5d
  2. 15da4c6e-29ae-3f6c-a7ed-ec36373d4e5d
  3. 8fa130be-2517-3e29-a087-c7d75545a62c
  4. 8fa130be-2517-3e29-a087-c7d75545a62c
  5. 8fa130be-2517-3e29-a087-c7d75545a62c
UUID::Tiny - bad namespace
UUID::Tiny::string_to_uuid(): 'namespace' is no UUID string! at ./uuid.pl line 50
When a valid UUID is used for the namespace, Data::UUID consistently generates the same value. However, if I pass in a bad value such as the string literal namespace, then I get different values depending on how/where/when it is called. UUID::Tiny on the other hand dies with a nice error message telling me exactly what is wrong.

Saturday, October 9, 2010

Damn Java Socket Exception Messages

When creating an error message you should think about what information would be useful for understanding what went wrong. This should especially be true if you are creating a library that is likely to be used by many other systems. Providing good error messages up front means that even if the programmers using the library do not check and customize the messages, the user will still get a reasonable result. For some use cases, such as scripting, it can also be useful because it may be a quick one-off program where the goal is to quickly automate a repetitive task or perform some analysis. In this case, having useful default error messages can speed up the initial development so you get your answer faster.

In my case, I was checking logs for a system that crawls pages and hence attempts to resolve and connect to thousands of hosts. This system logs the exceptions, but unfortunately did not provide a customized message when doing so. Analyzing the logs showed that the two most common error messages were: 1) a failure to resolve the hostname and 2) failing to connect to an HTTP server on the host. An example error message for the first case is java.net.UnknownHostException: some-host-that-does-not-exist. This message is quite useful as the exception name explains the problem and the message tells me the name of the host that could not be resolved. An example message for the second case is java.net.SocketTimeoutException: connect timed out. This message is explains the problem, but doesn't given me the crucial information of what it was trying to connect to.

Though this can easily be fixed in the application code, it is disappointing that the default message is so bad. I have noticed that my opinion of a programming language or technology seems to go down steadily the longer I am forced to use it at work. Is the grass greener on one of the other sides? How do other languages, or rather the networking libraries they provide, fair for this use case? I looked at 13 options to see how many would give a decent error message for both use cases. The results were not very encouraging. Only one option, Go, had reasonable messages for both. For the host not found case 4 options included the hostname. Only two options provided the host and port in the failed to connect case. The results are summarized in the table below the fold with links to the source code and raw error messages.

Thursday, April 8, 2010

Finding the Location of Perl Modules

There are (at least) two simple ways of finding the location on the file system of a perl module. The first is to use the perldoc command.
$ perldoc -l CPAN
/usr/lib/perl5/5.8.5/CPAN.pm
$ perldoc -l Pod::Perldoc
No documentation found for "Pod::Perldoc".
The second way is more robust, but also a little more verbose. Just use a perl one liner that uses the module and then looks up the path in the %INC variable.
$ perl -MCPAN -e 'print $INC{"CPAN.pm"}, "\n"'
/usr/lib/perl5/5.8.5/CPAN.pm
$ perl -MPod::Perldoc -e 'print $INC{"Pod/Perldoc.pm"}, "\n"'
/usr/lib/perl5/5.8.5/Pod/Perldoc.pm