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

  • Thread starter Iñaki Baz Castillo
  • Start date
I

Iñaki Baz Castillo

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 rep=
ly.


An example usage:

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

query =3D resolver.submit_A "google.com"

query.callback do |result|
puts "result =3D> #{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 li=
ke:

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.

--=20
I=C3=B1aki Baz Castillo
<[email protected]>
 
C

Christopher Dicely

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:

=C2=A0class EM::Udns::ErrorNoDomain < =C2=A0EM::Udns::Error ; end
=C2=A0class EM::Udns::ErrorNoData =C2=A0 =C2=A0 < =C2=A0EM::Udns::Error ;= end
=C2=A0class EM::Udns::ErrorTempFail =C2=A0< =C2=A0EM::Udns::Error ; end


or I could extend Errno module:

=C2=A0class Errno::DnsNoDomain ; end
=C2=A0class Errno::DnsNoData =C2=A0 =C2=A0 ; end
=C2=A0class Errno::DnsTempail =C2=A0 =C2=A0; 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.
 
I

Iñaki Baz Castillo

2011/5/1 Christopher Dicely said:
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:

https://github.com/ibc/em-udns

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).



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 :)


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.


--=20
I=C3=B1aki Baz Castillo
<[email protected]>
 
C

Christopher Dicely

2011/5/1 Christopher Dicely said:
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:

=C2=A0https://github.com/ibc/em-udns

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.
 
I

Iñaki Baz Castillo

2011/5/1 Christopher Dicely said:
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) :)

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.

--=20
I=C3=B1aki Baz Castillo
<[email protected]>
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,818
Latest member
Brigette36

Latest Threads

Top