(m uhaha.Machine, args []string)
| 195 | } |
| 196 | |
| 197 | func cmdRPOPLPUSH(m uhaha.Machine, args []string) (interface{}, error) { |
| 198 | |
| 199 | if len(args) != 3 { |
| 200 | return nil, uhaha.ErrWrongNumArgs |
| 201 | } |
| 202 | source, dest := []byte(args[1]), []byte(args[2]) |
| 203 | |
| 204 | var ttl int64 = -1 |
| 205 | var expireTime int64 = -1 |
| 206 | if bytes.Compare(source, dest) == 0 { |
| 207 | var err error |
| 208 | ttl, err = ldb.LTTL(source) |
| 209 | if err != nil { |
| 210 | return nil, err |
| 211 | } |
| 212 | if ttl != -1 { |
| 213 | expireTime = m.Now().Unix() + ttl |
| 214 | } |
| 215 | } |
| 216 | |
| 217 | // Special handling for same source and destination (rotation) |
| 218 | if bytes.Compare(source, dest) == 0 { |
| 219 | // Use global mutex for same source/destination operations |
| 220 | // to ensure absolute atomicity |
| 221 | sameKeyMutex.Lock() |
| 222 | defer sameKeyMutex.Unlock() |
| 223 | |
| 224 | // Get the last element |
| 225 | data, err := ldb.RPop(source) |
| 226 | if err != nil { |
| 227 | return nil, err |
| 228 | } |
| 229 | |
| 230 | if data == nil { |
| 231 | return nil, nil |
| 232 | } |
| 233 | |
| 234 | // Push it back to the front |
| 235 | if _, err := ldb.LPush(source, data); err != nil { |
| 236 | // If push fails, revert the pop |
| 237 | ldb.RPush(source, data) |
| 238 | return nil, err |
| 239 | } |
| 240 | |
| 241 | //reset ttl using absolute time |
| 242 | if expireTime != -1 { |
| 243 | ldb.LExpireAt(source, expireTime) |
| 244 | } |
| 245 | |
| 246 | return data, nil |
| 247 | } |
| 248 | |
| 249 | // For different source and destination, we need to lock both keys |
| 250 | // to prevent deadlocks, we always lock in a consistent order |
| 251 | var firstKey, secondKey []byte |
| 252 | if bytes.Compare(source, dest) < 0 { |
| 253 | firstKey = source |
| 254 | secondKey = dest |
nothing calls this directly
no test coverage detected