github.com/matrixorigin/matrixone@v0.7.0/pkg/txn/storage/memorystorage/memtable/physical_row.go (about)

     1  // Copyright 2022 Matrix Origin
     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  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package memtable
    16  
    17  import (
    18  	"database/sql"
    19  	"fmt"
    20  	"io"
    21  	"sync/atomic"
    22  	"time"
    23  
    24  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    25  	"github.com/matrixorigin/matrixone/pkg/txn/storage/memorystorage/memorytable"
    26  )
    27  
    28  type PhysicalRow[
    29  	K memorytable.Ordered[K],
    30  	V any,
    31  ] struct {
    32  	Key        K
    33  	LastUpdate time.Time
    34  	Versions   []Version[V]
    35  	//TODO version GC
    36  }
    37  
    38  type Version[T any] struct {
    39  	ID       int64
    40  	BornTx   *Transaction
    41  	BornTime Time
    42  	LockTx   *Transaction
    43  	LockTime Time
    44  	Value    T
    45  }
    46  
    47  var nextVersionID int64
    48  
    49  // Read reads the visible value from Values
    50  // readTime's logical time should be monotonically increasing in one transaction to reflect commands order
    51  func (p *PhysicalRow[K, V]) Read(now Time, tx *Transaction) (value V, err error) {
    52  	version, err := p.readVersion(now, tx)
    53  	if version != nil {
    54  		value = version.Value
    55  	}
    56  	return
    57  }
    58  
    59  func (p *PhysicalRow[K, V]) readVersion(now Time, tx *Transaction) (*Version[V], error) {
    60  	if tx.State.Load() != Active {
    61  		panic("should not call Read")
    62  	}
    63  
    64  	for i := len(p.Versions) - 1; i >= 0; i-- {
    65  		value := p.Versions[i]
    66  		if value.Visible(now, tx.ID, tx.IsolationPolicy.Read) {
    67  			switch tx.IsolationPolicy.Read {
    68  			case ReadCommitted:
    69  			case ReadSnapshot:
    70  				// BornTx must be committed to be visible here
    71  				if value.BornTx.ID != tx.ID && value.BornTime.After(tx.BeginTime) {
    72  					continue
    73  				}
    74  			case ReadNoStale:
    75  				// BornTx must be committed to be visible here
    76  				if value.BornTx.ID != tx.ID && value.BornTime.After(tx.BeginTime) {
    77  					return &value, moerr.NewTxnReadConflictNoCtx("%s %s", tx.ID, value.BornTx.ID)
    78  				}
    79  			}
    80  			return &value, nil
    81  		}
    82  	}
    83  
    84  	return nil, sql.ErrNoRows
    85  }
    86  
    87  func (v *Version[T]) Visible(now Time, txID string, policy ReadPolicy) bool {
    88  
    89  	// the following algorithm is from https://momjian.us/main/writings/pgsql/mvcc.pdf
    90  	// "[Mike Olson] says 17 march 1993: the tests in this routine are correct; if you think they’re not, you’re wrongand you should think about it again. i know, it happened to me."
    91  
    92  	// inserted by current tx
    93  	if v.BornTx.ID == txID {
    94  		// inserted before the read time
    95  		if v.BornTime.Before(now) {
    96  			// not been deleted
    97  			if v.LockTx == nil {
    98  				return true
    99  			}
   100  			// deleted by current tx after the read time
   101  			if v.LockTx.ID == txID && v.LockTime.After(now) {
   102  				return true
   103  			}
   104  		}
   105  	}
   106  
   107  	// inserted by a committed tx
   108  	if v.BornTx.State.Load() == Committed {
   109  		// not been deleted
   110  		if v.LockTx == nil {
   111  			// for isolation levels stricter than read-committed, instead of checking timestamps here, let the caller do it.
   112  			return true
   113  		}
   114  		// being deleted by current tx after the read time
   115  		if v.LockTx.ID == txID && v.LockTime.After(now) {
   116  			return true
   117  		}
   118  		// deleted by another tx but not committed
   119  		if v.LockTx.ID != txID && v.LockTx.State.Load() != Committed {
   120  			return true
   121  		}
   122  		// deleted by another committed tx after the read time
   123  		if policy == ReadSnapshot {
   124  			if v.LockTx.ID != txID && v.LockTx.State.Load() == Committed && v.LockTime.After(now) {
   125  				return true
   126  			}
   127  		}
   128  	}
   129  
   130  	return false
   131  }
   132  
   133  func (p *PhysicalRow[K, V]) Insert(
   134  	now Time,
   135  	tx *Transaction,
   136  	value V,
   137  ) (
   138  	newRow *PhysicalRow[K, V],
   139  	version *Version[V],
   140  	err error,
   141  ) {
   142  
   143  	if tx.State.Load() != Active {
   144  		panic("should not call Insert")
   145  	}
   146  
   147  	for i := len(p.Versions) - 1; i >= 0; i-- {
   148  		value := p.Versions[i]
   149  		if value.Visible(now, tx.ID, tx.IsolationPolicy.Read) {
   150  			if value.LockTx != nil && value.LockTx.State.Load() != Aborted {
   151  				// locked by active or committed tx
   152  				return nil, nil, moerr.NewTxnWriteConflictNoCtx("%s %s", tx.ID, value.LockTx.ID)
   153  			}
   154  			if value.BornTx.ID != tx.ID && value.BornTime.After(tx.BeginTime) {
   155  				return nil, nil, moerr.NewTxnWriteConflictNoCtx("%s %s", tx.ID, value.BornTx.ID)
   156  			}
   157  		}
   158  	}
   159  
   160  	p = p.clone()
   161  	p.LastUpdate = time.Now()
   162  
   163  	version = &Version[V]{
   164  		ID:       atomic.AddInt64(&nextVersionID, 1),
   165  		BornTx:   tx,
   166  		BornTime: now,
   167  		Value:    value,
   168  	}
   169  	p.Versions = append(p.Versions, *version)
   170  
   171  	return p, version, nil
   172  }
   173  
   174  func (p *PhysicalRow[K, V]) Delete(
   175  	now Time,
   176  	tx *Transaction,
   177  ) (
   178  	newRow *PhysicalRow[K, V],
   179  	version *Version[V],
   180  	err error,
   181  ) {
   182  	if tx.State.Load() != Active {
   183  		panic("should not call Delete")
   184  	}
   185  
   186  	for i := len(p.Versions) - 1; i >= 0; i-- {
   187  		value := p.Versions[i]
   188  		if value.Visible(now, tx.ID, tx.IsolationPolicy.Read) {
   189  			if value.LockTx != nil && value.LockTx.State.Load() != Aborted {
   190  				return nil, nil, moerr.NewTxnWriteConflictNoCtx("%s %s", tx.ID, value.LockTx.ID)
   191  			}
   192  			if value.BornTx.ID != tx.ID && value.BornTime.After(tx.BeginTime) {
   193  				return nil, nil, moerr.NewTxnWriteConflictNoCtx("%s %s", tx.ID, value.BornTx.ID)
   194  			}
   195  
   196  			p = p.clone()
   197  			p.LastUpdate = time.Now()
   198  			value.LockTx = tx
   199  			value.LockTime = now
   200  			p.Versions[i] = value
   201  
   202  			return p, &value, nil
   203  		}
   204  	}
   205  
   206  	return nil, nil, sql.ErrNoRows
   207  }
   208  
   209  func (p *PhysicalRow[K, V]) Update(
   210  	now Time,
   211  	tx *Transaction,
   212  	newValue V,
   213  ) (
   214  	newRow *PhysicalRow[K, V],
   215  	version *Version[V],
   216  	err error,
   217  ) {
   218  
   219  	if tx.State.Load() != Active {
   220  		panic("should not call Update")
   221  	}
   222  
   223  	for i := len(p.Versions) - 1; i >= 0; i-- {
   224  		value := p.Versions[i]
   225  		if value.Visible(now, tx.ID, tx.IsolationPolicy.Read) {
   226  
   227  			if value.LockTx != nil && value.LockTx.State.Load() != Aborted {
   228  				return nil, nil, moerr.NewTxnWriteConflictNoCtx("%s %s", tx.ID, value.LockTx.ID)
   229  			}
   230  
   231  			if value.BornTx.ID != tx.ID && value.BornTime.After(tx.BeginTime) {
   232  				return nil, nil, moerr.NewTxnWriteConflictNoCtx("%s %s", tx.ID, value.BornTx.ID)
   233  			}
   234  
   235  			p = p.clone()
   236  			p.LastUpdate = time.Now()
   237  
   238  			value.LockTx = tx
   239  			value.LockTime = now
   240  			p.Versions[i] = value
   241  
   242  			version = &Version[V]{
   243  				ID:       atomic.AddInt64(&nextVersionID, 1),
   244  				BornTx:   tx,
   245  				BornTime: now,
   246  				Value:    newValue,
   247  			}
   248  			p.Versions = append(p.Versions, *version)
   249  
   250  			return p, version, nil
   251  		}
   252  	}
   253  
   254  	return nil, nil, sql.ErrNoRows
   255  }
   256  
   257  func (p *PhysicalRow[K, V]) clone() *PhysicalRow[K, V] {
   258  	newRow := *p
   259  	newRow.Versions = make([]Version[V], len(p.Versions))
   260  	copy(newRow.Versions, p.Versions)
   261  	return &newRow
   262  }
   263  
   264  func (p *PhysicalRow[K, V]) dump(w io.Writer) {
   265  	for _, value := range p.Versions {
   266  		fmt.Fprintf(w, "born tx %s, born time %s, value %v",
   267  			value.BornTx.ID,
   268  			value.BornTime.String(),
   269  			value.Value,
   270  		)
   271  		if value.LockTx != nil {
   272  			fmt.Fprintf(w, " lock tx %s, lock time %s",
   273  				value.LockTx.ID,
   274  				value.LockTime.String(),
   275  			)
   276  		}
   277  		fmt.Fprintf(w, "\n")
   278  	}
   279  }