Is it usual/valid to extend custom classes under Errno module?

Hi, I've coded a DNS library. When a DNS query fails my library
doesn't raise an exception (as it's expensive) but instead returns a
Symbol (more efficient):

- :dns_error_nxdomain - The domain name does not exist.
- :dns_error_nodata - There is no data of requested type found.
- :dns_error_tempfail - Temporary error, the resolver nameserver was
not able to process our query or timed out.
- :dns_error_protocol - Protocol error, a nameserver returned malformed reply.

An example usage:

···

-----------------------------------------------------------
resolver = EM::Udns::Resolver.new
EM::Udns.run resolver

query = resolver.submit_A "google.com"

query.callback do |result|
  puts "result => #{result.inspect}"
end

query.errback do |error|
  case error
  when :dns_error_nxdomain
    # do something
  when :dns_error_nodata
    # do something
  when :dns_error_tempfail
    # do something
  end
end
--------------------------------------------------------------

So returning a Symbol (in case of failure) is good as I can use it
within a case/when statement.

Another possibility would be returning an instance of a class, something like:

  class EM::Udns::ErrorNoDomain < EM::Udns::Error ; end
  class EM::Udns::ErrorNoData < EM::Udns::Error ; end
  class EM::Udns::ErrorTempFail < EM::Udns::Error ; end

or I could extend Errno module:

  class Errno::DnsNoDomain ; end
  class Errno::DnsNoData ; end
  class Errno::DnsTempail ; end

In your opinnion, which is the most elegant way? any other suggestion?

Thanks a lot.

--
Iñaki Baz Castillo
<ibc@aliax.net>

Hi, I've coded a DNS library. When a DNS query fails my library
doesn't raise an exception (as it's expensive) but instead returns a
Symbol (more efficient):

This usually is a bad idea since performance on errors isn't really
something you usually need to optimize, and it ends up making the code
calling your library have to do ugly things like check return values
for errors.

So returning a Symbol (in case of failure) is good as I can use it
within a case/when statement.

Another possibility would be returning an instance of a class,
something like:

class EM::Udns::ErrorNoDomain < EM::Udns::Error ; end
class EM::Udns::ErrorNoData < EM::Udns::Error ; end
class EM::Udns::ErrorTempFail < EM::Udns::Error ; end

or I could extend Errno module:

class Errno::DnsNoDomain ; end
class Errno::DnsNoData ; end
class Errno::DnsTempail ; end

The Errno module provides a Ruby-ish way of dealing with OS error
values; so it doesn't really make sense for this use.

In your opinnion, which is the most elegant way? any other
suggestion?

The most elegant way is to raise an exception (which, ideally, should
be an instance of a unique exception class for each of your error
conditions.) This makes the code calling your library functions
cleaner, since it doesn't have to check return values for errors,
allowing a cleaner separation before the normal path and the error
path.

···

On Fri, Apr 22, 2011 at 11:15 AM, Iñaki Baz Castillo <ibc@aliax.net> wrote:

Hi, I've coded a DNS library. When a DNS query fails my library
doesn't raise an exception (as it's expensive) but instead returns a
Symbol (more efficient):

This usually is a bad idea since performance on errors isn't really
something you usually need to optimize, and it ends up making the code
calling your library have to do ugly things like check return values
for errors.

Hi, in my tests raising and capturing an exception is 100 times more
expensive (or more) than returning a value. Also take into account
that my DNS library works on top of EventMachine:

  GitHub - ibc/em-udns: An async DNS resolver for EventMachine based on the udns C library

So it's asyncrhonous and non-blocking. This means that ater doing a
DNS query I get the result in a callback (a callback and a errback in
case the domain or the resource record doesn't exist). This means
that, even if I want, I cannot raise an exception.

I use this DNS library in a SIP server on top of EventMachine I'm
coding. A SIP server receives lots of requests (i.e. telephony calls)
from many clients and, depending the scenario, for each request the
server must perform up to 3 DNS queries (NAPTR, SRV and A or AAAA). In
my tests, raising an exception is ~50 times slower than processing a
SIP request by my SIP server, so it would become an easy DoS attack
(in case a attacker sends lots of requests for a non existing domain).

or I could extend Errno module:

class Errno::DnsNoDomain ; end
class Errno::DnsNoData ; end
class Errno::DnsTempail ; end

The Errno module provides a Ruby-ish way of dealing with OS error
values; so it doesn't really make sense for this use.

Ok, discarded then :slight_smile:

In your opinnion, which is the most elegant way? any other
suggestion?

The most elegant way is to raise an exception (which, ideally, should
be an instance of a unique exception class for each of your error
conditions.) This makes the code calling your library functions
cleaner, since it doesn't have to check return values for errors,
allowing a cleaner separation before the normal path and the error
path.

But it's not valid in EventMachine and its efficience is really bad. I
must return a value, maybe a Symbol or maybe an instance of a error
class, i.e:

  class EM::Udns::ErrorNoDomain < EM::Udns::Error ; end
  class EM::Udns::ErrorNoData < EM::Udns::Error ; end
  class EM::Udns::ErrorTempFail < EM::Udns::Error ; end

Thanks a lot.

···

2011/5/1 Christopher Dicely <cmdicely@gmail.com>:

On Fri, Apr 22, 2011 at 11:15 AM, Iñaki Baz Castillo <ibc@aliax.net> wrote:

--
Iñaki Baz Castillo
<ibc@aliax.net>

Hi, I've coded a DNS library. When a DNS query fails my library
doesn't raise an exception (as it's expensive) but instead returns a
Symbol (more efficient):

This usually is a bad idea since performance on errors isn't really
something you usually need to optimize, and it ends up making the code
calling your library have to do ugly things like check return values
for errors.

Hi, in my tests raising and capturing an exception is 100 times
more expensive (or more) than returning a value.

That's not surprising. Still, unless its really not an exceptional
case, an exception is usually better in library code.

Also take into account that my DNS library works on top of
EventMachine:

GitHub - ibc/em-udns: An async DNS resolver for EventMachine based on the udns C library

So it's asyncrhonous and non-blocking. This means that ater doing a
DNS query I get the result in a callback (a callback and a errback in
case the domain or the resource record doesn't exist). This means
that, even if I want, I cannot raise an exception.

If you are writing an asynchronous, non-blocking DNS library on top of
EventMachine, and returning results the same way you get them back
(via callbacks), then, yeah, raising an exception is just going to
cause all kinds of problems.

You could probably return (not raise) an exception (have a separate
Exception class for each broad type of errors, and populated it with
the appropriate details; this seems to be approximately what your
Errno based approach would do -- Errno, after all, is a descendant of
Exception) in your error callbacks.

OTOH, if you aren't going to be providing any additional information
other than the general type of error, either a symbol or an opaque
constant defined in your library works just as well.

···

On Sun, May 1, 2011 at 5:22 AM, Iñaki Baz Castillo <ibc@aliax.net> wrote:

2011/5/1 Christopher Dicely <cmdicely@gmail.com>:

On Fri, Apr 22, 2011 at 11:15 AM, Iñaki Baz Castillo <ibc@aliax.net> wrote:

If you are writing an asynchronous, non-blocking DNS library on top of
EventMachine, and returning results the same way you get them back
(via callbacks), then, yeah, raising an exception is just going to
cause all kinds of problems.

Yes, non-blocking paradigm breaks all the rules (joking) :slight_smile:

You could probably return (not raise) an exception (have a separate
Exception class for each broad type of errors, and populated it with
the appropriate details; this seems to be approximately what your
Errno based approach would do -- Errno, after all, is a descendant of
Exception) in your error callbacks.

OTOH, if you aren't going to be providing any additional information
other than the general type of error, either a symbol or an opaque
constant defined in your library works just as well.

Thanks. The C library on top of my Ruby library (udns) returns 3-4
kinds of error, and being DNS they are self-descriptive (no more info
is required). Basically "domain not found", "resource record not
found", "malformed response", "timeout", so for now I'm done with 4
Symbols. But yes, perhaps I will change and return an instance of a
custom error class (in fact the same instance always, the one
generated within the Ruby C extension, as no more info is required in
the error).

Thanks a lot.

···

2011/5/1 Christopher Dicely <cmdicely@gmail.com>:

--
Iñaki Baz Castillo
<ibc@aliax.net>