/** * @param fs * @param hTableDescriptor * @param tableDir * @param status * @return Descriptor file or null if we failed write. * @throws IOException */ private static Path writeTableDescriptor( final FileSystem fs, final HTableDescriptor hTableDescriptor, final Path tableDir, final FileStatus status) throws IOException { // Get temporary dir into which we'll first write a file to avoid // half-written file phenomeon. Path tmpTableDir = new Path(tableDir, ".tmp"); // What is current sequenceid? We read the current sequenceid from // the current file. After we read it, another thread could come in and // compete with us writing out next version of file. The below retries // should help in this case some but its hard to do guarantees in face of // concurrent schema edits. int currentSequenceid = status == null ? 0 : getTableInfoSequenceid(status.getPath()); int sequenceid = currentSequenceid; // Put arbitrary upperbound on how often we retry int retries = 10; int retrymax = currentSequenceid + retries; Path tableInfoPath = null; do { sequenceid += 1; Path p = getTableInfoFileName(tmpTableDir, sequenceid); if (fs.exists(p)) { LOG.debug(p + " exists; retrying up to " + retries + " times"); continue; } try { writeHTD(fs, p, hTableDescriptor); tableInfoPath = getTableInfoFileName(tableDir, sequenceid); if (!fs.rename(p, tableInfoPath)) { throw new IOException("Failed rename of " + p + " to " + tableInfoPath); } } catch (IOException ioe) { // Presume clash of names or something; go around again. LOG.debug("Failed write and/or rename; retrying", ioe); if (!FSUtils.deleteDirectory(fs, p)) { LOG.warn("Failed cleanup of " + p); } tableInfoPath = null; continue; } // Cleanup old schema file. if (status != null) { if (!FSUtils.deleteDirectory(fs, status.getPath())) { LOG.warn("Failed delete of " + status.getPath() + "; continuing"); } } break; } while (sequenceid < retrymax); return tableInfoPath; }
/** Deletes a table's directory from the file system if exists. Used in unit tests. */ public static void deleteTableDescriptorIfExists(String tableName, Configuration conf) throws IOException { FileSystem fs = FSUtils.getCurrentFileSystem(conf); FileStatus status = getTableInfoPath(fs, FSUtils.getRootDir(conf), tableName); // The below deleteDirectory works for either file or directory. if (status != null && fs.exists(status.getPath())) { FSUtils.deleteDirectory(fs, status.getPath()); } }