Kevin Day
Kevin Day

Reputation: 16383

Design patterns for managing results of large operations

We frequently have objects that perform multi-part operations/tasks. An example would be refreshing internal state of a list of objects based on a query of the file system and reading data from the found files. These things are also often long running, and tend to be performed in background threads (we do a lot of Swing work, but I think this sort of thing would apply to multi-tier apps as well).

In this sort of operation, it's quite possible that part of the operation would fail (i.e. unable to read 5 or 6 of the 5000 files that we are processing). In this case, it would be inappropriate to throw an exception, but we still want to provide the user with feedback about what didn't happen, etc...

We've always used ad-hoc approaches here (e.g. logging, or have the operation return a list of exceptions), but I've decided to crack down and really think this through.

One motivating concept is Validation (jgoodies has a good implementation). The idea here is that when validating the values in a form or data structure, you construct a ValidationResults object, which can contain ValidationResult objects. Each ValidationResult has a state (OK, WARN, ERROR) as well as descriptive text (and other things as necessary).

I am highly tempted to just use the Validation framework as-is for task results.

Another option would be to expose the current operation result state of the task as a bound property (probably using a GlazedLists event list).

I was wondering if anyone else has any opinions/experience with this sort of thing, or what design patterns may have evolved to deal with long running tasks that can have sub-tasks that may fail, or not quite complete perfectly (i.e. the WARN state) ?

EDIT: Clarification on purpose of the question

A lot of folks have suggested logging the problems and telling the user to check with an administrator. To clarify the problem a bit, consider an application like Eclipse. If the build process had warnings or errors, and Eclipse just gave an error dialog saying 'there were problems, check the logs', I think the user would be less than satisfied.

Presenting the user with a list of the current warnings and errors is a much more interactive solution, and provides the user a mechanism for quickly evaluating and addressing the issues.

The two approaches that we'd tried so far have been:

  1. Logging and tossing a dialog box at the end of the process
  2. Exposing a property containing the exceptions that have occured

We are trying to move away from option 1 b/c of the user experience.

Option 2 feels extremely ad-hoc to me. It certainly works, but I'm trying to find a good, standardized methodology for addressing this situation.

Exceptions are good for routines that have 'stop the world' failures (the routine either succeeds or fails in some way), and throwing exceptions is a very nice cross-cutting mechanism for handling those situations.

But when a routine doesn't fall into a simple pass/fail categorization, things get a bit fuzzy - the routine itself can partially succeed.

So, is it better to pass in a 'ProblemListener' that we report issues to? Or is it better to have the routine return a list of problems? Or have the object expose a property of issues from the last run of the routine?

There are many potential solutions - I'm wondering if anyone has experience with trying these, and have they found a best practice (and what reasons they accept or reject a given approach)?

Upvotes: 3

Views: 721

Answers (6)

Xeno Lupus
Xeno Lupus

Reputation: 1534

Although the question is old here are my 5 cent:

I would use a listener for upcoming errors that might occur while processing the operation. This way you can inform the user that an error occurred while the operation is still running. Thus he also could cancel the operation midways. You might even ask him how to handle the error in the call back method.

Additionally to that you can return a boolean to indicate that the operation did not execute successfully complete. And only throw an exception when an error occurred the operation can not handle.

Upvotes: 0

dkretz
dkretz

Reputation: 37645

Where you say "Objects" I read Batches (at least as one implication), and I agree that logging is the appropriate conventional way to handle this, but with a Batch ID, either as a field in the log record, or maybe more likely a separate log file per batch, if the monitoring process, batch size, and object count make that workable.

Upvotes: 0

Ravi Wallau
Ravi Wallau

Reputation: 10493

Exceptions are usually the right way of signalizing that some operation failed in Java. I think that whatever your approach is, throwing an exception in case something wrong is the way to go. In our company we use this 3rd library to save objects in batch, and in case something goes wrong the entire operation fails, and the exception that they throw contain the details for each error and the list of objects that were not imported. We then try again with the healthy objects. If you can't do that because your operation is not transactional, I would go with one of the options:

  • Fail with an Exception that contain a list of exceptions and a list of the faulty objects;
  • Fail with a list of objects, each object containing:
    • The error level (ERROR, WARN);
    • The error message;
    • The faulty object.

I don't like the approach of not throwing an exception (returning an error value, for example), because it relies on developers checking for a return value in your method, which is not the standard way of treating errors in Java. I would also not go with the logging approach as it would force the user to dig into the log files to find what went wrong.

I worked once in this company that had client devices that had to perform synchronization tasks. Each client device had a record on a "devices" table, and this table contained information about the last synchronization - if it succeeded or not, and if it didn't, what went wrong. This is a nice approach if your task is performed by a service, and not as the result of a method invocation - or, better saying, if your task is asynchronous. Someone asks for the task to start, may or may not receive an event about its completion, and later checks for its results.

Upvotes: 0

neesh
neesh

Reputation: 5265

What you are trying to do is essentially logging and for that reason I agree with the response that says you should simply log the results.

I would go one step further to say if you use log4j, you can create a custom appender that would know how to handle this type of log events and act accordingly (if it is a WARN don't do anything, if it is an ERROR alert the user, etc). What's more, if you design things the right way, you can keeps things configurable via log4j.properties (or log4j.xml)

Upvotes: 0

James Black
James Black

Reputation: 41858

Why not just have a public property that has a list that contains the errors, and return a boolean, so if there is an error then the application knows to get the list of errors.

You can also just send out an event on each error that is caught by the subscriber and display the error, so they would see the errors as it happens, so, if the application takes an hour to process then they don't have to wait that long to know that early on there was an error.

What would be nice is to allow the user to then fix the problem and add it back to the queue to be reprocessed. :)

Upvotes: 0

Larry Watanabe
Larry Watanabe

Reputation: 10184

Why not just log the errors, and when the operation is done let the user know that an error occurred and that the sysadmin should look at the event log?

Any technical errors will probably not be understood by a casual user. Non-casual users should be savvy enough to look at a log file and interpret the results.

Upvotes: 2

Related Questions