Proper way to suppress die from DBIx::Class

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Proper way to suppress die from DBIx::Class

Sean Quinlan
Greetings all!

Our application has its own internal exception handling that we would like to be able to use when interacting with the database through DBIx::Class. I would prefer to do this in a way that does not require using evals all over!

Currently we have a role used throughout our app to manage db connections, and it would be simple to add a HandleError to the connection to trigger our error handling, which works fine for that part in testing. But I would rather not do something that also requires me to set 'unsafe' to true within DBIx::Class. I saw there is an 'exception_action' available on schema objects. But testing using that turn up 'errors' for lots of things that are really just warnings (from my applications point of view), and not just the subset of issues that cause a fatal error.

And regardless, no matter what I try to do, I don't seem to be able to suppress the die! Setting RaiseError to 0 and unsafe to 1 on connection does not appear to have any impact (though the documentation seemed to indicate it would). Returning true (or false) from my HandleError has no impact. Using exception_action with or without adjusting RaiseError or unsafe has no impact. Setting RaiseError on each resultset request has no impact. I _think_ I've tried every combination of documented options I found. Is there really no way whatsoever to change this behavior?

The HandleError I want to set on connections basically amounts to:
sub { $self->add_error($_[0]) }

In the application, while we try to validate inputs up front to limit potential issues, nothing is perfect. Inside the application, a common pattern is to test for errors after return from a code branch. Frequently we simply: return if $self->has_errors. Ultimately our response rendering examines itself for errors and updates the response for serialization appropriately. This removes _huge_ amounts of boilerplate. In complex cases we might execute some code before returning if errors had been registered (extra logging, queue some later job, etc). Wrapping every db interaction in eval, just in case, adds bunch of repetitive code that adds no value to this applications process.

To reiterate, my ideal solution allows me to:
1) Invoke our applications error handling ($self->add_error()) when 'fatal' errors occur.
2) Suppress die'ing on SQL errors, so our standard has_error's checks work
3) Does not require setting unsafe to true, nor block DBIX:Class's internal use of throw_exception

Is this possible? If there is documentation or examples we've missed, please do direct me to it, and I will RTFM. ;) 

If not all of the above is possible, are there known stable work-arounds, or recommended practices, that functionally achieve what I want to do in perhaps a slightly different way?

Much appreciated!

-Sean 

_______________________________________________
List: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/dbix-class
IRC: irc.perl.org#dbix-class
SVN: http://dev.catalyst.perl.org/repos/bast/DBIx-Class/
Searchable Archive: http://www.grokbase.com/group/dbix-class@...
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Proper way to suppress die from DBIx::Class

Sean Quinlan
The end result I want is to not have to wrap every update / insert / etc with an eval.

I have no problem if DBIC internals use exception-based control internally. I just want to optionally have it not bubble up to the level of my application. It would be far more elegant if I could have DBIC play nicely with our applications exception handling, rather than have to insulate every usage manually.

-Thanks.
Sean



On Sat, Oct 4, 2014 at 2:52 PM, Peter Rabbitson <[hidden email]> wrote:
On 10/01/2014 08:51 PM, Sean Quinlan wrote:
Greetings all!

Our application has its own internal exception handling that we would
like to be able to use when interacting with the database through
DBIx::Class. I would prefer to do this in a way that does not require
> ...
using evals all over >>
And regardless, no matter what I try to do, I don't seem to be able to
suppress the die! Setting RaiseError to 0 and unsafe to 1 on connection
does not appear to have any impact (though the documentation seemed to
indicate it would).


I am not sure I understand the exact goal you are pursuing here. Note that trapping and *neutralizing* all exceptions is not possible - DBIC internals use exception-based control flow at places.

Can ou please step away from describing the exact implementation you are trying to achieve, and re-explain what is the *end result* you are after.

Thanks!


_______________________________________________
List: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/dbix-class
IRC: irc.perl.org#dbix-class
SVN: http://dev.catalyst.perl.org/repos/bast/DBIx-Class/
Searchable Archive: http://www.grokbase.com/group/dbix-class@...


_______________________________________________
List: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/dbix-class
IRC: irc.perl.org#dbix-class
SVN: http://dev.catalyst.perl.org/repos/bast/DBIx-Class/
Searchable Archive: http://www.grokbase.com/group/dbix-class@...
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Proper way to suppress die from DBIx::Class

Sean Quinlan
Thanks!

I would have prefered to _not_ have to set unsafe, as it would be great if DBIC continued to handle exceptions internally as always, but just didn't pass the exception out after catching and handling it internally.

And I appreciate the 'architecturally unsound' warning, and would agree in general. The only reason we're doing it this way is that we already have exception handling built in to the application. The idea here is not to just skip handling exceptions, but rather automate propagating exceptions up to the application level, without having to hand-wrap all calls in eval. So if handle_error in DBI calls our applications add_error() (_without_ blocking or altering DBIC's), then the die on call is redundant, as the apps existing error handling will pick it up.

Actually, if you wanted to encourage die or handle as an explicit decision and still discourage not handling it at all, perhaps a new optional argument to connect to provide a callback for external error handling, which when provided sets DBIC to not propagate fatal errors to the application? exception_action seemed to come close, but was triggered too often and did not suppress the die.


Please feel free to email me directly if there is anything I can do to help or questions I can answer. I'd also of course be happy to test any updates.

-Cheers,
Sean

On Wed, Oct 15, 2014 at 12:01 PM, Peter Rabbitson <[hidden email]> wrote:
On 10/15/2014 04:55 PM, Sean Quinlan wrote:
The end result I want is to not have to wrap every update / insert / etc
with an eval.

I have no problem if DBIC internals use exception-based control
internally. I just want to optionally have it not bubble up to the level
of my application. It would be far more elegant if I could have DBIC
play nicely with our applications exception handling, rather than have
to insulate every usage manually.


This is a reasonable (albeit architecturally unsound) request. If the 'unsafe' handling is requested everything not happening in a transaction should indeed not result in exceptions.

In order to go forward with this I will need a modest set of lives_ok checks for various operations. The actual implementation will be more involved and will likely be carried out by me or another core contrib. For a concise test example, take a look at
https://github.com/dbsrgits/dbix-class/blob/current/for_cpan_index/t/schema/anon.t

Let me know if you have any further questions

Cheers!


_______________________________________________
List: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/dbix-class
IRC: irc.perl.org#dbix-class
SVN: http://dev.catalyst.perl.org/repos/bast/DBIx-Class/
Searchable Archive: http://www.grokbase.com/group/dbix-class@...
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Proper way to suppress die from DBIx::Class

Lasse Makholm


On Wed, Oct 15, 2014 at 7:11 PM, Sean Quinlan <[hidden email]> wrote:
Thanks!

I would have prefered to _not_ have to set unsafe, as it would be great if DBIC continued to handle exceptions internally as always, but just didn't pass the exception out after catching and handling it internally.

And I appreciate the 'architecturally unsound' warning, and would agree in general. The only reason we're doing it this way is that we already have exception handling built in to the application. The idea here is not to just skip handling exceptions, but rather automate propagating exceptions up to the application level, without having to hand-wrap all calls in eval. So if handle_error in DBI calls our applications add_error() (_without_ blocking or altering DBIC's), then the die on call is redundant, as the apps existing error handling will pick it up.

I'm really struggling to understand this reasoning, so forgive me if I come across as telling you that you're doing it all wrong...

Surely exceptions are always propagated up through the call stack to the nearest catch block and since DBIC does not catch exceptions for you, you are free to put a try/catch pair as high or as low in the call stack as you want. The whole point of structured exception handling is to let you put error handling where you want it instead of around every call that might possibly go wrong, no?

A try/catch pair in your dispatcher or entry point to your response handler should be a trivial thing to implement and would take care of meaningful responses in case of exceptions. Assuming no other try/catch pairs in the code path for a given request, that would still, of course, abort execution after the first error. But you want it to continue, right? Is that the crux of the matter?

I guess you have your reasons wanting to do that but I can think of any number of reasons (ranging from simply returning incorrect data to deleting the wrong data or worse) why this approach is dangerous. And I guess that's what Peter means by "architecturally unsound"...

Given a chunk of code that does multiple inserts/updates - do you really want it to just blindly continue, even after something has gone wrong?

I'm curious about what sort of application would want that kind of unsafe-by-default behaviour...
 
Actually, if you wanted to encourage die or handle as an explicit decision and still discourage not handling it at all, perhaps a new optional argument to connect to provide a callback for external error handling, which when provided sets DBIC to not propagate fatal errors to the application? exception_action seemed to come close, but was triggered too often and did not suppress the die.
 
You probably know this already but in case you don't: With DBIC it's a relatively trivial thing to create your own base class for all resultsets and all result classes in your schema. In those two classes, overriding insert(), update() and a handful of other methods would probably get you what you want. It's of course more work than just flipping a switch in DBIC, but it's a lot less work than changing every place you call into DBIC. E.g. assuming a property on your schema that knows what to do with exceptions, something like:

sub update
{
my $self = shift;
return try {
$self->next::method(@_);
} catch {
$self->result_source->schema->some_context_thingy->add_error($_);
};
}

/L

Please feel free to email me directly if there is anything I can do to help or questions I can answer. I'd also of course be happy to test any updates.

-Cheers,
Sean

On Wed, Oct 15, 2014 at 12:01 PM, Peter Rabbitson <[hidden email]> wrote:
On 10/15/2014 04:55 PM, Sean Quinlan wrote:
The end result I want is to not have to wrap every update / insert / etc
with an eval.

I have no problem if DBIC internals use exception-based control
internally. I just want to optionally have it not bubble up to the level
of my application. It would be far more elegant if I could have DBIC
play nicely with our applications exception handling, rather than have
to insulate every usage manually.


This is a reasonable (albeit architecturally unsound) request. If the 'unsafe' handling is requested everything not happening in a transaction should indeed not result in exceptions.

In order to go forward with this I will need a modest set of lives_ok checks for various operations. The actual implementation will be more involved and will likely be carried out by me or another core contrib. For a concise test example, take a look at
https://github.com/dbsrgits/dbix-class/blob/current/for_cpan_index/t/schema/anon.t

Let me know if you have any further questions

Cheers!


_______________________________________________
List: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/dbix-class
IRC: irc.perl.org#dbix-class
SVN: http://dev.catalyst.perl.org/repos/bast/DBIx-Class/
Searchable Archive: http://www.grokbase.com/group/dbix-class@...


_______________________________________________
List: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/dbix-class
IRC: irc.perl.org#dbix-class
SVN: http://dev.catalyst.perl.org/repos/bast/DBIx-Class/
Searchable Archive: http://www.grokbase.com/group/dbix-class@...
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Proper way to suppress die from DBIx::Class

Lasse Makholm
In reply to this post by Sean Quinlan

On Wed, Oct 15, 2014 at 7:11 PM, Sean Quinlan <[hidden email]> wrote:
Thanks!

I would have prefered to _not_ have to set unsafe, as it would be great if DBIC continued to handle exceptions internally as always, but just didn't pass the exception out after catching and handling it internally.

And I appreciate the 'architecturally unsound' warning, and would agree in general. The only reason we're doing it this way is that we already have exception handling built in to the application. The idea here is not to just skip handling exceptions, but rather automate propagating exceptions up to the application level, without having to hand-wrap all calls in eval. So if handle_error in DBI calls our applications add_error() (_without_ blocking or altering DBIC's), then the die on call is redundant, as the apps existing error handling will pick it up.

Actually, if you wanted to encourage die or handle as an explicit decision and still discourage not handling it at all, perhaps a new optional argument to connect to provide a callback for external error handling, which when provided sets DBIC to not propagate fatal errors to the application? exception_action seemed to come close, but was triggered too often and did not suppress the die.

Thinking about this, something like an internal_exception_action and an external_exception_action might be a useful distinction. The current behaviour of (internal) exception_action is useful because it allows us to capture the full amount of information about the origin of errors (stack, args, etc...) and use custom exception classes but is, as Sean points out, ill-suited for making errors non-fatal.

An external_exception_behaviour, on the other hand, would be useful for facilitating application defined error behaviour. Especially if the return value of such a hook would propagate back out to the caller of the failed DBIC method. Think:

In schema class:

__PACKAGE__->external_exception_action(sub { return MyApp::DBICError->new(@_) });

Elsewhere:

my $rs = $rsrc->search(....);
my @results = $rs->isa('MyApp::DBICError') ? () : $rs->all;

/L

Please feel free to email me directly if there is anything I can do to help or questions I can answer. I'd also of course be happy to test any updates.

-Cheers,
Sean

On Wed, Oct 15, 2014 at 12:01 PM, Peter Rabbitson <[hidden email]> wrote:
On 10/15/2014 04:55 PM, Sean Quinlan wrote:
The end result I want is to not have to wrap every update / insert / etc
with an eval.

I have no problem if DBIC internals use exception-based control
internally. I just want to optionally have it not bubble up to the level
of my application. It would be far more elegant if I could have DBIC
play nicely with our applications exception handling, rather than have
to insulate every usage manually.


This is a reasonable (albeit architecturally unsound) request. If the 'unsafe' handling is requested everything not happening in a transaction should indeed not result in exceptions.

In order to go forward with this I will need a modest set of lives_ok checks for various operations. The actual implementation will be more involved and will likely be carried out by me or another core contrib. For a concise test example, take a look at
https://github.com/dbsrgits/dbix-class/blob/current/for_cpan_index/t/schema/anon.t

Let me know if you have any further questions

Cheers!


_______________________________________________
List: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/dbix-class
IRC: irc.perl.org#dbix-class
SVN: http://dev.catalyst.perl.org/repos/bast/DBIx-Class/
Searchable Archive: http://www.grokbase.com/group/dbix-class@...


_______________________________________________
List: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/dbix-class
IRC: irc.perl.org#dbix-class
SVN: http://dev.catalyst.perl.org/repos/bast/DBIx-Class/
Searchable Archive: http://www.grokbase.com/group/dbix-class@...
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Proper way to suppress die from DBIx::Class

Sean Quinlan
In reply to this post by Lasse Makholm


On Wed, Oct 15, 2014 at 6:16 PM, Lasse Makholm <[hidden email]> wrote:


On Wed, Oct 15, 2014 at 7:11 PM, Sean Quinlan <[hidden email]> wrote:
Thanks!

I would have prefered to _not_ have to set unsafe, as it would be great if DBIC continued to handle exceptions internally as always, but just didn't pass the exception out after catching and handling it internally.

And I appreciate the 'architecturally unsound' warning, and would agree in general. The only reason we're doing it this way is that we already have exception handling built in to the application. The idea here is not to just skip handling exceptions, but rather automate propagating exceptions up to the application level, without having to hand-wrap all calls in eval. So if handle_error in DBI calls our applications add_error() (_without_ blocking or altering DBIC's), then the die on call is redundant, as the apps existing error handling will pick it up.

I'm really struggling to understand this reasoning, so forgive me if I come across as telling you that you're doing it all wrong...

Surely exceptions are always propagated up through the call stack to the nearest catch block and since DBIC does not catch exceptions for you, you are free to put a try/catch pair as high or as low in the call stack as you want. The whole point of structured exception handling is to let you put error handling where you want it instead of around every call that might possibly go wrong, no?

Exactly!!!! My issue has been finding the right way to do that "instead of around every call that might possibly go wrong"! In straight DBI I would write a custom HandleError and set RaiseError to 0. Simple, done, I now control error handling. But I can't do that with DBIC.

 

A try/catch pair in your dispatcher or entry point to your response handler should be a trivial thing to implement and would take care of meaningful responses in case of exceptions. Assuming no other try/catch pairs in the code path for a given request, that would still, of course, abort execution after the first error. But you want it to continue, right? Is that the crux of the matter?

No, the crux of the matter is I _want_ something like:
    $row->update({col => "value"});
    return if $self->has_errors;

Rather than something like:
    eval {
        $row->update({col => "value"}); 
    };
    if ($@) {
        $self->add_error("DB error stuffs");
        return;
    }

Where the if ($@) block is basically the same, repeated all over the place. Certainly we can just repeat that boiler plate over and over and over. But please, no. I do have a try/catch at the dispatch level of course, but that provides just a generic error (yes, with $@ in the details), as DBIC has effectively bypassed all of our internal error handling!
 


I guess you have your reasons wanting to do that but I can think of any number of reasons (ranging from simply returning incorrect data to deleting the wrong data or worse) why this approach is dangerous. And I guess that's what Peter means by "architecturally unsound"...

Yes yes yes. But the point I seem to not be getting across is that I do NOT want to ignore errors. I just want to handle them the same way the rest of the application handles them. Sometimes we return right away, some times we need to do some other stuff after an error first. No matter what happens (even if a dev missed adding a return if or eval), the application examines $self->errors before finalizing the request and returning the response.

Again, no matter what, the first thing I need to make sure always happens is that $self->add_error(...) is called _before_ the application dies.
 


Given a chunk of code that does multiple inserts/updates - do you really want it to just blindly continue, even after something has gone wrong?

Of course not! But I also don't want the error caught by an eval two levels higher, nor have to litter our code with repetitive evals for every operation. The goal here is to simplify the common cases and integrate DBIC with our application, rather than fight with it.
 


I'm curious about what sort of application would want that kind of unsafe-by-default behaviour...

<sigh>
 

 
Actually, if you wanted to encourage die or handle as an explicit decision and still discourage not handling it at all, perhaps a new optional argument to connect to provide a callback for external error handling, which when provided sets DBIC to not propagate fatal errors to the application? exception_action seemed to come close, but was triggered too often and did not suppress the die.
 
You probably know this already but in case you don't: With DBIC it's a relatively trivial thing to create your own base class for all resultsets and all result classes in your schema.

I did not. I knew I could do that for every specific resultset (lots of examples of that). How do I override the base class for all resultsets in one place. Just create my own class with:
    use base 'DBIx::Class::Core';

And change all the Result classes to base from mine? But that then breaks dbicdump, which I would also like to avoid. Or is there a better way to do it that I missed in the docs?
 

In those two classes, overriding insert(), update() and a handful of other methods

Which? Just add one for each method we end up using? Or should I just AUTOLOAD an override for all of them?
 

would probably get you what you want. It's of course more work than just flipping a switch in DBIC, but it's a lot less work than changing every place you call into DBIC. E.g. assuming a property on your schema that knows what to do with exceptions, something like:

sub update
{
my $self = shift;
return try {
$self->next::method(@_);
} catch {
$self->result_source->schema->some_context_thingy->add_error($_);
};
}


This sounds a little tougher than sending a callback to the initial connect method, as I'd also have to find a way to get the enclosing application object passed into these override definitions. ... Or figure out how to pass in my own callback to be used there. But if you can guide me to the right place to insert my own subclass(es) in the chain without breaking things I can hopefully figure out how to pass what I need in.

Much appreciated,
Sean

_______________________________________________
List: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/dbix-class
IRC: irc.perl.org#dbix-class
SVN: http://dev.catalyst.perl.org/repos/bast/DBIx-Class/
Searchable Archive: http://www.grokbase.com/group/dbix-class@...
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Proper way to suppress die from DBIx::Class

Charlie Garrison
Good morning,

On 16/10/14 at 2:32 PM -0400, Sean Quinlan <[hidden email]> wrote:

>I did not. I knew I could do that for every specific resultset (lots of
>examples of that). How do I override the base class for all resultsets in
>one place. Just create my own class with:
>    use base 'DBIx::Class::Core';

Yes.

>And change all the Result classes to base from mine? But that then
>breaks dbicdump, which I would also like to avoid. Or is there a better way
>to do it that I missed in the docs?

Check loader_options for DBIx::Class::Schema::Loader (which dbicdump uses), eg:

  result_base_class => 'YourResultBase'


Charlie

--
   Charlie Garrison  <[hidden email]>
   github.com/cngarrison   metacpan.org/author/CNG

O< ascii ribbon campaign - stop html mail - www.asciiribbon.org
http://www.ietf.org/rfc/rfc1855.txt


_______________________________________________
List: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/dbix-class
IRC: irc.perl.org#dbix-class
SVN: http://dev.catalyst.perl.org/repos/bast/DBIx-Class/
Searchable Archive: http://www.grokbase.com/group/dbix-class@...
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Proper way to suppress die from DBIx::Class

Sean Quinlan
Aha! Thank you!

On Thu, Oct 16, 2014 at 9:00 PM, Charlie Garrison <[hidden email]> wrote:
Good morning,

On 16/10/14 at 2:32 PM -0400, Sean Quinlan <[hidden email]> wrote:

>I did not. I knew I could do that for every specific resultset (lots of
>examples of that). How do I override the base class for all resultsets in
>one place. Just create my own class with:
>    use base 'DBIx::Class::Core';

Yes.

>And change all the Result classes to base from mine? But that then
>breaks dbicdump, which I would also like to avoid. Or is there a better way
>to do it that I missed in the docs?

Check loader_options for DBIx::Class::Schema::Loader (which dbicdump uses), eg:

  result_base_class => 'YourResultBase'


Charlie

--
   Charlie Garrison  <[hidden email]>
   github.com/cngarrison   metacpan.org/author/CNG

O< ascii ribbon campaign - stop html mail - www.asciiribbon.org
http://www.ietf.org/rfc/rfc1855.txt


_______________________________________________
List: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/dbix-class
IRC: irc.perl.org#dbix-class
SVN: http://dev.catalyst.perl.org/repos/bast/DBIx-Class/
Searchable Archive: http://www.grokbase.com/group/dbix-class@...
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Proper way to suppress die from DBIx::Class

Lasse Makholm
Additionally, custom resultset classes should be found and loaded automagically if you use_namespace => 1 in the loader options (the default these days, I believe)...


/L

On Fri, Oct 17, 2014 at 3:06 PM, Sean Quinlan <[hidden email]> wrote:
Aha! Thank you!

On Thu, Oct 16, 2014 at 9:00 PM, Charlie Garrison <[hidden email]> wrote:
Good morning,

On 16/10/14 at 2:32 PM -0400, Sean Quinlan <[hidden email]> wrote:

>I did not. I knew I could do that for every specific resultset (lots of
>examples of that). How do I override the base class for all resultsets in
>one place. Just create my own class with:
>    use base 'DBIx::Class::Core';

Yes.

>And change all the Result classes to base from mine? But that then
>breaks dbicdump, which I would also like to avoid. Or is there a better way
>to do it that I missed in the docs?

Check loader_options for DBIx::Class::Schema::Loader (which dbicdump uses), eg:

  result_base_class => 'YourResultBase'


Charlie

--
   Charlie Garrison  <[hidden email]>
   github.com/cngarrison   metacpan.org/author/CNG

O< ascii ribbon campaign - stop html mail - www.asciiribbon.org
http://www.ietf.org/rfc/rfc1855.txt


_______________________________________________
List: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/dbix-class
IRC: irc.perl.org#dbix-class
SVN: http://dev.catalyst.perl.org/repos/bast/DBIx-Class/
Searchable Archive: http://www.grokbase.com/group/dbix-class@...


_______________________________________________
List: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/dbix-class
IRC: irc.perl.org#dbix-class
SVN: http://dev.catalyst.perl.org/repos/bast/DBIx-Class/
Searchable Archive: http://www.grokbase.com/group/dbix-class@...
Loading...