Repository: etcd-io/etcd
File: server/etcdserver/corrupt.go (Go)

Code before:
	if !quorumExist {
		// If quorumExist doesn't exist, then only raise alarm for the least minority
		cm.lg.Error("Detected compaction hash mismatch but can't identify the corrupted members, so only raise alarm for the least minority",
			zap.String("leader-id", leaderId.String()),
			zap.Int64("leader-revision", leaderHash.Revision),
			zap.Int64("leader-compact-revision", leaderHash.CompactRevision),
			zap.Uint32("leader-hash", leaderHash.Hash),
		)

		// find out the members which are the least minority
		var (
			minCnt    int = math.MaxInt
			hashVal   uint32
			memberIDs types.IDSlice
		)

		for k, v := range hashMap {
			if v.Len() < minCnt {
				minCnt = v.Len()
				hashVal = k
				memberIDs = v
			}
		}

		for _, pid := range memberIDs {
			cm.hasher.TriggerCorruptAlarm(pid)
		}
		delete(hashMap, hashVal)

		cm.lg.Error("Detected compaction hash mismatch but can't identify the corrupted members, so only raise alarm for the least minority",
			zap.String("leader-id", leaderId.String()),
			zap.Int64("leader-revision", leaderHash.Revision),
			zap.Int64("leader-compact-revision", leaderHash.CompactRevision),
			zap.Uint32("leader-hash", leaderHash.Hash),
			zap.Uint32("peer-hash", hashVal),
			zap.String("peer-ids", memberIDs.String()),
		)
	}

	// Raise alarm for the left members if the quorum is present.
	// But we should always generate error log for debugging.
	for k, v := range hashMap {
		for _, pid := range v {
			if quorumExist {
				cm.hasher.TriggerCorruptAlarm(pid)
			}
		}
		cm.lg.Error("Detected compaction hash mismatch",
			zap.String("leader-id", leaderId.String()),
			zap.Int64("leader-revision", leaderHash.Revision),
			zap.Int64("leader-compact-revision", leaderHash.CompactRevision),

Review comment (suggestion):
Don't agree with using `least minority` to alarm when we cannot confirm source of corruption. I would prefer to just report `0` which for alarms is meant that if affects whole cluster.

Code after:
		cm.hasher.TriggerCorruptAlarm(0)
	}

	// Raise alarm for the left members if the quorum is present.
	// But we should always generate error log for debugging.
	for k, v := range hash2members {
		if quorumExist {
			for _, pid := range v {
				cm.hasher.TriggerCorruptAlarm(pid)
			}
		}

		cm.lg.Error("Detected compaction hash mismatch",
			zap.String("leader-id", leaderId.String()),
			zap.Int64("leader-revision", leaderHash.Revision),
			zap.Int64("leader-compact-revision", leaderHash.CompactRevision),
			zap.Uint32("leader-hash", leaderHash.Hash),
			zap.Uint32("peer-hash", k),
			zap.String("peer-ids", v.String()),
			zap.Bool("quorum-exist", quorumExist),
		)
	}

	return true
}

func (cm *corruptionChecker) handleConsistentHash(hash mvcc.KeyValueHash, peersChecked, peerCnt int) bool {
	if peersChecked == peerCnt {
		cm.lg.Info("successfully checked hash on whole cluster",
			zap.Int("number-of-peers-checked", peersChecked),
			zap.Int64("revision", hash.Revision),
			zap.Int64("compactRevision", hash.CompactRevision),
		)
		cm.mux.Lock()
		if hash.Revision > cm.latestRevisionChecked {
			cm.latestRevisionChecked = hash.Revision
		}
		cm.mux.Unlock()
		return true
	}
	cm.lg.Warn("skipped revision in compaction hash check; was not able to check all peers",
		zap.Int("number-of-peers-checked", peersChecked),
		zap.Int("number-of-peers", peerCnt),
		zap.Int64("revision", hash.Revision),
		zap.Int64("compactRevision", hash.CompactRevision),
	)
	// The only case which needs to check next hash
	return false
}

func (cm *corruptionChecker) uncheckedRevisions() []mvcc.KeyValueHash {