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 }