diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterCoprocessorEnvironment.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterCoprocessorEnvironment.java index 61c983468876..b6950f4c2e85 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterCoprocessorEnvironment.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterCoprocessorEnvironment.java @@ -23,6 +23,7 @@ import org.apache.hadoop.hbase.HBaseInterfaceAudience; import org.apache.hadoop.hbase.ServerName; import org.apache.hadoop.hbase.client.Connection; +import org.apache.hadoop.hbase.master.MasterServices; import org.apache.hadoop.hbase.metrics.MetricRegistry; import org.apache.yetus.audience.InterfaceAudience; import org.apache.yetus.audience.InterfaceStability; @@ -88,4 +89,6 @@ public interface MasterCoprocessorEnvironment extends CoprocessorEnvironment ctx BulkLoadObserver.super.preCleanupBulkLoad(ctx); } + private void manageActiveClusterIdFile(boolean newValue) { + MasterFileSystem mfs = this.masterServices.getMasterFileSystem(); + FileSystem fs = mfs.getFileSystem(); + Path rootDir = mfs.getRootDir(); + Path activeClusterFile = new Path(rootDir, HConstants.ACTIVE_CLUSTER_SUFFIX_FILE_NAME); + + try { + if (newValue) { + // ENABLING READ-ONLY (false -> true), delete the active cluster file. + LOG.debug("Global read-only mode is being ENABLED. Deleting active cluster file: {}", + activeClusterFile); + + if (fs.exists(activeClusterFile)) { + if (fs.delete(activeClusterFile, false)) { + LOG.info("Successfully deleted active cluster file: {}", activeClusterFile); + } else { + LOG.error( + "Failed to delete active cluster file: {}. " + + "Read-only flag will be updated, but file system state is inconsistent.", + activeClusterFile); + } + } else { + LOG.debug("Active cluster file not present, nothing to delete."); + } + } else { + // DISABLING READ-ONLY (true -> false), create the active cluster file id file + try (FSDataOutputStream stream = fs.create(activeClusterFile, true)) { + stream.writeUTF(new String(mfs.getSuffixFileDataToWrite(), StandardCharsets.UTF_8)); + LOG.debug( + "Global read-only mode is being DISABLED. Successfully created active " + + "cluster file {} with suffix {}", + activeClusterFile, mfs.getSuffixFileDataToWrite()); + } + } + } catch (IOException e) { + // We still update the flag, but log that the operation failed. + LOG.error("Failed to perform file operation for read-only switch. " + + "Flag will be updated, but file system state may be inconsistent.", e); + this.globalReadOnlyEnabled = newValue; + LOG.info("Config {} has been dynamically changed to {}. Encountered FS error", + HConstants.HBASE_GLOBAL_READONLY_ENABLED_KEY, this.globalReadOnlyEnabled); + } + } + /* ---- ConfigurationObserver Overrides ---- */ - @Override public void onConfigurationChange(Configuration conf) { - boolean maybeUpdatedConfValue = conf.getBoolean(HConstants.HBASE_GLOBAL_READONLY_ENABLED_KEY, + boolean newValue = conf.getBoolean(HConstants.HBASE_GLOBAL_READONLY_ENABLED_KEY, HConstants.HBASE_GLOBAL_READONLY_ENABLED_DEFAULT); - if (this.globalReadOnlyEnabled != maybeUpdatedConfValue) { - this.globalReadOnlyEnabled = maybeUpdatedConfValue; - LOG.info("Config {} has been dynamically changed to {}", + if (this.globalReadOnlyEnabled != newValue) { + if (this.masterServices != null) { + manageActiveClusterIdFile(newValue); + } else { + LOG.debug("MasterServices is not initialized. Cannot perform file operations for " + + "read-only mode switch."); + } + this.globalReadOnlyEnabled = newValue; + LOG.debug("Config {} has been dynamically changed to {}. (No FS ops performed 1)", HConstants.HBASE_GLOBAL_READONLY_ENABLED_KEY, this.globalReadOnlyEnabled); } }