You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							203 lines
						
					
					
						
							4.5 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							203 lines
						
					
					
						
							4.5 KiB
						
					
					
				
								package lock_manager
							 | 
						|
								
							 | 
						|
								import (
							 | 
						|
									"sort"
							 | 
						|
									"sync"
							 | 
						|
									"time"
							 | 
						|
								
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/glog"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/pb"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/util"
							 | 
						|
								)
							 | 
						|
								
							 | 
						|
								type LockRingSnapshot struct {
							 | 
						|
									servers []pb.ServerAddress
							 | 
						|
									ts      time.Time
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								type LockRing struct {
							 | 
						|
									sync.RWMutex
							 | 
						|
									snapshots        []*LockRingSnapshot
							 | 
						|
									candidateServers map[pb.ServerAddress]struct{}
							 | 
						|
									lastUpdateTime   time.Time
							 | 
						|
									lastCompactTime  time.Time
							 | 
						|
									snapshotInterval time.Duration
							 | 
						|
									onTakeSnapshot   func(snapshot []pb.ServerAddress)
							 | 
						|
									cleanupWg        sync.WaitGroup
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func NewLockRing(snapshotInterval time.Duration) *LockRing {
							 | 
						|
									return &LockRing{
							 | 
						|
										snapshotInterval: snapshotInterval,
							 | 
						|
										candidateServers: make(map[pb.ServerAddress]struct{}),
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (r *LockRing) SetTakeSnapshotCallback(onTakeSnapshot func(snapshot []pb.ServerAddress)) {
							 | 
						|
									r.Lock()
							 | 
						|
									defer r.Unlock()
							 | 
						|
									r.onTakeSnapshot = onTakeSnapshot
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// AddServer adds a server to the ring
							 | 
						|
								// if the previous snapshot passed the snapshot interval, create a new snapshot
							 | 
						|
								func (r *LockRing) AddServer(server pb.ServerAddress) {
							 | 
						|
									glog.V(0).Infof("add server %v", server)
							 | 
						|
									r.Lock()
							 | 
						|
								
							 | 
						|
									if _, found := r.candidateServers[server]; found {
							 | 
						|
										glog.V(0).Infof("add server: already exists %v", server)
							 | 
						|
										r.Unlock()
							 | 
						|
										return
							 | 
						|
									}
							 | 
						|
									r.lastUpdateTime = time.Now()
							 | 
						|
									r.candidateServers[server] = struct{}{}
							 | 
						|
									r.Unlock()
							 | 
						|
								
							 | 
						|
									r.takeSnapshotWithDelayedCompaction()
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (r *LockRing) RemoveServer(server pb.ServerAddress) {
							 | 
						|
									glog.V(0).Infof("remove server %v", server)
							 | 
						|
								
							 | 
						|
									r.Lock()
							 | 
						|
								
							 | 
						|
									if _, found := r.candidateServers[server]; !found {
							 | 
						|
										r.Unlock()
							 | 
						|
										return
							 | 
						|
									}
							 | 
						|
									r.lastUpdateTime = time.Now()
							 | 
						|
									delete(r.candidateServers, server)
							 | 
						|
									r.Unlock()
							 | 
						|
								
							 | 
						|
									r.takeSnapshotWithDelayedCompaction()
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (r *LockRing) SetSnapshot(servers []pb.ServerAddress) {
							 | 
						|
								
							 | 
						|
									sort.Slice(servers, func(i, j int) bool {
							 | 
						|
										return servers[i] < servers[j]
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									r.Lock()
							 | 
						|
									r.lastUpdateTime = time.Now()
							 | 
						|
									// init candidateServers
							 | 
						|
									for _, server := range servers {
							 | 
						|
										r.candidateServers[server] = struct{}{}
							 | 
						|
									}
							 | 
						|
									r.Unlock()
							 | 
						|
								
							 | 
						|
									r.addOneSnapshot(servers)
							 | 
						|
								
							 | 
						|
									r.cleanupWg.Add(1)
							 | 
						|
									go func() {
							 | 
						|
										defer r.cleanupWg.Done()
							 | 
						|
										<-time.After(r.snapshotInterval)
							 | 
						|
										r.compactSnapshots()
							 | 
						|
									}()
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (r *LockRing) takeSnapshotWithDelayedCompaction() {
							 | 
						|
									r.doTakeSnapshot()
							 | 
						|
								
							 | 
						|
									r.cleanupWg.Add(1)
							 | 
						|
									go func() {
							 | 
						|
										defer r.cleanupWg.Done()
							 | 
						|
										<-time.After(r.snapshotInterval)
							 | 
						|
										r.compactSnapshots()
							 | 
						|
									}()
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (r *LockRing) doTakeSnapshot() {
							 | 
						|
									servers := r.getSortedServers()
							 | 
						|
								
							 | 
						|
									r.addOneSnapshot(servers)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (r *LockRing) addOneSnapshot(servers []pb.ServerAddress) {
							 | 
						|
									r.Lock()
							 | 
						|
									defer r.Unlock()
							 | 
						|
								
							 | 
						|
									ts := time.Now()
							 | 
						|
									t := &LockRingSnapshot{
							 | 
						|
										servers: servers,
							 | 
						|
										ts:      ts,
							 | 
						|
									}
							 | 
						|
									r.snapshots = append(r.snapshots, t)
							 | 
						|
									for i := len(r.snapshots) - 2; i >= 0; i-- {
							 | 
						|
										r.snapshots[i+1] = r.snapshots[i]
							 | 
						|
									}
							 | 
						|
									r.snapshots[0] = t
							 | 
						|
								
							 | 
						|
									if r.onTakeSnapshot != nil {
							 | 
						|
										r.onTakeSnapshot(t.servers)
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (r *LockRing) compactSnapshots() {
							 | 
						|
									r.Lock()
							 | 
						|
									defer r.Unlock()
							 | 
						|
								
							 | 
						|
									// Always attempt compaction when called, regardless of lastCompactTime
							 | 
						|
									// This ensures proper cleanup even with multiple concurrent compaction requests
							 | 
						|
								
							 | 
						|
									ts := time.Now()
							 | 
						|
									// remove old snapshots
							 | 
						|
									recentSnapshotIndex := 1
							 | 
						|
									for ; recentSnapshotIndex < len(r.snapshots); recentSnapshotIndex++ {
							 | 
						|
										if ts.Sub(r.snapshots[recentSnapshotIndex].ts) > r.snapshotInterval {
							 | 
						|
											break
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
									// keep the one that has been running for a while
							 | 
						|
									if recentSnapshotIndex+1 <= len(r.snapshots) {
							 | 
						|
										r.snapshots = r.snapshots[:recentSnapshotIndex+1]
							 | 
						|
									}
							 | 
						|
									r.lastCompactTime = ts
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (r *LockRing) getSortedServers() []pb.ServerAddress {
							 | 
						|
									sortedServers := make([]pb.ServerAddress, 0, len(r.candidateServers))
							 | 
						|
									for server := range r.candidateServers {
							 | 
						|
										sortedServers = append(sortedServers, server)
							 | 
						|
									}
							 | 
						|
									sort.Slice(sortedServers, func(i, j int) bool {
							 | 
						|
										return sortedServers[i] < sortedServers[j]
							 | 
						|
									})
							 | 
						|
									return sortedServers
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (r *LockRing) GetSnapshot() (servers []pb.ServerAddress) {
							 | 
						|
									r.RLock()
							 | 
						|
									defer r.RUnlock()
							 | 
						|
								
							 | 
						|
									if len(r.snapshots) == 0 {
							 | 
						|
										return
							 | 
						|
									}
							 | 
						|
									return r.snapshots[0].servers
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// WaitForCleanup waits for all pending cleanup operations to complete
							 | 
						|
								// This is useful for testing to ensure deterministic behavior
							 | 
						|
								func (r *LockRing) WaitForCleanup() {
							 | 
						|
									r.cleanupWg.Wait()
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// GetSnapshotCount safely returns the number of snapshots for testing
							 | 
						|
								func (r *LockRing) GetSnapshotCount() int {
							 | 
						|
									r.RLock()
							 | 
						|
									defer r.RUnlock()
							 | 
						|
									return len(r.snapshots)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func hashKeyToServer(key string, servers []pb.ServerAddress) pb.ServerAddress {
							 | 
						|
									if len(servers) == 0 {
							 | 
						|
										return ""
							 | 
						|
									}
							 | 
						|
									x := util.HashStringToLong(key)
							 | 
						|
									if x < 0 {
							 | 
						|
										x = -x
							 | 
						|
									}
							 | 
						|
									x = x % int64(len(servers))
							 | 
						|
									return servers[x]
							 | 
						|
								}
							 |