Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

If user's data region is a partitioned region, there will be one LuceneIndex is for the partitioned region. Every bucket in the data region will have its own RegionDirectory (implements Lucene's Directory interface), which keeps the FileSystem for index regions. Index regions contain 2 regions:
  • FileRegion : holds the meta data about indexing files
  • ChunkRegion : Holds the actual data chunks for a given index file. 

The FileRegion and ChunkRegion will be collocated with the data region which is to be indexed. The FileRegion and ChunkRegion will have partition resolver that looks at the bucket id part of the key only.
In AsyncEventListener, when An AsyncEventQueue will be used to update the LuceneIndex. AsyncEventListener will procoess the events in AEQ in batch. When a data entry is processed
  1. create document for indexed fields. Indexed field values are obtained from AsyncEvent through reflection (in case of domain object) or by PdxInstance interface (in case pdx or JSON); constructing Lucene document object and adding it to the LuceneIndex associated with that region.
  2. determine the bucket id of the entry.
  3. Get the RegionDirectory for that bucket, save the document into RegionDirectory. 

...

PersistentRegions
The Lucene Index will be persisted.
OverflowRegions
The Lucene Index will not be overflowed. The rational here is that the Lucene index will be much smaller than the data size, so it is not necessary to overflow the index.
EmptyRegions
The Lucene Index not supported
OffHeapRegions
The Lucene index will be stored in OffHeap

Index Maintenance

An AsynchEventQueue will be used to update the LuceneIndex. This will allow us to do updates in batches supported by AEQ. Indexed field values are obtained from AsynchEvent through reflection (in case of domain object) or by PdxInstance interface (in case pdx or JSON); constructing Lucene document object and adding it to the LuceneIndex associated with that region.

 

Handling failures, restarts, and rebalance 

The index and async event queue will be stored and a region with the same redundancy level as the original region. We will take care to ensure that all updates are written to the index files before removing events from the queue. So during failover the new primary should be able to read index files from disk.

 

LuceneIndex can be created and destroy. We don't support creating index on a region with data for now. 

 

Handling failures, restarts, and rebalance 

The index region and async event queue will be restored with its colocated data region's buckets.  So during failover the new primary should be able to read/write index as usual.

 

Walkthrough creating index in Walkthrough creating index in Geode region

  
1) Create a LuceneIndex object to hold the data structures that will be created in following steps. This object will be registered to cache owned LuceneService later. 
2) LuceneIndex will keep all the reflective fields. 
3 Assume the dataregion is PartitionedRegion (otherwise, no need to define PartitionResolver). Create a FileRegion (let's call it "fr") and a ChunkRegion (let's call it "cr"), collocated with Data Region (let's name it "dataregion").
FileRegion and
 Define PartitionResolver to use dataregion's bucket id as routing object, which will guarantee the index bucket region will be the same bucket id as the dataregion's bucket region's even when dataregion has its own customer-defined PartitionResolver. We don't nedd to define PartitionResolver on dataregion. 
4) FileRegion and ChunkRegion use the same region attributes as dataregion. In partitioned region case, the FileRegion and ChunkRegion will be under the same parent region, i.e. /root in this example. In replicated region case, the index regions will be root regions all the time. 
 
35) Create a GeodeDirectory object using RegionDirectory object for a bucket using the FileRegion , ChunkRegion and the path we got in previous stepand ChunkRegion's same bucket
 
46) Create PerFieldAnalyzerWrapper and save the fields in LuceneIndex. 
 
57) Create a Lucene's IndexWriterConfig object using Analyzer. 
 
68) Create a Lucene's IndexWriter object using GeodeDirectory and IndexWriterConfig object. 
 
7) Define PartitionResolver to use dataregion's bucket id as routing object, which will guarantee the index bucket region will be the same bucket id as the dataregion's bucket region's even when dataregion has its own customer-defined PartitionResolver. We don't nedd to define PartitionResolver on dataregion. 
 
8) Define AEQ with multiple dispatcher threads and order-policy=partition. That will group events by bucket id into different dispatcher queues. Each dispatcher thread will call our AEQ listener to process events for one or more buckets. Each event will be processed to be Document and write into ChunkRegion via GeodeDirectory. We don't need lock for GeodeDirectory, since only one thread will process one bucket's events. 
 
9) If dataregion is a replicated region, then define AEQ with single dispatcher thread. 
 
10) Register the newly created LuceneIndex into LuceneService. The registration step will also publish the meta data into the "lucene_meta_region" which is a persistent replicate region, then other JVM will know a new luceneIndex with these meta data was created. All the members should have a LuceneService instance with the same LuceneIndex definition.
9) Define AEQ with multiple dispatcher threads and order-policy=partition. That will group events by bucket id into different dispatcher queues. Each dispatcher thread will call our AEQ listener to process events for one or more buckets. Each event will be processed to be document and write into ChunkRegion via RegionDirectory. We don't need lock for RegionDirectory, since only one thread will process one bucket's events. 
10) If dataregion is a replicated region, then define AEQ with single dispatcher thread. 
11) Register the newly created LuceneIndex into LuceneService. The registration step will also publish the meta data into the "lucene_meta_region" which is a persistent replicate region, then other JVM will know a new luceneIndex with these meta data was created. All the members should have a LuceneService instance with the same LuceneIndex definition.

Processing Queries

PlantUML
() User -down-> [LuceneQuery] : fields, Analyzer, query strings, or Query
[LuceneQuery] -down-> [User Data Region]: call search()
[User Data Region] -down-> [Function Excetion]
[Function Excetion] -down-> [Bucket 1]
[Bucket 1] -down-> [RegionDirectory for bucket 1]
[RegionDirectory for bucket 1] ..> [Bucket 1] : TopDocs, ScoreDocs
[Bucket 1] ..> [Function Excetion] : score, key

[Function Excetion] -down-> [Bucket 2]
[Bucket 2] -down-> [RegionDirectory for bucket 2]
[RegionDirectory for bucket 2] ..> [Bucket 2] : TopDocs, ScoreDocs
[Bucket 2] ..> [Function Excetion] : score, key



Processing Queries
 

Partitioned regions

In the case of partitioned regions, the query must be sent out to all of the primaries. The results will then need to be aggregated back together. We are still investigating options for how to aggregate the data, see Text Search Aggregation Options.

Replicated regions

Query will be sent to one of the members and get the results there. Aggregation will be handled in that member before returned to the caller. TBD


Result collection and paging

The ResultSet will support pagination mechanism to retrieve the results. All the keys are aggregated at the query executor node (client or peer); and getAll is used to fetch the values according to page size.