public synchronized boolean saveNamespace(long timeWindow, long txGap,
      FSNamesystem source) throws IOException {
    if (timeWindow > 0 || txGap > 0) {
      final FSImageStorageInspector inspector = storage.readAndInspectDirs(
          EnumSet.of(NameNodeFile.IMAGE, NameNodeFile.IMAGE_ROLLBACK),
          StartupOption.REGULAR);
      FSImageFile image = inspector.getLatestImages().get(0);
      File imageFile = image.getFile();

      final long checkpointTxId = image.getCheckpointTxId();
      final long checkpointAge = Time.now() - imageFile.lastModified();
      if (checkpointAge <= timeWindow * 1000 &&
          checkpointTxId >= this.getCorrectLastAppliedOrWrittenTxId() - txGap) {
        return false;
      }
    }
    saveNamespace(source, NameNodeFile.IMAGE, null);
    return true;
  }
@Test
  public void testSaveAndLoadErasureCodingPolicies() throws IOException{
    Configuration conf = new Configuration();
    final int blockSize = 16 * 1024 * 1024;
    conf.setLong(DFSConfigKeys.DFS_BLOCK_SIZE_KEY, blockSize);
    try (MiniDFSCluster cluster =
             new MiniDFSCluster.Builder(conf).numDataNodes(10).build()) {
      cluster.waitActive();
      DistributedFileSystem fs = cluster.getFileSystem();
      DFSTestUtil.enableAllECPolicies(fs);

      // Save namespace and restart NameNode
      fs.setSafeMode(SafeModeAction.ENTER);
      fs.saveNamespace();
      fs.setSafeMode(SafeModeAction.LEAVE);

      cluster.restartNameNodes();
      cluster.waitActive();

      assertEquals("Erasure coding policy number should match",
          SystemErasureCodingPolicies.getPolicies().size(),
          ErasureCodingPolicyManager.getInstance().getPolicies().length);

      // Add new erasure coding policy
      ECSchema newSchema = new ECSchema("rs", 5, 4);
      ErasureCodingPolicy newPolicy =
          new ErasureCodingPolicy(newSchema, 2 * 1024, (byte) 254);
      ErasureCodingPolicy[] policies = new ErasureCodingPolicy[]{newPolicy};
      AddErasureCodingPolicyResponse[] ret =
          fs.addErasureCodingPolicies(policies);
      assertEquals(1, ret.length);
      assertEquals(true, ret[0].isSucceed());
      newPolicy = ret[0].getPolicy();

      // Save namespace and restart NameNode
      fs.setSafeMode(SafeModeAction.ENTER);
      fs.saveNamespace();
      fs.setSafeMode(SafeModeAction.LEAVE);

      cluster.restartNameNodes();
      cluster.waitActive();

      assertEquals("Erasure coding policy number should match",
          SystemErasureCodingPolicies.getPolicies().size() + 1,
          ErasureCodingPolicyManager.getInstance().getPolicies().length);
      ErasureCodingPolicy ecPolicy =
          ErasureCodingPolicyManager.getInstance().getByID(newPolicy.getId());
      assertEquals("Newly added erasure coding policy is not found",
          newPolicy, ecPolicy);
      assertEquals(
          "Newly added erasure coding policy should be of disabled state",
          ErasureCodingPolicyState.DISABLED,
          DFSTestUtil.getECPolicyState(ecPolicy));

      // Test enable/disable/remove user customized erasure coding policy
      testChangeErasureCodingPolicyState(cluster, blockSize, newPolicy, false);
      // Test enable/disable default built-in erasure coding policy
      testChangeErasureCodingPolicyState(cluster, blockSize,
          SystemErasureCodingPolicies.getByID((byte) 1), true);
      // Test enable/disable non-default built-in erasure coding policy
      testChangeErasureCodingPolicyState(cluster, blockSize,
          SystemErasureCodingPolicies.getByID((byte) 2), false);
    }
  }