func()

in go/storage/certdatabase.go [581:697]


func (db *CertDatabase) Commit(aProofOfLock string) error {
	// Commit() moves serials from cache to storage, removes expired serial
	// numbers from storage, and updates the coverage metadata file. This is
	// done in four steps:
	//   1) coverage metadata is retrieved from cache and written to a
	//      temporary file,
	//   2) cached serials are moved to persistent storage,
	//   3) the coverage metadata file is atomically overwritten with the
	//      temporary file from step 1,
	//   4) expired serial numbers are removed from storage.
	// This sequence of operations ensures that the coverage metadata file
	// describes a subset of the stored serials at the end of step 3. (It
	// will typically be a strict subset, as the commit process is intended
	// to run in parallel with ct-fetch).
	//
	// The caller must hold the commit lock (i.e. the caller must store a
	// random value under the key `lock::commit` in the cache and then
	// provide that value here as `aProofOfLock`).
	//
	// The epoch value in storage must be one less than the epoch value in
	// cache (unless this is the first time that Commit() has been called,
	// in which case both epochs will be equal to 0).

	if db.readOnlyStorage {
		return fmt.Errorf("Cannot commit serials to read-only storage")
	}

	hasLock, err := db.cache.HasCommitLock(aProofOfLock)
	if err != nil {
		return err
	}
	if !hasLock {
		return errors.New("Caller must hold commit lock")
	}

	storageEpoch, err := db.getStorageEpoch()
	if err != nil {
		return err
	}

	cacheEpoch, err := db.cache.GetEpoch()
	if err != nil {
		return err
	}

	if (cacheEpoch != storageEpoch+1) && !(cacheEpoch == 0 && storageEpoch == 0) {
		return errors.New("Inconsistent cache and storage epochs. Restart ct-fetch.")
	}

	logList, err := db.cache.LoadAllLogStates()
	if err != nil {
		return err
	}

	ctLogFD, err := renameio.TempFile("", db.coverageFile())
	if err != nil {
		return err
	}
	defer ctLogFD.Cleanup()

	enc := json.NewEncoder(ctLogFD)
	if err = enc.Encode(logList); err != nil {
		return err
	}

	err = db.moveCachedSerialsToStorage()
	if err != nil {
		return err
	}

	err = ctLogFD.CloseAtomicallyReplace()
	if err != nil {
		return err
	}

	err = db.removeExpiredSerialsFromStorage(time.Now())
	if err != nil {
		return err
	}

	err = db.moveAliasedSerials()
	if err != nil {
		return err
	}

	// The data on disk is in a good state and we just have to increment
	// the cache and storage epochs. We can ignore some errors here as long
	// as the end result is that the cache is one epoch ahead of storage.
	epochFD, err := renameio.TempFile("", db.epochFile())
	if err != nil {
		glog.Warningf("Failed to increment epochs: %s", err)
		return nil
	}
	defer epochFD.Cleanup()

	writer := bufio.NewWriter(epochFD)
	_, err = writer.WriteString(fmt.Sprintf("%v\n", cacheEpoch))
	if err != nil {
		glog.Warningf("Failed to increment epochs: %s", err)
		return nil
	}
	writer.Flush()

	err = db.cache.NextEpoch()
	if err != nil {
		glog.Warningf("Failed to increment epochs: %s", err)
		return nil
	}

	err = epochFD.CloseAtomicallyReplace()
	if err != nil {
		// This is the one case where we get inconsistent epochs.
		return err
	}

	return nil
}