github.com/balzaczyy/golucene@v0.0.0-20151210033525-d0be9ee89713/core/index/deleteQueue.go (about)

     1  package index
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  	"sync/atomic"
     7  )
     8  
     9  // index/DocumentsWriterDeleteQueue.java
    10  
    11  /*
    12  DocumentsWriterDeleteQueue is a non-blocking linked pending deletes
    13  queue. In contrast to other queue implementation we only maintain the
    14  tail of the queue. A delete queue is always used in a context of a
    15  set of DWPTs and a global delete pool. Each of the DWPT and the
    16  global pool need to maintain their 'own' head of the queue (as a
    17  DeleteSlice instance per DWPT). The difference between the DWPT and
    18  the global pool is that the DWPT starts maintaining a head once it
    19  has added its first document since for its segments private deletes
    20  only the deletes after that document are relevant. The global pool
    21  instead starts maintaining the head once this instance is created by
    22  taking the sentinel instance as its initial head.
    23  
    24  Since each DeleteSlice maintains its own head and list is only single
    25  linked, the garbage collector takes care of pruning the list for us.
    26  All nodes in the list that are still relevant should be either
    27  directly or indirectly referenced by one of the DWPT's private
    28  DeleteSlice or by the global BufferedUpdates slice.
    29  
    30  Each DWPT as well as the global delete pool maintain their private
    31  DeleteSlice instance. In the DWPT case, updating a slice is equivalent
    32  to atomically finishing the document. The slice update guarantees a
    33  "happens before" relationship to all other updates in the same
    34  indexing session. When a DWPT updates a document it:
    35  
    36  1. consumes a document and finishes its processing
    37  2. updates its private DeleteSlice either by calling updateSlice() or
    38     addTermToDeleteSlice() (if the document has a delTerm)
    39  3. applies all deletes in the slice to its private BufferedUpdates
    40     and resets it
    41  4. increments its internal document id
    42  
    43  The DWPT also doesn't apply its current docments delete term until it
    44  has updated its delete slice which ensures the consistency of the
    45  update. If the update fails before the DeleteSlice could have been
    46  updated the deleteTerm will also not be added to its private deletes
    47  neither to the global deletes.
    48  */
    49  type DocumentsWriterDeleteQueue struct {
    50  	tail                  *Node // volatile
    51  	globalSlice           *DeleteSlice
    52  	globalBufferedUpdates *BufferedUpdates
    53  	globalBufferLock      sync.Locker
    54  
    55  	generation int64
    56  }
    57  
    58  func newDocumentsWriterDeleteQueue() *DocumentsWriterDeleteQueue {
    59  	return newDocumentsWriterDeleteQueueWithGeneration(0)
    60  }
    61  
    62  func newDocumentsWriterDeleteQueueWithGeneration(generation int64) *DocumentsWriterDeleteQueue {
    63  	return newDocumentsWriterDeleteQueueWith(newBufferedUpdates(), generation)
    64  }
    65  
    66  func newDocumentsWriterDeleteQueueWith(globalBufferedUpdates *BufferedUpdates, generation int64) *DocumentsWriterDeleteQueue {
    67  	tail := newNode(nil)
    68  	return &DocumentsWriterDeleteQueue{
    69  		globalBufferedUpdates: globalBufferedUpdates,
    70  		globalBufferLock:      &sync.Mutex{},
    71  		generation:            generation,
    72  		// we use a sentinel instance as our initial tail. No slice will
    73  		// ever try to apply this tail since the head is always omitted.
    74  		tail:        tail, // sentinel
    75  		globalSlice: newDeleteSlice(tail),
    76  	}
    77  }
    78  
    79  /* Invariant for document update */
    80  func (q *DocumentsWriterDeleteQueue) add(term *Term, slice *DeleteSlice) {
    81  	panic("not implemented yet")
    82  }
    83  
    84  func (dq *DocumentsWriterDeleteQueue) freezeGlobalBuffer(callerSlice *DeleteSlice) *FrozenBufferedUpdates {
    85  	dq.globalBufferLock.Lock()
    86  	defer dq.globalBufferLock.Unlock()
    87  
    88  	// Here we freeze the global buffer so we need to lock it, apply
    89  	// all deletes in the queue and reset the global slice to let the
    90  	// GC prune the queue.
    91  	currentTail := dq.tail
    92  	// take the current tail and make this local. Any changes after
    93  	// this call are applied later and not relevant here
    94  	if callerSlice != nil {
    95  		// update the callers slices so we are on the same page
    96  		callerSlice.tail = currentTail
    97  	}
    98  	if dq.globalSlice.tail != currentTail {
    99  		dq.globalSlice.tail = currentTail
   100  		dq.globalSlice.apply(dq.globalBufferedUpdates, MAX_INT)
   101  	}
   102  
   103  	packet := freezeBufferedUpdates(dq.globalBufferedUpdates, false)
   104  	dq.globalBufferedUpdates.clear()
   105  	return packet
   106  }
   107  
   108  func (dq *DocumentsWriterDeleteQueue) anyChanges() bool {
   109  	dq.globalBufferLock.Lock()
   110  	defer dq.globalBufferLock.Unlock()
   111  	// check if all items in the global slice were applied
   112  	// and if the global slice is up-to-date
   113  	// and if globalBufferedUpdates has changes
   114  	return dq.globalBufferedUpdates.any() ||
   115  		!dq.globalSlice.isEmpty() ||
   116  		dq.globalSlice.tail != dq.tail ||
   117  		dq.tail.next != nil
   118  }
   119  
   120  func (dq *DocumentsWriterDeleteQueue) newSlice() *DeleteSlice {
   121  	return newDeleteSlice(dq.tail)
   122  }
   123  
   124  func (q *DocumentsWriterDeleteQueue) updateSlice(slice *DeleteSlice) bool {
   125  	if slice.tail != q.tail { // if we are the same just
   126  		slice.tail = q.tail
   127  		return true
   128  	}
   129  	return false
   130  }
   131  
   132  func (dq *DocumentsWriterDeleteQueue) clear() {
   133  	dq.globalBufferLock.Lock()
   134  	defer dq.globalBufferLock.Unlock()
   135  
   136  	currentTail := dq.tail
   137  	dq.globalSlice.head, dq.globalSlice.tail = currentTail, currentTail
   138  	dq.globalBufferedUpdates.clear()
   139  }
   140  
   141  func (q *DocumentsWriterDeleteQueue) RamBytesUsed() int64 {
   142  	return atomic.LoadInt64(&q.globalBufferedUpdates.bytesUsed)
   143  }
   144  
   145  func (dq *DocumentsWriterDeleteQueue) String() string {
   146  	return fmt.Sprintf("DWDQ: [ generation: %v ]", dq.generation)
   147  }
   148  
   149  type DeleteSlice struct {
   150  	head *Node // we don't apply this one
   151  	tail *Node
   152  }
   153  
   154  func newDeleteSlice(currentTail *Node) *DeleteSlice {
   155  	assert(currentTail != nil)
   156  	// Initially this is a 0 length slice pointing to the 'current'
   157  	// tail of the queue. Once we update the slice we only need to
   158  	// assig the tail and have a new slice
   159  	return &DeleteSlice{head: currentTail, tail: currentTail}
   160  }
   161  
   162  func (ds *DeleteSlice) apply(del *BufferedUpdates, docIDUpto int) {
   163  	if ds.head == ds.tail {
   164  		// 0 length slice
   165  		return
   166  	}
   167  	// When we apply a slice we take the head and get its next as our
   168  	// first item to apply and continue until we applied the tail. If
   169  	// the head and tail in this slice are not equal then there will be
   170  	// at least one more non-nil node in the slice!
   171  	for current := ds.head; current != ds.tail; {
   172  		current = current.next
   173  		assert2(current != nil,
   174  			"slice property violated between the head on the tail must not be a null node")
   175  		current.apply(del, docIDUpto)
   176  	}
   177  	ds.reset()
   178  }
   179  
   180  func (ds *DeleteSlice) reset() {
   181  	// reset to 0 length slice
   182  	ds.head = ds.tail
   183  }
   184  
   185  /*
   186  Returns true iff the given item is identical to the item held by the
   187  slice's tail, otherwise false.
   188  */
   189  func (ds *DeleteSlice) isTailItem(item interface{}) bool {
   190  	return ds.tail.item == item
   191  }
   192  
   193  func (ds *DeleteSlice) isEmpty() bool {
   194  	return ds.head == ds.tail
   195  }
   196  
   197  type Node struct {
   198  	next *Node // volatile
   199  	item interface{}
   200  }
   201  
   202  func newNode(item interface{}) *Node {
   203  	return &Node{item: item}
   204  }
   205  
   206  func (node *Node) apply(BufferedUpdates *BufferedUpdates, docIDUpto int) {
   207  	panic("sentinel item must never be applied")
   208  }