github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/remotestorage/internal/reliable/chan.go (about)

     1  // Copyright 2024 Dolthub, 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  // 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 reliable
    16  
    17  import (
    18  	"github.com/dolthub/dolt/go/libraries/doltcore/remotestorage/internal/circular"
    19  )
    20  
    21  // A reliable.Chan is a type of channel transformer which can be used to build
    22  // guaranteed delivery machinery. Constructed with a source channel, it will
    23  // deliver elements it receives from the source channel into its |Recv|
    24  // channel. As the elements are successfully processed by the consumer of
    25  // |Recv|, |Ack| should be called, which ensures that the oldest unacked
    26  // delivery will not be redelivered. However, if the batch of un-Ack'd messages
    27  // needs to be reprocessed, |Reset| should be called. After |Reset| returns,
    28  // the un-|Ack|d messages will be redriven through |Recv| before new messages
    29  // are read from the source channel.
    30  //
    31  // The channel that |Recv| refers to will change after a |Reset|. Chan will
    32  // close |Recv| after its source channel closes and all buffered messages have
    33  // been delivered on |Recv|. In general, reads from then channel returned by
    34  // Recv should not happen concurrently with a call to Reset - the reads and the
    35  // call are safe, but the read is not guaranteed to ever complete.
    36  //
    37  // Close should always be called on an reliable.Chan to ensure resource cleanup.
    38  type Chan[T any] struct {
    39  	// All unack'd |T|s are stored in |buff|. As they get Ackd, they get poped from here.
    40  	buff *circular.Buff[T]
    41  
    42  	// We return new |T|s from here and they go into |buff| to be delivered
    43  	// to |output|, possibly multiple times if |Reset| is called before
    44  	// they are Ackd. This is the |src| channel.
    45  	input chan T
    46  
    47  	// The current destination where we try to deliver new |T|s that have
    48  	// not been delivered since the last |Reset| call. This is a new
    49  	// channel every time |Reset| is called.
    50  	output chan T
    51  
    52  	// Request channel to ask |thread| to shutdown.
    53  	done chan struct{}
    54  
    55  	// Request channel used to ack the Front |T|. After this succeeds, the
    56  	// Ackd message will never be delivered on |output| again, even after a
    57  	// |Reset|.  The Ackd message should have already been delivered on
    58  	// |output| for this window of |Reset|. It usually indicates an error
    59  	// in logic to call this concurrently with |Reset| or with a receive
    60  	// from a channel returned by |Recv|.
    61  	ack chan struct{}
    62  
    63  	// Request channel to reset the current channel. After this succeeds,
    64  	// all unAckd |T|s in |buff| will be delivered to the new |output|
    65  	// channel.
    66  	reset chan struct{}
    67  	// Indicates |thread| is shutdown. All requests made to |thread| also
    68  	// read from this so they can return successfully if |thread| is
    69  	// already shutdown or shutsdown concurrently.
    70  
    71  	closed chan struct{}
    72  	// Used by clients to request to peek at the Front of the current
    73  	// |buff|.
    74  	front chan frontReq[T]
    75  
    76  	// Used by clients to read the current value of |c.output|.
    77  	outputCh chan chan T
    78  }
    79  
    80  type frontReq[T any] struct {
    81  	resCh chan T
    82  }
    83  
    84  func NewChan[T any](src chan T) *Chan[T] {
    85  	ret := &Chan[T]{
    86  		buff:     circular.NewBuff[T](8),
    87  		input:    src,
    88  		done:     make(chan struct{}),
    89  		ack:      make(chan struct{}),
    90  		reset:    make(chan struct{}),
    91  		closed:   make(chan struct{}),
    92  		front:    make(chan frontReq[T]),
    93  		output:   make(chan T),
    94  		outputCh: make(chan chan T),
    95  	}
    96  	go func() {
    97  		defer close(ret.closed)
    98  		ret.thread()
    99  	}()
   100  	return ret
   101  }
   102  
   103  // Returns the channel to read from in order to read |T|s that have been
   104  // delivered to |src|.
   105  func (c *Chan[T]) Recv() <-chan T {
   106  	select {
   107  	case o := <-c.outputCh:
   108  		return o
   109  	case <-c.closed:
   110  		return c.output
   111  	}
   112  }
   113  
   114  // Peek at the earliest unAckd element in |buff|. For reliable, in-order RPC
   115  // machinery, this will be the Request that corresponds to the most recently
   116  // successfully received Response.
   117  func (c *Chan[T]) Front() (T, bool) {
   118  	var res T
   119  	resCh := make(chan T)
   120  	select {
   121  	case c.front <- frontReq[T]{resCh: resCh}:
   122  		return <-resCh, true
   123  	case <-c.closed:
   124  		return res, false
   125  	}
   126  }
   127  
   128  // Acknowledge the earlier buffered |T| in |buff|, indicating that a response
   129  // was successfully received and processed for it and it will never need to be
   130  // redriven.
   131  func (c *Chan[T]) Ack() {
   132  	select {
   133  	case c.ack <- struct{}{}:
   134  	case <-c.closed:
   135  	}
   136  }
   137  
   138  // Reset the channel so that it will redrive all unacknowledged |T|s into the
   139  // channel returned from |Recv| before it sends any new |T|s that it reads from
   140  // |src|.
   141  func (c *Chan[T]) Reset() {
   142  	select {
   143  	case c.reset <- struct{}{}:
   144  	case <-c.closed:
   145  	}
   146  }
   147  
   148  // Shutdown the Chan and cleanup any resources associated with it.
   149  func (c *Chan[T]) Close() {
   150  	close(c.done)
   151  	<-c.closed
   152  }
   153  
   154  // Chan works on the basis of a request/response actor that run in its own
   155  // goroutine. All the client methods send and receive from channels,
   156  // interacting with this actor thread.
   157  func (c *Chan[T]) thread() {
   158  	input := c.input
   159  	// The current index in |buff| which we are trying to send to |output|.
   160  	// When we reset, this goes back to 0 and we start working through
   161  	// |buff| again.
   162  	outI := 0
   163  	for {
   164  		// |input| gets nil'd out when we see |c.input| close.
   165  		// If |c.input| is closed and our |buff| is empty (all buffered
   166  		// messages have been Ackd), then we are done and we shut down
   167  		// here.
   168  		if input == nil && c.buff.Len() == 0 {
   169  			return
   170  		}
   171  
   172  		// We only read a new element from |input| when we don't have
   173  		// anything buffered we're trying to send to |output|.
   174  
   175  		thisInput := input
   176  		thisOutput := c.output
   177  		var toOut T
   178  
   179  		if outI < c.buff.Len() {
   180  			// We have an element we're trying to get out...
   181  			thisInput = nil
   182  			toOut = c.buff.At(outI)
   183  		} else {
   184  			// No element we're trying to send -- read from input.
   185  			thisOutput = nil
   186  		}
   187  
   188  		select {
   189  		case <-c.done:
   190  			return
   191  		case c.outputCh <- c.output:
   192  		case t, ok := <-thisInput:
   193  			if !ok {
   194  				input = nil
   195  				// We can close |c.output| here because, since
   196  				// we are in this branch, we know we had
   197  				// nothing left to try to send to |output|. If
   198  				// |Reset| gets called, we will get a new
   199  				// output channel, and the buffer will be sent
   200  				// to it. We will make another read from
   201  				// |c.input|, and we will see the closed
   202  				// channel again, and we will close |c.output|
   203  				// again.
   204  				close(c.output)
   205  			} else {
   206  				// Add the newly read |T| to our buffer.
   207  				c.buff.Push(t)
   208  			}
   209  		case thisOutput <- toOut:
   210  			// We successfully delivered the current element in |buff|.
   211  			// The index in |buff| which we will try to deliver next is |outI + 1|.
   212  			outI += 1
   213  		case <-c.reset:
   214  			input = c.input
   215  			c.output = make(chan T)
   216  			outI = 0
   217  		case <-c.ack:
   218  			// Remove the front element from |buff| and decrement |outI|, since |buff| is one element shorter now.
   219  			c.buff.Pop()
   220  			outI -= 1
   221  		case req := <-c.front:
   222  			// Peek at the front of the buffer. Return the element on |resCh|.
   223  			req.resCh <- c.buff.Front()
   224  		}
   225  	}
   226  }