github.com/angenalZZZ/gofunc@v0.0.0-20210507121333-48ff1be3917b/data/queue/prefix_queue.go (about) 1 package queue 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "encoding/gob" 7 "github.com/angenalZZZ/gofunc/f" 8 "os" 9 "sync" 10 11 "github.com/syndtr/goleveldb/leveldb" 12 "github.com/syndtr/goleveldb/leveldb/errors" 13 ) 14 15 // prefixDelimiter defines the delimiter used to separate a prefix from an 16 // item ID within the LevelDB database. We use the lowest possible value for 17 // a single byte, 0x00 (null), as the delimiter. 18 const prefixDelimiter byte = '\x00' 19 20 // queue defines the unique queue for a prefix. 21 type queue struct { 22 Head uint64 23 Tail uint64 24 } 25 26 // Length returns the total number of items in the queue. 27 func (q *queue) Length() uint64 { 28 return q.Tail - q.Head 29 } 30 31 // PrefixQueue is a standard FIFO (first in, first out) queue that separates 32 // each given prefix into its own queue. 33 type PrefixQueue struct { 34 sync.RWMutex 35 DataDir string 36 db *leveldb.DB 37 size uint64 38 isOpen bool 39 } 40 41 // OpenPrefixQueue opens a prefix queue if one exists at the given directory. 42 // If one does not already exist, a new prefix queue is created. 43 func OpenPrefixQueue(dataDir string) (*PrefixQueue, error) { 44 var err error 45 46 // Create a new Queue. 47 pq := &PrefixQueue{ 48 DataDir: dataDir, 49 db: &leveldb.DB{}, 50 isOpen: false, 51 } 52 53 // Open database for the prefix queue. 54 pq.db, err = leveldb.OpenFile(dataDir, nil) 55 if err != nil { 56 return nil, err 57 } 58 59 // Check if this queue type can open the requested data directory. 60 ok, err := checkQueueType(dataDir, queuePrefixQueue) 61 if err != nil { 62 return nil, err 63 } 64 if !ok { 65 return nil, ErrIncompatibleType 66 } 67 68 // SetHeader isOpen and return. 69 pq.isOpen = true 70 return pq, pq.init() 71 } 72 73 // Enqueue adds an item to the queue. 74 func (pq *PrefixQueue) Enqueue(prefix, value []byte) (*Item, error) { 75 pq.Lock() 76 defer pq.Unlock() 77 78 // Check if queue is closed. 79 if !pq.isOpen { 80 return nil, ErrDBClosed 81 } 82 83 // GetHeader the queue for this prefix. 84 q, err := pq.getOrCreateQueue(prefix) 85 if err != nil { 86 return nil, err 87 } 88 89 // Create new Item. 90 item := &Item{ 91 ID: q.Tail + 1, 92 Key: generateKeyPrefixID(prefix, q.Tail+1), 93 Value: value, 94 } 95 96 // Add it to the queue. 97 if err := pq.db.Put(item.Key, item.Value, nil); err != nil { 98 return nil, err 99 } 100 101 // Increment tail position and prefix queue size. 102 q.Tail++ 103 pq.size++ 104 105 // Save the queue. 106 if err := pq.saveQueue(prefix, q); err != nil { 107 return nil, err 108 } 109 110 // Save main prefix queue data. 111 if err := pq.save(); err != nil { 112 return nil, err 113 } 114 115 return item, nil 116 } 117 118 // EnqueueString is a helper function for Enqueue that accepts the prefix and 119 // value as a string rather than a byte slice. 120 func (pq *PrefixQueue) EnqueueString(prefix, value string) (*Item, error) { 121 return pq.Enqueue([]byte(prefix), []byte(value)) 122 } 123 124 // EnqueueObject is a helper function for Enqueue that accepts any 125 // value type, which is then encoded into a byte slice using 126 // encoding/gob. 127 // 128 // Objects containing pointers with zero values will decode to nil 129 // when using this function. This is due to how the encoding/gob 130 // package works. Because of this, you should only use this function 131 // to encode simple types. 132 func (pq *PrefixQueue) EnqueueObject(prefix []byte, value interface{}) (*Item, error) { 133 var buffer bytes.Buffer 134 enc := gob.NewEncoder(&buffer) 135 if err := enc.Encode(value); err != nil { 136 return nil, err 137 } 138 139 return pq.Enqueue(prefix, buffer.Bytes()) 140 } 141 142 // EnqueueObjectAsJSON is a helper function for Enqueue that accepts 143 // any value type, which is then encoded into a JSON byte slice using 144 // encoding/json. 145 // 146 // Use this function to handle encoding of complex types. 147 func (pq *PrefixQueue) EnqueueObjectAsJSON(prefix []byte, value interface{}) (*Item, error) { 148 jsonBytes, err := f.EncodeJson(value) 149 if err != nil { 150 return nil, err 151 } 152 153 return pq.Enqueue(prefix, jsonBytes) 154 } 155 156 // Dequeue removes the next item in the prefix queue and returns it. 157 func (pq *PrefixQueue) Dequeue(prefix []byte) (*Item, error) { 158 pq.Lock() 159 defer pq.Unlock() 160 161 // Check if queue is closed. 162 if !pq.isOpen { 163 return nil, ErrDBClosed 164 } 165 166 // GetHeader the queue for this prefix. 167 q, err := pq.getQueue(prefix) 168 if err != nil { 169 return nil, err 170 } 171 172 // Try to get the next item in the queue. 173 item, err := pq.getItemByPrefixID(prefix, q.Head+1) 174 if err != nil { 175 return nil, err 176 } 177 178 // Remove this item from the queue. 179 if err := pq.db.Delete(item.Key, nil); err != nil { 180 return nil, err 181 } 182 183 // Increment head position and decrement prefix queue size. 184 q.Head++ 185 pq.size-- 186 187 // Save the queue. 188 if err := pq.saveQueue(prefix, q); err != nil { 189 return nil, err 190 } 191 192 // Save main prefix queue data. 193 if err := pq.save(); err != nil { 194 return nil, err 195 } 196 197 return item, nil 198 } 199 200 // DequeueString is a helper function for Dequeue that accepts the prefix as a 201 // string rather than a byte slice. 202 func (pq *PrefixQueue) DequeueString(prefix string) (*Item, error) { 203 return pq.Dequeue([]byte(prefix)) 204 } 205 206 // Peek returns the next item in the given queue without removing it. 207 func (pq *PrefixQueue) Peek(prefix []byte) (*Item, error) { 208 pq.RLock() 209 defer pq.RUnlock() 210 211 // Check if queue is closed. 212 if !pq.isOpen { 213 return nil, ErrDBClosed 214 } 215 216 // GetHeader the queue for this prefix. 217 q, err := pq.getQueue(prefix) 218 if err != nil { 219 return nil, err 220 } 221 222 return pq.getItemByPrefixID(prefix, q.Head+1) 223 } 224 225 // PeekString is a helper function for Peek that accepts the prefix as a 226 // string rather than a byte slice. 227 func (pq *PrefixQueue) PeekString(prefix string) (*Item, error) { 228 return pq.Peek([]byte(prefix)) 229 } 230 231 // PeekByID returns the item with the given ID without removing it. 232 func (pq *PrefixQueue) PeekByID(prefix []byte, id uint64) (*Item, error) { 233 pq.RLock() 234 defer pq.RUnlock() 235 236 // Check if queue is closed. 237 if !pq.isOpen { 238 return nil, ErrDBClosed 239 } 240 241 return pq.getItemByPrefixID(prefix, id) 242 } 243 244 // PeekByIDString is a helper function for Peek that accepts the prefix as a 245 // string rather than a byte slice. 246 func (pq *PrefixQueue) PeekByIDString(prefix string, id uint64) (*Item, error) { 247 return pq.PeekByID([]byte(prefix), id) 248 } 249 250 // Update updates an item in the given queue without changing its position. 251 func (pq *PrefixQueue) Update(prefix []byte, id uint64, newValue []byte) (*Item, error) { 252 pq.Lock() 253 defer pq.Unlock() 254 255 // Check if queue is closed. 256 if !pq.isOpen { 257 return nil, ErrDBClosed 258 } 259 260 // GetHeader the queue for this prefix. 261 q, err := pq.getQueue(prefix) 262 if err != nil { 263 return nil, err 264 } 265 266 // Check if item exists in queue. 267 if id <= q.Head || id > q.Tail { 268 return nil, ErrOutOfBounds 269 } 270 271 // Create new Item. 272 item := &Item{ 273 ID: id, 274 Key: generateKeyPrefixID(prefix, id), 275 Value: newValue, 276 } 277 278 // Update this item in the queue. 279 if err := pq.db.Put(item.Key, item.Value, nil); err != nil { 280 return nil, err 281 } 282 283 return item, nil 284 } 285 286 // UpdateString is a helper function for Update that accepts the prefix and 287 // value as a string rather than a byte slice. 288 func (pq *PrefixQueue) UpdateString(prefix string, id uint64, value string) (*Item, error) { 289 return pq.Update([]byte(prefix), id, []byte(value)) 290 } 291 292 // UpdateObject is a helper function for Update that accepts any 293 // value type, which is then encoded into a byte slice using 294 // encoding/gob. 295 // 296 // Objects containing pointers with zero values will decode to nil 297 // when using this function. This is due to how the encoding/gob 298 // package works. Because of this, you should only use this function 299 // to encode simple types. 300 func (pq *PrefixQueue) UpdateObject(prefix []byte, id uint64, newValue interface{}) (*Item, error) { 301 var buffer bytes.Buffer 302 enc := gob.NewEncoder(&buffer) 303 if err := enc.Encode(newValue); err != nil { 304 return nil, err 305 } 306 return pq.Update(prefix, id, buffer.Bytes()) 307 } 308 309 // UpdateObjectAsJSON is a helper function for Update that accepts 310 // any value type, which is then encoded into a JSON byte slice using 311 // encoding/json. 312 // 313 // Use this function to handle encoding of complex types. 314 func (pq *PrefixQueue) UpdateObjectAsJSON(prefix []byte, id uint64, newValue interface{}) (*Item, error) { 315 jsonBytes, err := f.EncodeJson(newValue) 316 if err != nil { 317 return nil, err 318 } 319 320 return pq.Update(prefix, id, jsonBytes) 321 } 322 323 // Length returns the total number of items in the prefix queue. 324 func (pq *PrefixQueue) Length() uint64 { 325 return pq.size 326 } 327 328 // Close closes the LevelDB database of the prefix queue. 329 func (pq *PrefixQueue) Close() error { 330 pq.Lock() 331 defer pq.Unlock() 332 333 // Check if queue is already closed. 334 if !pq.isOpen { 335 return nil 336 } 337 338 // Close the LevelDB database. 339 if err := pq.db.Close(); err != nil { 340 return err 341 } 342 343 // Reset size and set isOpen to false. 344 pq.size = 0 345 pq.isOpen = false 346 347 return nil 348 } 349 350 // Drop closes and deletes the LevelDB database of the prefix queue. 351 func (pq *PrefixQueue) Drop() error { 352 if err := pq.Close(); err != nil { 353 return err 354 } 355 356 return os.RemoveAll(pq.DataDir) 357 } 358 359 // getQueue gets the unique queue for the given prefix. 360 func (pq *PrefixQueue) getQueue(prefix []byte) (*queue, error) { 361 // Try to get the queue gob value. 362 qval, err := pq.db.Get(generateKeyPrefixData(prefix), nil) 363 if err == errors.ErrNotFound { 364 return nil, ErrEmpty 365 } else if err != nil { 366 return nil, err 367 } 368 369 // Decode gob to our queue type. 370 q := &queue{} 371 buffer := bytes.NewBuffer(qval) 372 dec := gob.NewDecoder(buffer) 373 return q, dec.Decode(q) 374 } 375 376 // getOrCreateQueue gets the unique queue for the given prefix. If one does not 377 // already exist, a new queue is created. 378 func (pq *PrefixQueue) getOrCreateQueue(prefix []byte) (*queue, error) { 379 // Try to get the queue gob value. 380 qval, err := pq.db.Get(generateKeyPrefixData(prefix), nil) 381 if err == errors.ErrNotFound { 382 return &queue{}, nil 383 } else if err != nil { 384 return nil, err 385 } 386 387 // Decode gob to our queue type. 388 q := &queue{} 389 buffer := bytes.NewBuffer(qval) 390 dec := gob.NewDecoder(buffer) 391 return q, dec.Decode(q) 392 } 393 394 // savePrefixQueue saves the given queue for the given prefix. 395 func (pq *PrefixQueue) saveQueue(prefix []byte, q *queue) error { 396 // Encode the queue using gob. 397 var buffer bytes.Buffer 398 enc := gob.NewEncoder(&buffer) 399 if err := enc.Encode(q); err != nil { 400 return err 401 } 402 403 // Save it to the database. 404 return pq.db.Put(generateKeyPrefixData(prefix), buffer.Bytes(), nil) 405 } 406 407 // save saves the main prefix queue data. 408 func (pq *PrefixQueue) save() error { 409 val := make([]byte, 8) 410 binary.BigEndian.PutUint64(val, pq.size) 411 return pq.db.Put(pq.getDataKey(), val, nil) 412 } 413 414 // getDataKey generates the main prefix queue data key. 415 func (pq *PrefixQueue) getDataKey() []byte { 416 var key []byte 417 key = append(key, prefixDelimiter) 418 return append(key, []byte(":main_data")...) 419 } 420 421 // getItemByPrefixID returns an item, if found, for the given prefix and ID. 422 func (pq *PrefixQueue) getItemByPrefixID(prefix []byte, id uint64) (*Item, error) { 423 // Check if empty. 424 if pq.size == 0 { 425 return nil, ErrEmpty 426 } 427 428 // GetHeader the queue for this prefix. 429 q, err := pq.getQueue(prefix) 430 if err != nil { 431 return nil, err 432 } 433 434 // Check if out of bounds. 435 if id <= q.Head || id > q.Tail { 436 return nil, ErrOutOfBounds 437 } 438 439 // GetHeader item from database. 440 item := &Item{ 441 ID: id, 442 Key: generateKeyPrefixID(prefix, id), 443 } 444 445 if item.Value, err = pq.db.Get(item.Key, nil); err != nil { 446 return nil, err 447 } 448 449 return item, nil 450 } 451 452 // init initializes the prefix queue data. 453 func (pq *PrefixQueue) init() error { 454 // GetHeader the main prefix queue data. 455 val, err := pq.db.Get(pq.getDataKey(), nil) 456 if err == errors.ErrNotFound { 457 return nil 458 } else if err != nil { 459 return err 460 } 461 462 pq.size = binary.BigEndian.Uint64(val) 463 return nil 464 } 465 466 // generateKeyPrefixData generates a data key using the given prefix. This key 467 // should be used to get the stored queue struct for the given prefix. 468 func generateKeyPrefixData(prefix []byte) []byte { 469 return append(prefix, []byte(":data")...) 470 } 471 472 // generateKeyPrefixID generates a key using the given prefix and ID. 473 func generateKeyPrefixID(prefix []byte, id uint64) []byte { 474 // Handle the prefix. 475 key := append(prefix, prefixDelimiter) 476 477 // Handle the item ID. 478 key = append(key, idToKey(id)...) 479 480 return key 481 }