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  }