Hi all,
I'm using slide with the JDBCDescriptorsStore and FileContentStore.
Sometime yesterday, a user PUT a file through the webdav servlet to
slide. Network problems at this inconvenient time resulted in the socket
timing out. The result: the FileContentStore found that the length of
the file didn't match the amount of data it was actually able to read,
so it threw an IOException, later rethrown as a ServiceAccessException
and then finally as an WebdavException with status set to 500 (Internal
server error).
All of that is reasonable, and pretty much as expected.
However, the end result was an inconsistent repository that caused
ongoing problems - a bad thing. After writing the file failed, nothing
further is done - the exception is just propagated up the call stack
until it comes out at the webdav servlet. This means that the on-disk
data (in this case around 200 kB) and the getcontentlength property
(which said it was something like 350 kB, I think) were out of sync.
Slide predictably doesn't like this, and starts throwing exceptions as
soon as it has to read the file (which isn't unreasonable).
Now - the question is, how do we fix this? A comment in
FileContentStore.java says "FIXME : Delete the file". That in itself is
pretty easy to do (merely need a single line addition of file.delete()
just before we throw the IOException, or perhaps where we catch the
IOException). However, that isn't sufficient - we still have all the
metadata lying around, which we have to do something with.
There are two or three things we can do here: one is significantly
easier, but it's not clear to me which is correct.
1) Set the getcontentlength property to what we actually wrote to the
file. Simple, effective. Solves all the problems. We can still give an
internal server error, but the file will exist in a consistent state in
the repository. Easy, but not neccesarily correct.
2) Delete the revision, along with any other sub-parts. This means that
Content.create() will become significantly more complex, but won't
really affect anything else. However, this means we'll be left with a
revisionless object (which I think is treated as an empty collection,
then). More complex, but probably a worse solution.
3) Go the whole way and revert everything to how it was before we
started the PUT. This is very difficult, because we have multiple stages
in the PUT (i.e. modify structure, set properties, create revision(s),
write content, etc), which are completely seperate. The webdav servlet
would have to catch errors at each stage and be prepared to 'undo' all
the modifications it has made so far. Does the transactions stuff help
here at all? (I haven't looked at that stuff in detail). This is far
more complex than either of the other solutions. It's the most
'complete' fix, but it isn't (to me) clearly superior to option 1.
I've attached a (fairly trivial) fix that implements option 1, since at
this point that's my preferred option - Remy, feel free to commit it (or
ask me to) if you think this is the right thing to do.
Michael
Index: FileContentStore.java
===================================================================
RCS file:
/home/cvs/jakarta-slide/src/stores/slidestore/reference/FileContentStore.java,v
retrieving revision 1.7
diff -u -r1.7 FileContentStore.java
--- FileContentStore.java 2001/03/19 16:49:37 1.7
+++ FileContentStore.java 2001/06/13 01:54:49
@@ -314,6 +314,8 @@
if (contentLength != -1) {
if (position != contentLength) {
+ // set content length so that repository is consistent
+ revisionDescriptor.setContentLength(position);
if (position < contentLength) {
// Not enough bytes read !!!
throw new IOException("Not enough bytes
read");