| 162 | } |
| 163 | |
| 164 | func (l *List) Push(key string, r *core.Record, isLeft bool) error { |
| 165 | // key is seq + user_key |
| 166 | userKey, curSeq := utils.DecodeListKey([]byte(key)) |
| 167 | userKeyStr := string(userKey) |
| 168 | if l.IsExpire(userKeyStr) { |
| 169 | return ErrListNotFound |
| 170 | } |
| 171 | |
| 172 | list, ok := l.Items[userKeyStr] |
| 173 | if !ok { |
| 174 | l.Items[userKeyStr] = l.CreateListStructure() |
| 175 | list = l.Items[userKeyStr] |
| 176 | } |
| 177 | |
| 178 | // Initialize seq if not exists |
| 179 | if _, ok := l.Seq[userKeyStr]; !ok { |
| 180 | l.Seq[userKeyStr] = &HeadTailSeq{Head: InitialListSeq, Tail: InitialListSeq + 1} |
| 181 | } |
| 182 | |
| 183 | list.InsertRecord(utils.ConvertUint64ToBigEndianBytes(curSeq), r) |
| 184 | |
| 185 | // Update seq boundaries to track the next insertion positions |
| 186 | // This is important for recovery scenarios where we rebuild the index |
| 187 | // Head and Tail should always represent the next available positions for insertion |
| 188 | seq := l.Seq[userKeyStr] |
| 189 | if isLeft { |
| 190 | // LPush: Head should be the next available position on the left |
| 191 | // If current seq is the actual head, set Head to current seq - 1 |
| 192 | if curSeq <= seq.Head { |
| 193 | seq.Head = curSeq - 1 |
| 194 | } |
| 195 | } else { |
| 196 | // RPush: Tail should be the next available position on the right |
| 197 | // If current seq is at or beyond current tail, update Tail accordingly |
| 198 | if curSeq >= seq.Tail { |
| 199 | seq.Tail = curSeq + 1 |
| 200 | } |
| 201 | } |
| 202 | |
| 203 | return nil |
| 204 | } |
| 205 | |
| 206 | func (l *List) LPop(key string) (*core.Record, error) { |
| 207 | if l.IsExpire(key) { |