github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/pkg/shardddl/pessimism/lock.go (about)

     1  // Copyright 2020 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package pessimism
    15  
    16  import (
    17  	"sync"
    18  
    19  	"github.com/pingcap/tiflow/dm/master/metrics"
    20  	"github.com/pingcap/tiflow/dm/pkg/terror"
    21  	"github.com/pingcap/tiflow/dm/pkg/utils"
    22  )
    23  
    24  // Lock represents the shard DDL lock in memory.
    25  // This information does not need to be persistent, and can be re-constructed from the shard DDL info.
    26  type Lock struct {
    27  	mu sync.RWMutex
    28  
    29  	ID     string   // lock's ID
    30  	Task   string   // lock's corresponding task name
    31  	Owner  string   // Owner's source ID (not DM-worker's name)
    32  	DDLs   []string // DDL statements
    33  	remain int      // remain count of sources needed to receive DDL info
    34  
    35  	// whether the DDL info received from the source.
    36  	// if all of them have been ready, then we call the lock `synced`.
    37  	ready map[string]bool
    38  
    39  	// whether the operations have done (exec/skip the shard DDL).
    40  	// if all of them have done, then we call the lock `resolved`.
    41  	done map[string]bool
    42  }
    43  
    44  // NewLock creates a new Lock instance.
    45  func NewLock(id, task, owner string, ddls, sources []string) *Lock {
    46  	l := &Lock{
    47  		ID:     id,
    48  		Task:   task,
    49  		Owner:  owner,
    50  		DDLs:   ddls,
    51  		remain: len(sources),
    52  		ready:  make(map[string]bool),
    53  		done:   make(map[string]bool),
    54  	}
    55  	for _, s := range sources {
    56  		l.ready[s] = false
    57  		l.done[s] = false
    58  	}
    59  	metrics.ReportDDLPending(task, metrics.DDLPendingNone, metrics.DDLPendingUnSynced)
    60  
    61  	return l
    62  }
    63  
    64  // TrySync tries to sync the lock, does decrease on remain, re-entrant.
    65  // new upstream sources may join when the DDL lock is in syncing,
    66  // so we need to merge these new sources.
    67  func (l *Lock) TrySync(caller string, ddls, sources []string) (bool, int, error) {
    68  	l.mu.Lock()
    69  	defer l.mu.Unlock()
    70  
    71  	// check DDL statement first.
    72  	if !utils.CompareShardingDDLs(ddls, l.DDLs) {
    73  		return l.remain <= 0, l.remain, terror.ErrMasterShardingDDLDiff.Generate(l.DDLs, ddls)
    74  	}
    75  
    76  	// try to merge any newly joined sources.
    77  	for _, s := range sources {
    78  		if _, ok := l.ready[s]; !ok {
    79  			l.remain++
    80  			l.ready[s] = false
    81  			l.done[s] = false // mark as not-done for newly joined sources.
    82  		}
    83  	}
    84  
    85  	// only `sync` once.
    86  	if synced, ok := l.ready[caller]; ok && !synced {
    87  		l.remain--
    88  		l.ready[caller] = true
    89  		if l.remain == 0 {
    90  			metrics.ReportDDLPending(l.Task, metrics.DDLPendingUnSynced, metrics.DDLPendingSynced)
    91  		}
    92  	}
    93  
    94  	return l.remain <= 0, l.remain, nil
    95  }
    96  
    97  // ForceSynced forces to mark the lock as synced.
    98  func (l *Lock) ForceSynced() {
    99  	l.mu.Lock()
   100  	defer l.mu.Unlock()
   101  
   102  	if l.remain > 0 {
   103  		metrics.ReportDDLPending(l.Task, metrics.DDLPendingUnSynced, metrics.DDLPendingSynced)
   104  	}
   105  	for source := range l.ready {
   106  		l.ready[source] = true
   107  	}
   108  	l.remain = 0
   109  }
   110  
   111  // RevertSynced reverts the synced stage of the sources.
   112  func (l *Lock) RevertSynced(sources []string) {
   113  	l.mu.Lock()
   114  	defer l.mu.Unlock()
   115  
   116  	if l.remain == 0 && len(sources) > 0 {
   117  		metrics.ReportDDLPending(l.Task, metrics.DDLPendingSynced, metrics.DDLPendingUnSynced)
   118  	}
   119  	for _, source := range sources {
   120  		if synced, ok := l.ready[source]; ok && synced {
   121  			l.ready[source] = false
   122  			l.remain++
   123  		}
   124  	}
   125  }
   126  
   127  // IsSynced returns whether the lock has synced.
   128  func (l *Lock) IsSynced() (bool, int) {
   129  	l.mu.RLock()
   130  	defer l.mu.RUnlock()
   131  	return l.remain <= 0, l.remain
   132  }
   133  
   134  // Ready returns the sources sync status or whether they are ready.
   135  func (l *Lock) Ready() map[string]bool {
   136  	l.mu.RLock()
   137  	defer l.mu.RUnlock()
   138  
   139  	ret := make(map[string]bool, len(l.ready))
   140  	for k, v := range l.ready {
   141  		ret[k] = v
   142  	}
   143  	return ret
   144  }
   145  
   146  // MarkDone marks the operation of the source as done.
   147  // NOTE: we do not support revert the `done` after marked now.
   148  func (l *Lock) MarkDone(source string) {
   149  	l.mu.Lock()
   150  	defer l.mu.Unlock()
   151  
   152  	if _, ok := l.done[source]; !ok {
   153  		return // do not add it if not exists.
   154  	}
   155  	l.done[source] = true
   156  }
   157  
   158  // IsDone returns whether the operation has done.
   159  func (l *Lock) IsDone(source string) bool {
   160  	l.mu.RLock()
   161  	defer l.mu.RUnlock()
   162  
   163  	return l.done[source]
   164  }
   165  
   166  // IsResolved returns whether the lock has resolved (all operations have done).
   167  func (l *Lock) IsResolved() bool {
   168  	l.mu.RLock()
   169  	defer l.mu.RUnlock()
   170  
   171  	for _, done := range l.done {
   172  		if !done {
   173  			return false
   174  		}
   175  	}
   176  	return true
   177  }