vgritsenko 2003/12/21 17:38:42
Modified: java/src/org/apache/xindice/core/filer HashFiler.java Log: Reduce synchronization scope when adding record - synchronize only while searching free page. Add attempt to release free page if write fails. Add check on large keys (over one page size) - HashFiler does not support them. Revision Changes Path 1.19 +72 -51 xml-xindice/java/src/org/apache/xindice/core/filer/HashFiler.java Index: HashFiler.java =================================================================== RCS file: /home/cvs/xml-xindice/java/src/org/apache/xindice/core/filer/HashFiler.java,v retrieving revision 1.18 retrieving revision 1.19 diff -u -r1.18 -r1.19 --- HashFiler.java 18 Dec 2003 15:05:20 -0000 1.18 +++ HashFiler.java 22 Dec 2003 01:38:42 -0000 1.19 @@ -188,66 +188,87 @@ return null; } + private Page seekInsertionPage(Key key) throws IOException { + int hash = key.getHash(); + long pageNum = hash % fileHeader.getPageCount(); + + // Synchronize by chain head page + Page p = getPage(pageNum); + synchronized (p) { + HashPageHeader ph; + while (true) { + ph = (HashPageHeader) p.getPageHeader(); + if (ph.getStatus() == UNUSED || ph.getStatus() == DELETED + || (ph.getStatus() == RECORD && ph.getKeyHash() == key.getHash() && p.getKey().equals(key))) { + // Found free page + break; + } + + pageNum = ph.getNextCollision(); + if (pageNum == -1) { + // Reached end of chain, add new page + Page np = getFreePage(); + + ph.setNextCollision(np.getPageNum().longValue()); + p.write(); + + p = np; + ph = (HashPageHeader) p.getPageHeader(); + ph.setNextCollision(NO_PAGE); + break; + } + + // Go to next page in chain + p = getPage(pageNum); + } + + // Here we have a page + long t = System.currentTimeMillis(); + if (ph.getStatus() == UNUSED || ph.getStatus() == DELETED) { + // This is a new Record + fileHeader.incRecordCount(); + ph.setCreated(t); + } + ph.setModified(t); + ph.setStatus(RECORD); + + // Write modifications to the page header before existing synchronization block + // This will prevent other threads from getting this same page + p.write(); + } + + return p; + } + public boolean writeRecord(Key key, Value value) throws DBException { - if (key == null || key.equals("")) { + // Check that key is not larger than space on the page + if (key == null || key.equals("") || key.getLength() > fileHeader.getPageSize() - fileHeader.getPageHeaderSize()) { throw new FilerException(FaultCodes.DBE_CANNOT_CREATE, "Invalid key: '" + key + "'"); } if (value == null) { throw new FilerException(FaultCodes.DBE_CANNOT_CREATE, "Invalid null value"); } checkOpened(); + Page p = null; try { - int hash = key.getHash(); - long pageNum = hash % fileHeader.getPageCount(); - - // Synchronize by chain head page - Page p = getPage(pageNum); - synchronized (p) { - HashPageHeader ph; - while (true) { - ph = (HashPageHeader) p.getPageHeader(); - if (ph.getStatus() == UNUSED || ph.getStatus() == DELETED - || (ph.getStatus() == RECORD && ph.getKeyHash() == key.getHash() && p.getKey().equals(key))) { - // Found free page - break; - } - - pageNum = ph.getNextCollision(); - if (pageNum == -1) { - // Reached end of chain, add new page - Page np = getFreePage(); - ph.setNextCollision(np.getPageNum().longValue()); - p.write(); - p = np; - ph = (HashPageHeader) p.getPageHeader(); - ph.setNextCollision(NO_PAGE); - break; - } - - // Go to next page in chain - p = getPage(pageNum); - } - - // Here we have a page - long t = System.currentTimeMillis(); - if (ph.getStatus() == UNUSED || ph.getStatus() == DELETED) { - // This is a new Record - fileHeader.incRecordCount(); - ph.setCreated(t); - } - ph.setModified(t); - ph.setStatus(RECORD); - - p.setKey(key); - writeValue(p, value); - } - - flush(); + p = seekInsertionPage(key); + p.setKey(key); + writeValue(p, value); + p = null; } catch (Exception e) { - if (log.isWarnEnabled()) { - log.warn("ignored exception", e); + throw new FilerException(FaultCodes.DBE_CANNOT_CREATE, "Exception: " + e); + } finally { + if (p != null) { + p.getPageHeader().setStatus(DELETED); + try { + p.write(); + } catch (IOException ignored) { + // Double exception + } } } + + flush(); return true; }