NetKernel/News/3/43/October_26th_2012
search:

NetKernel News Volume 3 Issue 43

October 26th 2012

What's new this week?

Catch up on last week's news here

Repository Updates

The following update is available in the NKEE and NKSE repositories...

  • database-relational 1.13.1
    • Workaround to enable parameterisation of prepared statement when JDBC driver does not implement field metadata (cough, Oracle, cough). Thanks to Keith Treague of Thomson Reuters Findlaw.com for reporting and testing this fix.

On Exceptions

What is an Exception? To the designer of a programming language an exception is a signal to indicate that an anomalous situation has arisen.

An exception interrupts the normal execution of a process.

Modern languages support exception handling whereby when an exception is encountered the process flow switches to an exception handling routine. In Java we are used to wrapping method invocations that may return exceptions in try-catch blocks...

try
{   doThis();
}
catch(Exception e)
{   doThisIfItFails();
}

The implementer of a method is able to signal an anomalous condition by constructing an Exception object and throwing the exception...

public void doThis() throws Exception
{   ...
    if(problem)
    {   throw new Exception("Houston we have a "+problem);
    }
    ...
}

Which automatically exits the currently executing method and causes process flow to jump to the catch block of the invoking process.

The discussion so far has been a language and code-centric view. It is the view from the physical domain, but what about the logical ROC domain?

Exception Handling in NetKernel

What happens if an ROC request results in an exception? What happens if this request to "active:foo"...

try
{   context.source("active:foo");
}
catch(Exception e)
{   doThisIfItFails();
}

is resolved to an endpoint that implements an onSource method like this...

public void onSource(INKFRequestContext context) throws Exception
{   ...
    if(problem)
    {   throw new Exception("Houston we have a "+problem);
    }
    ...
}

(I've shown a Java endpoint to be consistent with the original example above, but the same holds if it is a scripting language or anything else that could throw an exception)

The answer is, the exception is regarded as a signal that causes the process flow to jump to the catch block.

So apparently NetKernel and NKF behaves exactly as you would expect. Exceptions are exceptions are exceptions...

Good that's exactly what we hoped you'd think. Now here's what is really happening...

Exception as Resource State

NetKernel is a consistent abstraction. It manages spaces and their abstract resources and mediates requests for the representation state of those resources. Exceptions are consistent within the abstraction.

When you request a resource and that resource implementation throws an exception (like in the example above), then to ROC and NetKernel the exception is the representation state of the resource.

Or put it another way, for that request in that context, the resource is an exception.

How NKF Treats Exceptions

The NetKernel Foundation API (NKF) also understands the nature of exceptions in the ROC domain.

When you throw an exception in an endpoint (or script or whatever..), the underlying NKF API catches the thrown exception. It constructs an INKFResponse containing the Exception and returns that as the response for the request.

When the kernel sees the response with the Exception it doesn't deviate from its operation at all. It doesn't care. To the NetKernel abstraction this is the representation state of the resource.

The kernel returns the response (with the exception) to the requesting endpoint.

The endpoint that issued the request notices that this response's representation is an Exception. It knows that the physical level code expects to be able to conform to the standard exception handling process flow, so the NKF endpoint throws the exception causing the requesting code to jump to the catch block.

The requesting code doesn't ever know what happened. Everything appeared "normal" as you would expect.

So far, so what, why should I care?

Implications

The most important thing is that if you never knew this was what was happening and just assumed exceptions were exceptions then we got the design right. Most of the time that's all you'll ever want and so long as exceptions appear to work as expected then things are good.

But there are a number of significant consequences - some you may not have considered and some you may not even have thought possible...

Polyglot/Extrinsic Exception Handling

Did it ever occur to you that an exception thrown in Groovy can be caught in a Java endpoint? Or Ruby can catch Python exceptions? Or XSLT exceptions can be caught in Javascript?

The first significant implication of NetKernel's exception handling model is that any language or arbitrary endpoint can throw an exception to any other. The NK exception handling is not confined to the plane of a single language.

If you're getting to grips with NK and ROC then this probably doesn't even feel different to local try-catch flows. Hopefully it never crossed your mind. But this is a very significant win.

Exception Caching

To the ROC abstraction an Exception is just the state of a resource. It follows that NetKernel is perfectly happy to cache the Exception state.

Equally an NKF endpoint that issues a request and which receives a cached Exception response also doesn't care - it will still cause the requesting endpoint to jump to its catch block.

Therefore, since the NK dependency model ensures that referential integrity is assured, then from a system optimisation perspective, caching exceptions is exceptionally efficient.

NetKernel has the power to automatically eliminate redundant computational execution that would otherwise have resulted in an exception.

To Cache or Not to Cache: That was the question

About seven years ago Tony and I spent a week locked in mortal combat over the rights and wrongs of caching exceptions.

Tony argued that NetKernel must default to the absolute maximally efficient computational model. If it knew something could be cached it should cache it - including exceptions. He argued that exceptions should always be cached with an associated dependency expiry by default.

On the other hand, I imagined a world where a developer has not finished developing yet. An imperfect world where implementation code still had unanticipated bugs.

While it is true the dependency model would ensure that any change would make cached exceptions expire and so allow source code changes to be tested, imagine trying to trace a subtle problem if the system is caching the exceptions. It is a recipe for developer insanity.

We went back and forth debating our obligations to future developers versus our obligation to maximally efficient solutions.

The clincher came when we discussed externally triggered exceptions - things like IO socket timeouts, database errors, file not found etc etc. Externalities that may, for the moment, result in an exception, but that are innately transient and could in future be changed to no longer result in exceptions.

We discussed if it would be possible to have a "dictionary of exceptions" and use it to decide if the type of an exception could determine if it had intrinsic or extrinsic referential integrity. But, with the polyglot world I mentioned already - you rapidly see that this is not something that could be consistently written or maintained.

It followed that Tony's dream was shattered. Pragmatism stole the day and by default NetKernel does not automatically try to cache unhandled exceptions thrown within an endpoint.

But that's not the end of the story, just because the default is to err on the side of the expected behaviour doesn't exclude exceptional exception handling patterns...

How to Tell NKF to use "Platonic Exceptions"

If you write an endpoint and you know that it has no externalities (sub-requests that might end-up interacting with external systems etc) then you can tell an endpoint that it should not expire uncaught exceptions by declaring so in its constructor...

public MyEndpoint()
{
    declareUnhandledExceptionsExpired(false);
}

Any such endpoint will still automatically catch exceptions and return them through the kernel as before - only this time it gives the exception a "dependency expiry". The exception will be cacheable so long as all resources it depends upon are valid - just as is the default for an ordinary endpoint response.

Of course, if you turn on this switch you need to keep your wits about you. It is a pretty big sledge-hammer and although it will be operationally consistent - it may cause you nightmares if you have unanticipated exceptions that need debugging.

But there's an alternative - less potentially risky option...

How to Cache an Exception Safely

When I said the kernel doesn't care if its an Exception - actually I should have qualified this... As of last week's kernel update - the kernel doesn't care if the response has an exception.

Based on a use-case we received, which I'll discuss below, we realised that the kernel actually implemented a constraint to always prevent exceptions being cached unless an endpoint declared not to expire exceptions (as discussed in the previous section).

On reflection we decided this was actually playing things a little too safe - so, with the kernel update, we removed a one-line constraint and opened up a new world of opportunity...

Firstly just to make very clear, uncaught exceptions thrown within onRequest(), onSource(), onSink() etc are handled by NKF as discussed above and as before they are not cached by default!

However the new opportunity is for you to take control of the ROC domain exception state by explicitly creating a response from an Exception.

It's probably easier to see what's possible with an example...

public void onSource(INKFRequestContext context) throws Exception
{   ...
    if(problem)
    {   Exception e=new Exception("Houston we have a temporary "+problem);
        INKFResponse resp=context.createResponseFrom(e);
        //Expire in 5 minutes
        resp.setExpiry(INKFResponse.EXPIRY_CONSTANT, System.currentTimeMillis()+5*60000);
        return;
    }
    ...
}

Notice that we are constructing an Exception and making it our response representation. If we do nothing more, the response will default to the standard dependent expiry function - therefore the exception has the opportunity to be cached.

Relying on the dependent expiry of Exceptions is something you need to take a careful decision about - I would suggest you might want to explicitly set your own expiry as a conscious design decision.

In this example for instance, I've declared that this Exception should be cacheable for five minutes (more on which below).

The example code is pretty self explanatory but there are a few things to note:

  1. You cannot throw an exception and expect it to cache - thrown exceptions (and JVM runtime exceptions) are always not cached by default.
  2. After creating an exception response and setting its expiry you need to exit the called function (return). This will cause NKF to start dealing with the Exception resource and pass it back to the requesting endpoint as if you had thrown it.
  3. The Exception object should be immutable (as it will be shared and concurrently accessed - that's the whole point of caching it!)
  4. I repeat - think carefully about expiry.

Otherwise that's all there is to it.

From the client endpoint's point of view, when it issues a request for a resource which is immediately returned as a cached Exception then, just as when an exception is thrown, it will locally throw the exception causing the catch block to execute. However, with exception caching, the time wasted actually running the code that would have generated the exception is eliminated - this can yield some very extreme efficiencies and offers some elegant system architectural patterns...

System Engineering Opportunity

While you'll no doubt think of scenarios that could take advantage of exception caching, its ironic that the one stand-out situation that really offers huge engineering potential is the caching of externally triggered exceptions (ie the ones we were so worried about all those years ago).

Say for example you have a NetKernel making requests to an overloaded database? If you use the pattern I showed above, catch the immediate exception, then return it as a representation and cache it for some time period - you can politely implement a back-off policy to give the DB some breathing space. Piling in more and more threads to an already overloaded database isn't going to help anyone.

Equally this pattern applies to any client-tier in an architecture - for example gracefully backing off from a web-service that is offline.

Essentially what you are doing with this Exception caching pattern is introducing a Nyquist sampling rate on the external resource. In so doing you are saving huge amounts of time and wasted effort attempting to use external resources which you already know will not be accessible. The additional benefit is that even though the system is not working - the caching is providing a much much more responsive solution which, ultimately, is a better experience for the calling tier.

Distributed Exception Handling

Finally, there is one more important advantage to the NetKernel ROC model of "Exceptions as representation state": When you use the NetKernel Protocol (NKP) which embodies the full ROC abstraction, exceptions can be seamlessly thrown and handled within and between distributed (cloud) architectures.

Distributing your ROC solution makes no difference to your error handling.

Think about that one for a moment...

Then think about this: Since the ROC abstraction is coherent and NKP supports the cache dependency abstraction between servers, you can coherently operate clusters of NetKernels that implement the System Engineering exception caching pattern discussed above.


Have a great weekend.

Comments

Please feel free to comment on the NetKernel Forum

Follow on Twitter:

@pjr1060 for day-to-day NK/ROC updates
@netkernel for announcements
@tab1060 for the hard-core stuff

To subscribe for news and alerts

Join the NetKernel Portal to get news, announcements and extra features.

NetKernel will ROC your world

Download now
NetKernel, ROC, Resource Oriented Computing are registered trademarks of 1060 Research


WiNK
© 2008-2011, 1060 Research Limited