فهرست منبع

HPCC-25605 Reduce the overhead of the index node cache

Signed-off-by: Gavin Halliday <gavin.halliday@lexisnexis.com>
Gavin Halliday 4 سال پیش
والد
کامیت
5b6fff5876

+ 1 - 1
common/fileview2/fvidxsource.cpp

@@ -48,7 +48,7 @@ static IKeyIndex *openKeyFile(IDistributedFilePart *keyFile)
                 rfn.getPath(remotePath);
                 unsigned crc = 0;
                 keyFile->getCrc(crc);
-                return createKeyIndex(remotePath.str(), crc, false, false);
+                return createKeyIndex(remotePath.str(), crc, false);
             }
         }
         catch (IException *E)

+ 1 - 1
ecl/eclagent/eclagent.cpp

@@ -3064,7 +3064,7 @@ char * EclAgent::queryIndexMetaData(char const * lfn, char const * xpath)
                 rfn.getPath(remotePath);
                 unsigned crc;
                 part->getCrc(crc);
-                key.setown(createKeyIndex(remotePath.str(), crc, false, false));
+                key.setown(createKeyIndex(remotePath.str(), crc, false));
                 break;
             }
         }

+ 1 - 1
ecl/hthor/hthorkey.cpp

@@ -54,7 +54,7 @@ static IKeyIndex *openKeyFile(IDistributedFilePart & keyFile)
                 rfn.getPath(remotePath);
                 unsigned crc = 0;
                 keyFile.getCrc(crc);
-                return createKeyIndex(remotePath.str(), crc, false, false);
+                return createKeyIndex(remotePath.str(), crc, false);
             }
         }
         catch (IException *E)

+ 1 - 3
fs/dafsserver/dafsserver.cpp

@@ -2184,7 +2184,6 @@ class CRemoteIndexBaseActivity : public CRemoteDiskBaseActivity
 
 protected:
     bool isTlk = false;
-    bool allowPreload = false;
     unsigned fileCrc = 0;
     Owned<IKeyIndex> keyIndex;
     Owned<IKeyManager> keyManager;
@@ -2201,7 +2200,7 @@ protected:
         crc32.tally(sizeof(time_t), &modTimeTT);
         unsigned crc = crc32.get();
 
-        keyIndex.setown(createKeyIndex(fileName, crc, isTlk, allowPreload));
+        keyIndex.setown(createKeyIndex(fileName, crc, isTlk));
         keyManager.setown(createLocalKeyManager(*record, keyIndex, nullptr, true, false));
         filters.createSegmentMonitors(keyManager);
         keyManager->finishSegmentMonitors();
@@ -2222,7 +2221,6 @@ public:
         setupInputMeta(config, getTypeInfoOutputMetaData(config, "input", false));
 
         isTlk = config.getPropBool("isTlk");
-        allowPreload = config.getPropBool("allowPreload");
         fileCrc = config.getPropInt("crc");
     }
     virtual void flushStatistics(CClientStats &stats) override

+ 0 - 1
roxie/ccd/ccd.hpp

@@ -377,7 +377,6 @@ extern unsigned roxiePort;     // If listening on multiple, this is the first. U
 extern unsigned udpMulticastBufferSize;
 extern size32_t diskReadBufferSize;
 
-extern bool nodeCachePreload;
 extern unsigned nodeCacheMB;
 extern unsigned leafCacheMB;
 extern unsigned blobCacheMB;

+ 69 - 40
roxie/ccd/ccdfile.cpp

@@ -604,23 +604,34 @@ typedef StringArray *StringArrayPtr;
 
 struct CacheInfoEntry
 {
+    //For convenience the values for PageType match the NodeX enumeration (see noteWarm).
+    //Ensure disk entries sort last so that index nodes take precedence when deduping offsets.
+    enum PageType : unsigned
+    {
+        PageTypeBranch = 0,
+        PageTypeLeaf = 1,
+        PageTypeBlob = 2,
+        PageTypeDisk = 3,
+    };
+
     union
     {
         struct
         {
-            bool diskCache : 1;   // false means it's in the jhtree cache, true means it's only in OS disk cache
-            __uint64 page: 39;
-            unsigned file: 24;
+            unsigned type: 2;    // disk or the kind of index node
+            __uint64 page: 38;   // Support file sizes up to 2^51 i.e. 2PB
+            unsigned file: 24;   // Up to 4 million files
         } b;
         __uint64 u;
     };
+    static_assert(sizeof(b) == sizeof(u), "Unexpected packing issue");
 
     inline CacheInfoEntry() { u = 0; }
-    inline CacheInfoEntry(unsigned _file, offset_t _pos, bool _diskCache)
+    inline CacheInfoEntry(unsigned _file, offset_t _pos, PageType pageType)
     {
         b.file = _file;
         b.page = _pos >> pageBits;
-        b.diskCache = _diskCache;
+        b.type = pageType;
     }
     inline bool operator < ( const CacheInfoEntry &l) const { return u < l.u; }
     inline bool operator <= ( const CacheInfoEntry &l) const { return u <= l.u; }
@@ -644,7 +655,9 @@ public:
         recentReadSize = trackSize >> CacheInfoEntry::pageBits;
         if (traceLevel)
             DBGLOG("Creating CacheReportingBuffer with %d elements", recentReadSize);
-        assertex(recentReadSize);
+        if (!recentReadSize)
+            throw makeStringExceptionV(ROXIE_FILE_ERROR, "cacheTrackSize(%u) is the size in bytes it cannot be < %u", (unsigned)trackSize, 1U << CacheInfoEntry::pageBits);
+
         recentReads = new CacheInfoEntry[recentReadSize];
         recentReadHead = 0;
     }
@@ -668,12 +681,12 @@ public:
         recentReadHead = 0;
     }
 
-    void noteRead(unsigned fileIdx, offset_t pos, unsigned len, bool diskCache)
+    void noteRead(unsigned fileIdx, offset_t pos, unsigned len, CacheInfoEntry::PageType pageType)
     {
         if (recentReads && len)
         {
-            CacheInfoEntry start(fileIdx, pos, diskCache);
-            CacheInfoEntry end(fileIdx, pos+len-1, diskCache);
+            CacheInfoEntry start(fileIdx, pos, pageType);
+            CacheInfoEntry end(fileIdx, pos+len-1, pageType);
             for(;start <= end; ++start)
             {
                 recentReads[recentReadHead++ % recentReadSize] = start;
@@ -690,7 +703,7 @@ public:
         else
             sortSize = recentReadHead;
         std::sort(recentReads, recentReads + sortSize);
-        CacheInfoEntry lastPos(-1,-1,false);
+        CacheInfoEntry lastPos(-1,-1,CacheInfoEntry::PageTypeDisk);
         unsigned dest = 0;
         for (unsigned idx = 0; idx < sortSize; idx++)
         {
@@ -711,7 +724,7 @@ public:
         unsigned lastFileIdx = (unsigned) -1;
         offset_t lastPage = (offset_t) -1;
         offset_t startRange = 0;
-        bool lastDiskCache = false;
+        CacheInfoEntry::PageType lastPageType = CacheInfoEntry::PageTypeDisk;
         bool includeFile = false;
         for (unsigned idx = 0; idx < recentReadHead; idx++)
         {
@@ -719,7 +732,7 @@ public:
             if (pos.b.file != lastFileIdx)
             {
                 if (includeFile)
-                    appendRange(ret, startRange, lastPage, lastDiskCache).newline();
+                    appendRange(ret, startRange, lastPage, lastPageType).newline();
                 lastFileIdx = pos.b.file;
                 if (channel==(unsigned) -1 || cacheIndexChannels.item(lastFileIdx)==channel)
                 {
@@ -730,34 +743,37 @@ public:
                     includeFile = false;
                 startRange = pos.b.page;
             }
-            else if ((pos.b.page == lastPage || pos.b.page == lastPage+1) && pos.b.diskCache == lastDiskCache)
+            else if ((pos.b.page == lastPage || pos.b.page == lastPage+1) && pos.b.type == lastPageType)
             {
                 // Still in current range
             }
             else
             {
                 if (includeFile)
-                    appendRange(ret, startRange, lastPage, lastDiskCache);
+                    appendRange(ret, startRange, lastPage, lastPageType);
                 startRange = pos.b.page;
             }
             lastPage = pos.b.page;
-            lastDiskCache = pos.b.diskCache;
+            lastPageType = (CacheInfoEntry::PageType)pos.b.type;
         }
         if (includeFile)
-            appendRange(ret, startRange, lastPage, lastDiskCache).newline();
+            appendRange(ret, startRange, lastPage, lastPageType).newline();
     }
 
-    virtual void noteWarm(unsigned fileIdx, offset_t pos, unsigned len) override
+    virtual void noteWarm(unsigned fileIdx, offset_t pos, unsigned len, NodeType type) override
     {
-        noteRead(fileIdx, pos, len, false);
+        //For convenience the values for PageType match the NodeX enumeration.
+        CacheInfoEntry::PageType pageType = (type <= NodeBlob) ? (CacheInfoEntry::PageType)type : CacheInfoEntry::PageTypeDisk;
+        noteRead(fileIdx, pos, len, pageType);
     }
 
 private:
-    static StringBuffer &appendRange(StringBuffer &ret, offset_t start, offset_t end, bool diskCache)
+    static StringBuffer &appendRange(StringBuffer &ret, offset_t start, offset_t end, CacheInfoEntry::PageType pageType)
     {
         ret.append(' ');
-        if (!diskCache)
-            ret.append('*');
+        if (pageType != CacheInfoEntry::PageTypeDisk)
+            ret.append('*').append("RLB"[pageType]);
+
         if (start==end)
             ret.appendf("%" I64F "x", start);
         else
@@ -772,9 +788,7 @@ class CRoxieFileCache : implements IRoxieFileCache, implements ICopyFileProgress
     mutable ICopyArrayOf<ILazyFileIO> todo; // Might prefer a queue but probably doesn't really matter.
     InterruptableSemaphore toCopy;
     InterruptableSemaphore toClose;
-#ifdef _CONTAINERIZED
     InterruptableSemaphore cidtSleep;
-#endif
     mutable CopyMapStringToMyClass<ILazyFileIO> files;
     mutable CriticalSection crit;
     CriticalSection cpcrit;
@@ -783,10 +797,8 @@ class CRoxieFileCache : implements IRoxieFileCache, implements ICopyFileProgress
     std::atomic<bool> closing;
     bool closePending[2];
     StringAttrMapping fileErrorList;
-#ifdef _CONTAINERIZED
     bool cidtActive = false;
     Semaphore cidtStarted;
-#endif
     Semaphore bctStarted;
     Semaphore hctStarted;
 
@@ -824,7 +836,6 @@ class CRoxieFileCache : implements IRoxieFileCache, implements ICopyFileProgress
             return FileNotFound;
     }
 
-#ifdef _CONTAINERIZED
     int runCacheInfoDump()
     {
         cidtStarted.signal();
@@ -887,7 +898,6 @@ class CRoxieFileCache : implements IRoxieFileCache, implements ICopyFileProgress
             DBGLOG("Cache info dump thread %p exiting", this);
         return 0;
     }
-#endif
 
     unsigned trackCache(const char *filename, unsigned channel)
     {
@@ -902,7 +912,7 @@ class CRoxieFileCache : implements IRoxieFileCache, implements ICopyFileProgress
     virtual void noteRead(unsigned fileIdx, offset_t pos, unsigned len) override
     {
         if (activeCacheReportingBuffer)
-            activeCacheReportingBuffer->noteRead(fileIdx, pos, len, true);
+            activeCacheReportingBuffer->noteRead(fileIdx, pos, len, CacheInfoEntry::PageTypeDisk);
     }
 
     ILazyFileIO *openFile(const char *lfn, unsigned partNo, unsigned channel, const char *localLocation,
@@ -1188,9 +1198,7 @@ public:
     IMPLEMENT_IINTERFACE;
 
     CRoxieFileCache() :
-#ifdef _CONTAINERIZED
                         cidt(*this),
-#endif
                         bct(*this), hct(*this)
     {
         aborting = false;
@@ -1254,17 +1262,18 @@ public:
 
     virtual void startCacheReporter() override
     {
-#ifdef _CONTAINERIZED
+#ifndef _CONTAINERIZED
+        if (!getenv("HPCC_DLLSERVER_PATH"))
+            return;
+#endif
         if (activeCacheReportingBuffer && cacheReportPeriodSeconds)
         {
             cidt.start();
             cidtStarted.wait();
             cidtActive = true;
         }
-#endif
     }
 
-#ifdef _CONTAINERIZED
     class CacheInfoDumpThread : public Thread
     {
         CRoxieFileCache &owner;
@@ -1276,7 +1285,6 @@ public:
             return owner.runCacheInfoDump();
         }
     } cidt;
-#endif
 
     class BackgroundCopyThread : public Thread
     {
@@ -1670,7 +1678,13 @@ public:
     void doLoadSavedOsCacheInfo(unsigned channel)
     {
         const char* dllserver_root = getenv("HPCC_DLLSERVER_PATH");
+#ifdef _CONTAINERIZED
         assertex(dllserver_root != nullptr);
+#else
+        //Default behaviour is to not load or saving anything on bare metal
+        if (!dllserver_root)
+            return;
+#endif
         VStringBuffer cacheFileName("%s/%s/cacheInfo.%d", dllserver_root, roxieName.str(), channel);
         StringBuffer cacheInfo;
         try
@@ -1753,6 +1767,8 @@ public:
                 DBGLOG("Failed to open file %s to pre-warm cache (error %d)", fileName.str(), errno);
             }
 #endif
+            // "fileName" is the filename that roxie would use if it copied the file locally.  This may not
+            // match the name of the actual file - e.g. if the file is local but in a different location.
             Owned<ILazyFileIO> localFile = lookupLocalFile(fileName);
             if (localFile)
             {
@@ -1764,8 +1780,20 @@ public:
             for (;;)
             {
                 bool inNodeCache = (*cacheInfo=='*');
+                NodeType nodeType = NodeNone;
                 if (inNodeCache)
+                {
                     cacheInfo++;
+                    switch (*cacheInfo)
+                    {
+                    case 'R': nodeType = NodeBranch; break;
+                    case 'L': nodeType = NodeLeaf; break;
+                    case 'B': nodeType = NodeBlob; break;
+                    default:
+                        throwUnexpectedX("Unknown node type");
+                    }
+                    cacheInfo++;
+                }
                 __uint64 startPage = readPage(cacheInfo);
                 __uint64 endPage;
                 if (*cacheInfo=='-')
@@ -1781,7 +1809,8 @@ public:
                 offset_t endOffset = (endPage+1) << CacheInfoEntry::pageBits;
                 if (inNodeCache && !keyFailed && localFile && !keyIndex)
                 {
-                    keyIndex.setown(createKeyIndex(fileName, localFile->getCrc(), *localFile.get(), fileIdx, false, false));  // MORE - we don't know if it's a TLK, but hopefully it doesn't matter
+                    //Pass false for isTLK - it will be initialised from the index header
+                    keyIndex.setown(createKeyIndex(fileName, localFile->getCrc(), *localFile.get(), fileIdx, false));
                     if (!keyIndex)
                         keyFailed = true;
                 }
@@ -1792,7 +1821,7 @@ public:
                     startOffset = ((startOffset+nodeSize-1)/nodeSize)*nodeSize;
                     do
                     {
-                        bool loaded = keyIndex->prewarmPage(startOffset);
+                        bool loaded = keyIndex->prewarmPage(startOffset, nodeType);
                         if (!loaded)
                             break;
                         preloaded++;
@@ -2918,10 +2947,10 @@ public:
                             if (lazyOpen)
                             {
                                 // We pass the IDelayedFile interface to createKeyIndex, so that it does not open the file immediately
-                                keyset->addIndex(createKeyIndex(part->queryFilename(), crc, *QUERYINTERFACE(part.get(), IDelayedFile), part->getFileIdx(), false, false));
+                                keyset->addIndex(createKeyIndex(part->queryFilename(), crc, *QUERYINTERFACE(part.get(), IDelayedFile), part->getFileIdx(), false));
                             }
                             else
-                                keyset->addIndex(createKeyIndex(part->queryFilename(), crc, *part.get(), part->getFileIdx(), false, false));
+                                keyset->addIndex(createKeyIndex(part->queryFilename(), crc, *part.get(), part->getFileIdx(), false));
                         }
                         else
                             keyset->addIndex(NULL);
@@ -2955,10 +2984,10 @@ public:
                     if (lazyOpen)
                     {
                         // We pass the IDelayedFile interface to createKeyIndex, so that it does not open the file immediately
-                        key.setown(createKeyIndex(pname.str(), crc, *QUERYINTERFACE(keyFile.get(), IDelayedFile), keyFile->getFileIdx(), numParts>1, false));
+                        key.setown(createKeyIndex(pname.str(), crc, *QUERYINTERFACE(keyFile.get(), IDelayedFile), keyFile->getFileIdx(), numParts>1));
                     }
                     else
-                        key.setown(createKeyIndex(pname.str(), crc, *keyFile.get(), keyFile->getFileIdx(), numParts>1, false));
+                        key.setown(createKeyIndex(pname.str(), crc, *keyFile.get(), keyFile->getFileIdx(), numParts>1));
                     keyset->addIndex(LINK(key->queryPart(0)));
                 }
                 else

+ 2 - 4
roxie/ccd/ccdmain.cpp

@@ -204,7 +204,6 @@ HardwareInfo hdwInfo;
 unsigned parallelAggregate;
 bool inMemoryKeysEnabled = true;
 
-bool nodeCachePreload = false;
 unsigned nodeCacheMB = 100;
 unsigned leafCacheMB = 50;
 unsigned blobCacheMB = 0;
@@ -1109,8 +1108,6 @@ int CCD_API roxie_main(int argc, const char *argv[], const char * defaultYaml)
         inMemoryKeysEnabled = topology->getPropBool("@inMemoryKeysEnabled", true);
 
         setKeyIndexCacheSize((unsigned)-1); // unbound
-        nodeCachePreload = topology->getPropBool("@nodeCachePreload", false);
-        setNodeCachePreload(nodeCachePreload);
         nodeCacheMB = topology->getPropInt("@nodeCacheMem", 100); 
         setNodeCacheMem(nodeCacheMB * 0x100000);
         leafCacheMB = topology->getPropInt("@leafCacheMem", 50);
@@ -1399,9 +1396,10 @@ int CCD_API roxie_main(int argc, const char *argv[], const char * defaultYaml)
                     time(&startupTime);
                     roxieServer->start();
                 }
-#ifdef _CONTAINERIZED
+
                 queryFileCache().loadSavedOsCacheInfo();
                 queryFileCache().startCacheReporter();
+#ifdef _CONTAINERIZED
                 publishTopology(traceLevel);
 #endif
                 writeSentinelFile(sentinelFile);

+ 1 - 7
roxie/ccd/ccdstate.cpp

@@ -2450,13 +2450,7 @@ private:
             break;
 
         case 'N':
-            if (stricmp(queryName, "control:nodeCachePreload")==0)
-            {
-                nodeCachePreload = control->getPropBool("@val", true);
-                topology->setPropBool("@nodeCachePreload", nodeCachePreload);
-                setNodeCachePreload(nodeCachePreload);
-            }
-            else if (stricmp(queryName, "control:nodeCacheMem")==0)
+            if (stricmp(queryName, "control:nodeCacheMem")==0)
             {
                 nodeCacheMB = control->getPropInt("@val", 100);
                 topology->setPropInt("@nodeCacheMem", nodeCacheMB);

+ 7 - 7
system/jhtree/ctfile.cpp

@@ -281,7 +281,7 @@ void CWriteNodeBase::write(IFileIOStream *out, CRC32 *crc)
 
 CWriteNode::CWriteNode(offset_t _fpos, CKeyHdr *_keyHdr, bool isLeaf) : CWriteNodeBase(_fpos, _keyHdr)
 {
-    hdr.leafFlag = isLeaf ? 1 : 0;
+    hdr.leafFlag = isLeaf ? NodeLeaf : NodeBranch;
     if (!isLeaf)
     {
         keyLen = keyHdr->getNodeKeyLength();
@@ -393,7 +393,7 @@ size32_t CWriteNode::compressValue(const char *keyData, size32_t size, char *res
 
 CBlobWriteNode::CBlobWriteNode(offset_t _fpos, CKeyHdr *_keyHdr) : CWriteNodeBase(_fpos, _keyHdr)
 {
-    hdr.leafFlag = 2;
+    hdr.leafFlag = NodeBlob;
     lzwcomp.openBlob(keyPtr, maxBytes);
 }
 
@@ -427,7 +427,7 @@ unsigned __int64 CBlobWriteNode::add(const char * &data, size32_t &size)
 
 CMetadataWriteNode::CMetadataWriteNode(offset_t _fpos, CKeyHdr *_keyHdr) : CWriteNodeBase(_fpos, _keyHdr)
 {
-    hdr.leafFlag = 3;
+    hdr.leafFlag = NodeMeta;
 }
 
 size32_t CMetadataWriteNode::set(const char * &data, size32_t &size)
@@ -444,7 +444,7 @@ size32_t CMetadataWriteNode::set(const char * &data, size32_t &size)
 
 CBloomFilterWriteNode::CBloomFilterWriteNode(offset_t _fpos, CKeyHdr *_keyHdr) : CWriteNodeBase(_fpos, _keyHdr)
 {
-    hdr.leafFlag = 4;
+    hdr.leafFlag = NodeBloom;
 }
 
 size32_t CBloomFilterWriteNode::set(const byte * &data, size32_t &size)
@@ -565,7 +565,7 @@ void CJHTreeNode::unpack(const void *node, bool needCopy)
         PrintStackReport();
         throw MakeStringException(0, "Htree: Corrupt key node detected");
     }
-    if (!hdr.leafFlag)
+    if (hdr.leafFlag == NodeBranch)
         keyLen = keyHdr->getNodeKeyLength();
     keyRecLen = keyLen + sizeof(offset_t);
     char *keys = ((char *) node) + sizeof(hdr);
@@ -575,7 +575,7 @@ void CJHTreeNode::unpack(const void *node, bool needCopy)
         if (hdr.crc32 != crc)
             throw MakeStringException(0, "CRC error on key node");
     }
-    if (hdr.leafFlag==1)
+    if (hdr.leafFlag==NodeLeaf)
     {
         firstSequence = *(unsigned __int64 *) keys;
         keys += sizeof(unsigned __int64);
@@ -944,7 +944,7 @@ offset_t CJHVarTreeNode::getFPosAt(unsigned int num) const
 void CJHRowCompressedNode::load(CKeyHdr *_keyHdr, const void *rawData, offset_t _fpos, bool needCopy)
 {
     CJHTreeNode::load(_keyHdr, rawData, _fpos, needCopy);
-    assertex(hdr.leafFlag==1);
+    assertex(hdr.leafFlag==NodeLeaf);
     char *keys = ((char *) rawData) + sizeof(hdr)+sizeof(firstSequence);
     assertex(IRandRowExpander::isRand(keys));
     rowexp.setown(createRandRDiffExpander());

+ 16 - 4
system/jhtree/ctfile.hpp

@@ -99,6 +99,17 @@ struct __declspec(novtable) jhtree_decl KeyHdr
     __uint64 partitionFieldMask; /* Bitmap indicating partition keyed fields */
 };
 
+enum NodeType : char
+{
+    NodeBranch = 0,
+    NodeLeaf = 1,
+    NodeBlob = 2,
+    NodeMeta = 3,
+    NodeBloom = 4,
+//The following is never stored and only used in code as a value that does not match any of the above.
+    NodeNone = 127,
+};
+
 //#pragma pack(1)
 #pragma pack(push,1)
 struct jhtree_decl NodeHdr
@@ -196,10 +207,11 @@ public:
     inline offset_t getFpos() const { assertex(fpos); return fpos; }
     inline size32_t getKeyLen() const { return keyLen; }
     inline size32_t getNumKeys() const { return hdr.numKeys; }
-    inline bool isBlob() const { return hdr.leafFlag == 2; }
-    inline bool isMetadata() const { return hdr.leafFlag == 3; }
-    inline bool isBloom() const { return hdr.leafFlag == 4; }
-    inline bool isLeaf() const { return hdr.leafFlag != 0; }
+    inline bool isBlob() const { return hdr.leafFlag == NodeBlob; }
+    inline bool isMetadata() const { return hdr.leafFlag == NodeMeta; }
+    inline bool isBloom() const { return hdr.leafFlag == NodeBloom; }
+    inline bool isLeaf() const { return hdr.leafFlag != NodeBranch; }       // actually is-non-branch.  Use should be reviewed.
+    inline NodeType getNodeType() const { return (NodeType)hdr.leafFlag; }
 
 public:
     CNodeBase();

+ 189 - 211
system/jhtree/jhtree.cpp

@@ -644,7 +644,8 @@ public:
         {
             CNodeMapping &mapping = iter->query();
             const CKeyIdAndPos &key = mapping.queryFindValue();
-            cacheInfo.noteWarm(key.keyId, key.pos, mapping.queryElement().getNodeSize());
+            const CJHTreeNode &node = mapping.queryElement();
+            cacheInfo.noteWarm(key.keyId, key.pos, node.getNodeSize(), node.getNodeType());
         }
     }
 };
@@ -656,38 +657,20 @@ private:
     CNodeMRUCache nodeCache;
     CNodeMRUCache leafCache;
     CNodeMRUCache blobCache;
-    CNodeMRUCache preloadCache;
     bool cacheNodes; 
     bool cacheLeaves;
     bool cacheBlobs;
-    bool preloadNodes;
 public:
     CNodeCache(size32_t maxNodeMem, size32_t maxLeaveMem, size32_t maxBlobMem)
-        : nodeCache(maxNodeMem), leafCache(maxLeaveMem), blobCache(maxBlobMem), preloadCache((unsigned) -1)
+        : nodeCache(maxNodeMem), leafCache(maxLeaveMem), blobCache(maxBlobMem)
     {
         cacheNodes = maxNodeMem != 0;
         cacheLeaves = maxLeaveMem != 0;;
         cacheBlobs = maxBlobMem != 0;
-        preloadNodes = false;
         // note that each index caches the last blob it unpacked so that sequential blobfetches are still ok
     }
-    CJHTreeNode *getNode(INodeLoader *key, unsigned keyID, offset_t pos, IContextLogger *ctx, bool isTLK);
+    CJHTreeNode *getNode(INodeLoader *key, unsigned keyID, offset_t pos, NodeType type, IContextLogger *ctx, bool isTLK);
     void getCacheInfo(ICacheInfoRecorder &cacheInfo);
-    void preload(CJHTreeNode *node, unsigned keyID, offset_t pos, IContextLogger *ctx);
-
-    bool isPreloaded(unsigned keyID, offset_t pos);
-
-    inline bool getNodeCachePreload() 
-    {
-        return preloadNodes;
-    }
-
-    inline bool setNodeCachePreload(bool _preload)
-    {
-        bool oldPreloadNodes = preloadNodes;
-        preloadNodes = _preload;
-        return oldPreloadNodes;
-    }
 
     inline size32_t setNodeCacheMem(size32_t newSize)
     {
@@ -772,17 +755,17 @@ unsigned CKeyStore::setKeyCacheLimit(unsigned limit)
     return keyIndexCache.setCacheLimit(limit);
 }
 
-IKeyIndex *CKeyStore::doload(const char *fileName, unsigned crc, IReplicatedFile *part, IFileIO *iFileIO, unsigned fileIdx, IMemoryMappedFile *iMappedFile, bool isTLK, bool allowPreload)
+IKeyIndex *CKeyStore::doload(const char *fileName, unsigned crc, IReplicatedFile *part, IFileIO *iFileIO, unsigned fileIdx, IMemoryMappedFile *iMappedFile, bool isTLK)
 {
     // isTLK provided by caller since flags in key header unreliable. If either say it's a TLK, I believe it.
     {
         MTIME_SECTION(queryActiveTimer(), "CKeyStore_load");
         IKeyIndex *keyIndex;
+        StringBuffer fname;
+        fname.append(fileName).append('/').append(crc);
 
         // MORE - holds onto the mutex way too long
         synchronized block(mutex);
-        StringBuffer fname;
-        fname.append(fileName).append('/').append(crc);
         keyIndex = keyIndexCache.query(fname);
         if (NULL == keyIndex)
         {
@@ -794,7 +777,7 @@ IKeyIndex *CKeyStore::doload(const char *fileName, unsigned crc, IReplicatedFile
             else if (iFileIO)
             {
                 assert(!part);
-                keyIndex = new CDiskKeyIndex(getUniqId(fileIdx), LINK(iFileIO), fname, isTLK, allowPreload);
+                keyIndex = new CDiskKeyIndex(getUniqId(fileIdx), LINK(iFileIO), fname, isTLK);
             }
             else
             {
@@ -810,7 +793,7 @@ IKeyIndex *CKeyStore::doload(const char *fileName, unsigned crc, IReplicatedFile
                     iFile.setown(createIFile(fileName));
                 IFileIO *fio = iFile->open(IFOread);
                 if (fio)
-                    keyIndex = new CDiskKeyIndex(getUniqId(fileIdx), fio, fname, isTLK, allowPreload);
+                    keyIndex = new CDiskKeyIndex(getUniqId(fileIdx), fio, fname, isTLK);
                 else
                     throw MakeStringException(0, "Failed to open index file %s", fileName);
             }
@@ -825,19 +808,19 @@ IKeyIndex *CKeyStore::doload(const char *fileName, unsigned crc, IReplicatedFile
     }
 }
 
-IKeyIndex *CKeyStore::load(const char *fileName, unsigned crc, IFileIO *iFileIO, unsigned fileIdx, bool isTLK, bool allowPreload)
+IKeyIndex *CKeyStore::load(const char *fileName, unsigned crc, IFileIO *iFileIO, unsigned fileIdx, bool isTLK)
 {
-    return doload(fileName, crc, NULL, iFileIO, fileIdx, NULL, isTLK, allowPreload);
+    return doload(fileName, crc, NULL, iFileIO, fileIdx, NULL, isTLK);
 }
 
-IKeyIndex *CKeyStore::load(const char *fileName, unsigned crc, IMemoryMappedFile *iMappedFile, bool isTLK, bool allowPreload)
+IKeyIndex *CKeyStore::load(const char *fileName, unsigned crc, IMemoryMappedFile *iMappedFile, bool isTLK)
 {
-    return doload(fileName, crc, NULL, NULL, (unsigned) -1, iMappedFile, isTLK, allowPreload);
+    return doload(fileName, crc, NULL, NULL, (unsigned) -1, iMappedFile, isTLK);
 }
 
-IKeyIndex *CKeyStore::load(const char *fileName, unsigned crc, bool isTLK, bool allowPreload)
+IKeyIndex *CKeyStore::load(const char *fileName, unsigned crc, bool isTLK)
 {
-    return doload(fileName, crc, NULL, NULL, (unsigned) -1, NULL, isTLK, allowPreload);
+    return doload(fileName, crc, NULL, NULL, (unsigned) -1, NULL, isTLK);
 }
 
 StringBuffer &CKeyStore::getMetrics(StringBuffer &xml)
@@ -964,45 +947,24 @@ CKeyIndex::CKeyIndex(unsigned _iD, const char *_name) : name(_name)
     latestGetNodeOffset = 0;
 }
 
-void CKeyIndex::cacheNodes(CNodeCache *cache, offset_t nodePos, bool isTLK)
-{
-    bool first = true;
-    while (nodePos)
-    {
-        Owned<CJHTreeNode> node = loadNode(nodePos);
-        if (node->isLeaf())
-        {
-            if (!isTLK)
-                return;
-        }
-        else if (first)
-        {
-            cacheNodes(cache, node->getFPosAt(0), isTLK);
-            first = false;
-        }
-        cache->preload(node, iD, nodePos, NULL);
-        nodePos = node->getRightSib();
-    }
-}
-
-void CKeyIndex::init(KeyHdr &hdr, bool isTLK, bool allowPreload)
+void CKeyIndex::init(KeyHdr &hdr, bool isTLK)
 {
     if (isTLK)
         hdr.ktype |= HTREE_TOPLEVEL_KEY; // Once upon a time, thor did not set
+    else if (hdr.ktype & HTREE_TOPLEVEL_KEY)
+        isTLK = true;
+
     keyHdr = new CKeyHdr();
     try
     {
         keyHdr->load(hdr);
         offset_t rootPos = keyHdr->getRootFPos();
         Linked<CNodeCache> nodeCache = queryNodeCache();
-        if (allowPreload)
-        {
-            if (nodeCache->getNodeCachePreload() && !nodeCache->isPreloaded(iD, rootPos))
-            {
-                cacheNodes(nodeCache, rootPos, isTLK);
-            }
-        }
-        rootNode = nodeCache->getNode(this, iD, rootPos, NULL, isTLK);
+
+        //The root node is currently a branch - but it may change - so check the branch depth for this index
+        NodeType type = getBranchDepth() != 0 ? NodeBranch : NodeLeaf;
+        rootNode = nodeCache->getNode(this, iD, rootPos, type, NULL, isTLK);
+
         // It's not uncommon for a TLK to have a "root node" that has a single entry in it pointing to a leaf node
         // with all the info in. In such cases we can avoid a lot of cache lookups by pointing the "root" in the
         // CKeyIndex directly to the (single) leaf.
@@ -1013,7 +975,7 @@ void CKeyIndex::init(KeyHdr &hdr, bool isTLK, bool allowPreload)
         {
             Owned<CJHTreeNode> oldRoot = rootNode;
             rootPos = rootNode->getFPosAt(0);
-            rootNode = nodeCache->getNode(this, iD, rootPos, NULL, isTLK);
+            rootNode = nodeCache->getNode(this, iD, rootPos, NodeLeaf, NULL, isTLK);
         }
         loadBloomFilters();
     }
@@ -1049,7 +1011,7 @@ CMemKeyIndex::CMemKeyIndex(unsigned _iD, IMemoryMappedFile *_io, const char *_na
         _WINREV(hdr.nodeSize);
         memcpy(&hdr, (io->base()+io->length()) - hdr.nodeSize, sizeof(hdr));
     }
-    init(hdr, isTLK, false);
+    init(hdr, isTLK);
 }
 
 CJHTreeNode *CMemKeyIndex::loadNode(offset_t pos)
@@ -1068,7 +1030,7 @@ CJHTreeNode *CMemKeyIndex::loadNode(offset_t pos)
     return CKeyIndex::loadNode(nodeData, pos, false);
 }
 
-CDiskKeyIndex::CDiskKeyIndex(unsigned _iD, IFileIO *_io, const char *_name, bool isTLK, bool allowPreload)
+CDiskKeyIndex::CDiskKeyIndex(unsigned _iD, IFileIO *_io, const char *_name, bool isTLK)
     : CKeyIndex(_iD, _name)
 {
     io.setown(_io);
@@ -1081,7 +1043,7 @@ CDiskKeyIndex::CDiskKeyIndex(unsigned _iD, IFileIO *_io, const char *_name, bool
         if (!io->read(io->size() - hdr.nodeSize, sizeof(hdr), &hdr))
             throw MakeStringException(4, "Invalid key %s: failed to read trailing key header", _name);
     }
-    init(hdr, isTLK, allowPreload);
+    init(hdr, isTLK);
 }
 
 CJHTreeNode *CDiskKeyIndex::loadNode(offset_t pos) 
@@ -1110,10 +1072,10 @@ CJHTreeNode *CKeyIndex::loadNode(char *nodeData, offset_t pos, bool needsCopy)
         char leafFlag = ((NodeHdr *) nodeData)->leafFlag;
         switch(leafFlag)
         {
-        case 0:
+        case NodeBranch:
             ret.setown(new CJHTreeNode());
             break;
-        case 1:
+        case NodeLeaf:
             if (keyHdr->isVariable())
                 ret.setown(new CJHVarTreeNode());
             else if (keyHdr->isRowCompressed())
@@ -1121,13 +1083,13 @@ CJHTreeNode *CKeyIndex::loadNode(char *nodeData, offset_t pos, bool needsCopy)
             else
                 ret.setown(new CJHTreeNode());
             break;
-        case 2:
+        case NodeBlob:
             ret.setown(new CJHTreeBlobNode());
             break;
-        case 3:
+        case NodeMeta:
             ret.setown(new CJHTreeMetadataNode());
             break;
-        case 4:
+        case NodeBloom:
             ret.setown(new CJHTreeBloomTableNode());
             break;
         default:
@@ -1178,10 +1140,11 @@ IKeyCursor *CKeyIndex::getCursor(const IIndexFilterList *filter, bool logExcessi
     return new CKeyCursor(*this, filter, logExcessiveSeeks);
 }
 
-CJHTreeNode *CKeyIndex::getNode(offset_t offset, IContextLogger *ctx) 
+CJHTreeNode *CKeyIndex::getNode(offset_t offset, NodeType type, IContextLogger *ctx)
 { 
     latestGetNodeOffset = offset;
-    CJHTreeNode *node = cache->getNode(this, iD, offset, ctx, isTopLevelKey());
+    CJHTreeNode *node = cache->getNode(this, iD, offset, type, ctx, isTopLevelKey());
+    assertex(!node || type == node->getNodeType());
     return node;
 }
 
@@ -1357,11 +1320,11 @@ IPropertyTree * CKeyIndex::getMetadata()
     return ret;
 }
 
-bool CKeyIndex::prewarmPage(offset_t offset)
+bool CKeyIndex::prewarmPage(offset_t offset, NodeType type)
 {
     try
     {
-        Owned<CJHTreeNode> page = loadNode(offset);
+        Owned<CJHTreeNode> page = getNode(offset, type, nullptr);
         return page != nullptr;
     }
     catch(IException *E)
@@ -1375,33 +1338,52 @@ CJHTreeNode *CKeyIndex::locateFirstNode(KeyStatsCollector &stats)
 {
     keySeeks++;
     stats.seeks++;
-    CJHTreeNode * n = 0;
-    CJHTreeNode * p = LINK(rootNode);
-    while (p != 0)
+
+    CJHTreeNode * cur = LINK(rootNode);
+    unsigned depth = 0;
+    while (!cur->isLeaf())
     {
-        n = p;
-        p = getNode(n->prevNodeFpos(), stats.ctx);
-        if (p != 0)
-            n->Release();
+        CJHTreeNode * prev = cur;
+        depth++;
+        NodeType type = (depth < getBranchDepth()) ? NodeBranch : NodeLeaf;
+        cur = getNode(cur->getFPosAt(0), type, stats.ctx);
+        //Unusual - an index with no elements
+        if (!cur)
+            return prev;
+        prev->Release();
     }
-    return n;
+    return cur;
 }
 
 CJHTreeNode *CKeyIndex::locateLastNode(KeyStatsCollector &stats)
 {
     keySeeks++;
     stats.seeks++;
-    CJHTreeNode * n = 0;
-    CJHTreeNode * p = LINK(rootNode);
 
-    while (p != 0)
+    CJHTreeNode * cur = LINK(rootNode);
+    unsigned depth = 0;
+    //First find the last leaf node pointed to by the higher level index
+    while (!cur->isLeaf())
+    {
+        CJHTreeNode * prev = cur;
+        depth++;
+        NodeType type = (depth < getBranchDepth()) ? NodeBranch : NodeLeaf;
+        cur = getNode(cur->nextNodeFpos(), type, stats.ctx);
+        //Unusual - an index with no elements
+        if (!cur)
+            return prev;
+        prev->Release();
+    }
+
+    //Now walk the lead node siblings until there are no more.
+    for (;;)
     {
-        n = p;
-        p = getNode(n->nextNodeFpos(), stats.ctx);
-        if (p != 0)
-            n->Release();
+        CJHTreeNode * last = cur;
+        cur = getNode(cur->nextNodeFpos(), NodeLeaf, stats.ctx);
+        if (!cur)
+            return last;
+        ::Release(last);
     }
-    return n;
 }
 
 
@@ -1486,10 +1468,11 @@ bool CKeyCursor::next(char *dst, KeyStatsCollector &stats)
         if (!node->getValueAt( ++nodeKey, dst))
         {
             offset_t rsib = node->getRightSib();
+            NodeType type = node->getNodeType();
             node.clear();
             if (rsib != 0)
             {
-                node.setown(key.getNode(rsib, stats.ctx));
+                node.setown(key.getNode(rsib, type, stats.ctx));
                 if (node != NULL)
                 {
                     nodeKey = 0;
@@ -1548,6 +1531,8 @@ bool CKeyCursor::gtEqual(const char *src, char *dst, KeyStatsCollector &stats)
 {
     key.keySeeks++;
     unsigned lwm = 0;
+    unsigned branchDepth = key.getBranchDepth();
+    unsigned depth = branchDepth;
     if (node)
     {
         // When seeking forward, there are two cases worth optimizing:
@@ -1571,7 +1556,10 @@ bool CKeyCursor::gtEqual(const char *src, char *dst, KeyStatsCollector &stats)
         }
     }
     if (!lwm)
+    {
         node.set(key.rootNode);
+        depth = 0;
+    }
     for (;;)
     {
         unsigned int a = lwm;
@@ -1593,7 +1581,7 @@ bool CKeyCursor::gtEqual(const char *src, char *dst, KeyStatsCollector &stats)
             else
             {
                 offset_t nextPos = node->nextNodeFpos();  // This can happen at eof because of key peculiarity where level above reports ffff as last
-                node.setown(key.getNode(nextPos, stats.ctx));
+                node.setown(key.getNode(nextPos, NodeLeaf, stats.ctx));
                 nodeKey = 0;
             }
             if (node)
@@ -1609,7 +1597,9 @@ bool CKeyCursor::gtEqual(const char *src, char *dst, KeyStatsCollector &stats)
             if (a<node->getNumKeys())
             {
                 offset_t npos = node->getFPosAt(a);
-                node.setown(key.getNode(npos, stats.ctx));
+                depth++;
+                NodeType type = (depth < branchDepth) ? NodeBranch : NodeLeaf;
+                node.setown(key.getNode(npos, type, stats.ctx));
             }
             else
                 return false;
@@ -1622,6 +1612,8 @@ bool CKeyCursor::ltEqual(const char *src, KeyStatsCollector &stats)
     key.keySeeks++;
     matched = false;
     unsigned lwm = 0;
+    unsigned branchDepth = key.getBranchDepth();
+    unsigned depth = branchDepth;
     if (node)
     {
         // When seeking forward, there are two cases worth optimizing:
@@ -1645,7 +1637,10 @@ bool CKeyCursor::ltEqual(const char *src, KeyStatsCollector &stats)
         }
     }
     if (!lwm)
+    {
         node.set(key.rootNode);
+        depth = 0;
+    }
     for (;;)
     {
         unsigned int a = lwm;
@@ -1668,7 +1663,7 @@ bool CKeyCursor::ltEqual(const char *src, KeyStatsCollector &stats)
             else
             {
                 offset_t prevPos = node->prevNodeFpos();
-                node.setown(key.getNode(prevPos, stats.ctx));
+                node.setown(key.getNode(prevPos, NodeLeaf, stats.ctx));
                 if (node)
                     nodeKey = node->getNumKeys()-1;
             }
@@ -1685,7 +1680,9 @@ bool CKeyCursor::ltEqual(const char *src, KeyStatsCollector &stats)
             if (a==node->getNumKeys())
                 a--;   // value being looked for is off the end of the index.
             offset_t npos = node->getFPosAt(a);
-            node.setown(key.getNode(npos, stats.ctx));
+            depth++;
+            NodeType type = (depth < branchDepth) ? NodeBranch : NodeLeaf;
+            node.setown(key.getNode(npos, type, stats.ctx));
             if (!node)
                 throw MakeStringException(0, "Invalid key %s: child node pointer should never be NULL", key.name.get());
         }
@@ -1725,7 +1722,7 @@ void CKeyCursor::deserializeCursorPos(MemoryBuffer &mb, KeyStatsCollector &stats
         mb.read(nodeKey);
         if (nodeAddress)
         {
-            node.setown(key.getNode(nodeAddress, stats.ctx));
+            node.setown(key.getNode(nodeAddress, NodeLeaf, stats.ctx));
             if (node && keyBuffer)
                 node->getValueAt(nodeKey, keyBuffer);
         }
@@ -1986,7 +1983,7 @@ IndexRowFilter::IndexRowFilter(const RtlRecord &_recInfo) : recInfo(_recInfo)
 {
     keySegCount = recInfo.getNumKeyedFields();
     lastReal = 0;
-    lastFull = 0;
+    lastFull = -1;
     keyedSize = 0;
 }
 
@@ -1994,7 +1991,7 @@ IndexRowFilter::IndexRowFilter(const IndexRowFilter &from, const char *fixedVals
 : recInfo(from.recInfo), keySegCount(from.keySegCount)
 {
     lastReal = 0;
-    lastFull = 0;
+    lastFull = -1;
     keyedSize = 0;
     ForEachItemIn(idx, from.filters)
     {
@@ -2078,6 +2075,9 @@ unsigned IndexRowFilter::setLowAfter(size32_t offset, void *keyBuffer) const
 bool IndexRowFilter::incrementKey(unsigned segno, void *keyBuffer) const
 {
     // Increment the key buffer to next acceptable value
+    if (segno == (unsigned)-1)
+        return false;
+
     for(;;)
     {
         if (queryFilter(segno).incrementKey(keyBuffer, recInfo.getFixedOffset(segno)))
@@ -2130,7 +2130,7 @@ void IndexRowFilter::reset()
 {
     RowFilter::clear();
     lastReal = 0;
-    lastFull = 0;
+    lastFull = -1;
     keyedSize = 0;
 }
 
@@ -2174,8 +2174,9 @@ bool IndexRowFilter::matchesBuffer(const void *buffer, unsigned lastSeg, unsigne
 {
     if (numFilterFields())
     {
+        unsigned maxSeg = lastSeg+1; // avoid unlikely problems with -1
         RtlFixedRow rowInfo(recInfo, buffer, numFilterFields());
-        for (; matchSeg <= lastSeg; matchSeg++)
+        for (; matchSeg < maxSeg; matchSeg++)
         {
             if (!queryFilter(matchSeg).matches(rowInfo))
                 return false;
@@ -2206,7 +2207,6 @@ class CLazyKeyIndex : implements IKeyIndex, public CInterface
     mutable Owned<IKeyIndex> realKey;
     mutable CriticalSection c;
     bool isTLK;
-    bool preloadAllowed;
 
     inline IKeyIndex &checkOpen() const
     {
@@ -2215,11 +2215,11 @@ class CLazyKeyIndex : implements IKeyIndex, public CInterface
         {
             Owned<IMemoryMappedFile> mapped = useMemoryMappedIndexes ? delayedFile->getMappedFile() : nullptr;
             if (mapped)
-                realKey.setown(queryKeyStore()->load(keyfile, crc, mapped, isTLK, preloadAllowed));
+                realKey.setown(queryKeyStore()->load(keyfile, crc, mapped, isTLK));
             else
             {
                 iFileIO.setown(delayedFile->getFileIO());
-                realKey.setown(queryKeyStore()->load(keyfile, crc, iFileIO, fileIdx, isTLK, preloadAllowed));
+                realKey.setown(queryKeyStore()->load(keyfile, crc, iFileIO, fileIdx, isTLK));
             }
             if (!realKey)
             {
@@ -2232,8 +2232,8 @@ class CLazyKeyIndex : implements IKeyIndex, public CInterface
 
 public:
     IMPLEMENT_IINTERFACE;
-    CLazyKeyIndex(const char *_keyfile, unsigned _crc, IDelayedFile *_delayedFile, unsigned _fileIdx, bool _isTLK, bool _preloadAllowed)
-        : keyfile(_keyfile), crc(_crc), fileIdx(_fileIdx), delayedFile(_delayedFile), isTLK(_isTLK), preloadAllowed(_preloadAllowed)
+    CLazyKeyIndex(const char *_keyfile, unsigned _crc, IDelayedFile *_delayedFile, unsigned _fileIdx, bool _isTLK)
+        : keyfile(_keyfile), crc(_crc), fileIdx(_fileIdx), delayedFile(_delayedFile), isTLK(_isTLK)
     {}
 
     virtual bool IsShared() const { return CInterface::IsShared(); }
@@ -2262,23 +2262,23 @@ public:
     virtual const IFileIO *queryFileIO() const override { return iFileIO; } // NB: if not yet opened, will be null
     virtual bool hasSpecialFileposition() const { return checkOpen().hasSpecialFileposition(); }
     virtual bool needsRowBuffer() const { return checkOpen().needsRowBuffer(); }
-    virtual bool prewarmPage(offset_t offset) { return checkOpen().prewarmPage(offset); }
+    virtual bool prewarmPage(offset_t offset, NodeType type) { return checkOpen().prewarmPage(offset, type); }
 
 };
 
-extern jhtree_decl IKeyIndex *createKeyIndex(const char *keyfile, unsigned crc, IFileIO &iFileIO, unsigned fileIdx, bool isTLK, bool preloadAllowed)
+extern jhtree_decl IKeyIndex *createKeyIndex(const char *keyfile, unsigned crc, IFileIO &iFileIO, unsigned fileIdx, bool isTLK)
 {
-    return queryKeyStore()->load(keyfile, crc, &iFileIO, fileIdx, isTLK, preloadAllowed);
+    return queryKeyStore()->load(keyfile, crc, &iFileIO, fileIdx, isTLK);
 }
 
-extern jhtree_decl IKeyIndex *createKeyIndex(const char *keyfile, unsigned crc, bool isTLK, bool preloadAllowed)
+extern jhtree_decl IKeyIndex *createKeyIndex(const char *keyfile, unsigned crc, bool isTLK)
 {
-    return queryKeyStore()->load(keyfile, crc, isTLK, preloadAllowed);
+    return queryKeyStore()->load(keyfile, crc, isTLK);
 }
 
-extern jhtree_decl IKeyIndex *createKeyIndex(const char *keyfile, unsigned crc, IDelayedFile &iFileIO, unsigned fileIdx, bool isTLK, bool preloadAllowed)
+extern jhtree_decl IKeyIndex *createKeyIndex(const char *keyfile, unsigned crc, IDelayedFile &iFileIO, unsigned fileIdx, bool isTLK)
 {
-    return new CLazyKeyIndex(keyfile, crc, &iFileIO, fileIdx, isTLK, preloadAllowed);
+    return new CLazyKeyIndex(keyfile, crc, &iFileIO, fileIdx, isTLK);
 }
 
 extern jhtree_decl void clearKeyStoreCache(bool killAll)
@@ -2306,11 +2306,6 @@ extern jhtree_decl void resetIndexMetrics()
     queryKeyStore()->resetMetrics();
 }
 
-extern jhtree_decl bool setNodeCachePreload(bool preload)
-{
-    return queryNodeCache()->setNodeCachePreload(preload);
-}
-
 extern jhtree_decl size32_t setNodeCacheMem(size32_t cacheSize)
 {
     return queryNodeCache()->setNodeCacheMem(cacheSize);
@@ -2339,33 +2334,34 @@ extern jhtree_decl void getNodeCacheInfo(ICacheInfoRecorder &cacheInfo)
 void CNodeCache::getCacheInfo(ICacheInfoRecorder &cacheInfo)
 {
     CriticalBlock block(lock);
-    preloadCache.reportEntries(cacheInfo); // Debatable whether we should include this
     blobCache.reportEntries(cacheInfo);
     nodeCache.reportEntries(cacheInfo);
     leafCache.reportEntries(cacheInfo);
 }
 
-CJHTreeNode *CNodeCache::getNode(INodeLoader *keyIndex, unsigned iD, offset_t pos, IContextLogger *ctx, bool isTLK)
+CJHTreeNode *CNodeCache::getNode(INodeLoader *keyIndex, unsigned iD, offset_t pos, NodeType type, IContextLogger *ctx, bool isTLK)
 {
     // MORE - could probably be improved - I think having the cache template separate is not helping us here
     // Also one cache per key would surely be faster, and could still use a global total
     if (!pos)
         return NULL;
-    { 
-        // It's a shame that we don't know the type before we read it. But probably not that big a deal
-        CriticalBlock block(lock);
-        CKeyIdAndPos key(iD, pos);
-        if (preloadNodes)
-        {
-            CJHTreeNode *cacheNode = preloadCache.query(key);
-            if (cacheNode)
-            {
-                cacheHits++;
-                if (ctx) ctx->noteStatistic(StNumPreloadCacheHits, 1);
-                preloadCacheHits++;
-                return LINK(cacheNode);
-            }
-        }
+
+    CKeyIdAndPos key(iD, pos);
+    //NOTE: TLK leaf nodes are currently cached along with branches, not with leaves.  It might be better if this was a separate cache.
+    NodeType cacheType = isTLK ? NodeBranch : type;
+
+    // No benefit in caching the following, especially since they will evict useful pages
+    // could also check cache[cacheType] if an array was used and avoid the tests in the critical section (change later)
+    if ((cacheType == NodeMeta) || (cacheType == NodeBloom))
+    {
+        return keyIndex->loadNode(pos);
+    }
+
+    CriticalBlock block(lock);
+    //TBD: Use arrays indexed by the caching type to simplify this code.
+    switch (cacheType)
+    {
+    case NodeBranch:
         if (cacheNodes)
         {
             CJHTreeNode *cacheNode = nodeCache.query(key);
@@ -2377,6 +2373,8 @@ CJHTreeNode *CNodeCache::getNode(INodeLoader *keyIndex, unsigned iD, offset_t po
                 return LINK(cacheNode);
             }
         }
+        break;
+    case NodeLeaf:
         if (cacheLeaves)
         {
             CJHTreeNode *cacheNode = leafCache.query(key);
@@ -2388,6 +2386,8 @@ CJHTreeNode *CNodeCache::getNode(INodeLoader *keyIndex, unsigned iD, offset_t po
                 return LINK(cacheNode);
             }
         }
+        break;
+    case NodeBlob:
         if (cacheBlobs)
         {
             CJHTreeNode *cacheNode = blobCache.query(key);
@@ -2399,91 +2399,69 @@ CJHTreeNode *CNodeCache::getNode(INodeLoader *keyIndex, unsigned iD, offset_t po
                 return LINK(cacheNode);
             }
         }
-        CJHTreeNode *node;
-        {
-            CriticalUnblock block(lock);
-            node = keyIndex->loadNode(pos);  // NOTE - don't want cache locked while we load!
-        }
-        cacheAdds++;
-        if (node->isBlob())
+        break;
+    }
+    CJHTreeNode *node;
+    {
+        CriticalUnblock block(lock);
+        node = keyIndex->loadNode(pos);  // NOTE - don't want cache locked while we load!
+    }
+    cacheAdds++;
+    switch (cacheType)
+    {
+    case NodeBranch:
+        if (cacheNodes)
         {
-            if (cacheBlobs)
+            CJHTreeNode *cacheNode = nodeCache.query(key); // check if added to cache while we were reading
+            if (cacheNode)
             {
-                CJHTreeNode *cacheNode = blobCache.query(key); // check if added to cache while we were reading
-                if (cacheNode)
-                {
-                    ::Release(node);
-                    cacheHits++;
-                    if (ctx) ctx->noteStatistic(StNumBlobCacheHits, 1);
-                    blobCacheHits++;
-                    return LINK(cacheNode);
-                }
-                if (ctx) ctx->noteStatistic(StNumBlobCacheAdds, 1);
-                blobCacheAdds++;
-                blobCache.add(key, *LINK(node));
+                ::Release(node);
+                cacheHits++;
+                if (ctx) ctx->noteStatistic(StNumNodeCacheHits, 1);
+                nodeCacheHits++;
+                return LINK(cacheNode);
             }
+            if (ctx) ctx->noteStatistic(StNumNodeCacheAdds, 1);
+            nodeCacheAdds++;
+            nodeCache.add(key, *LINK(node));
         }
-        else if (node->isLeaf() && !isTLK) // leaves in TLK are cached as if they were nodes
+        break;
+    case NodeLeaf:
+        if (cacheLeaves)
         {
-            if (cacheLeaves)
+            CJHTreeNode *cacheNode = leafCache.query(key); // check if added to cache while we were reading
+            if (cacheNode)
             {
-                CJHTreeNode *cacheNode = leafCache.query(key); // check if added to cache while we were reading
-                if (cacheNode)
-                {
-                    ::Release(node);
-                    cacheHits++;
-                    if (ctx) ctx->noteStatistic(StNumLeafCacheHits, 1);
-                    leafCacheHits++;
-                    return LINK(cacheNode);
-                }
-                if (ctx) ctx->noteStatistic(StNumLeafCacheAdds, 1);
-                leafCacheAdds++;
-                leafCache.add(key, *LINK(node));
+                ::Release(node);
+                cacheHits++;
+                if (ctx) ctx->noteStatistic(StNumLeafCacheHits, 1);
+                leafCacheHits++;
+                return LINK(cacheNode);
             }
+            if (ctx) ctx->noteStatistic(StNumLeafCacheAdds, 1);
+            leafCacheAdds++;
+            leafCache.add(key, *LINK(node));
         }
-        else
+        break;
+    case NodeBlob:
+        if (cacheBlobs)
         {
-            if (cacheNodes)
+            CJHTreeNode *cacheNode = blobCache.query(key); // check if added to cache while we were reading
+            if (cacheNode)
             {
-                CJHTreeNode *cacheNode = nodeCache.query(key); // check if added to cache while we were reading
-                if (cacheNode)
-                {
-                    ::Release(node);
-                    cacheHits++;
-                    if (ctx) ctx->noteStatistic(StNumNodeCacheHits, 1);
-                    nodeCacheHits++;
-                    return LINK(cacheNode);
-                }
-                if (ctx) ctx->noteStatistic(StNumNodeCacheAdds, 1);
-                nodeCacheAdds++;
-                nodeCache.add(key, *LINK(node));
+                ::Release(node);
+                cacheHits++;
+                if (ctx) ctx->noteStatistic(StNumBlobCacheHits, 1);
+                blobCacheHits++;
+                return LINK(cacheNode);
             }
+            if (ctx) ctx->noteStatistic(StNumBlobCacheAdds, 1);
+            blobCacheAdds++;
+            blobCache.add(key, *LINK(node));
         }
-        return node;
+        break;
     }
-}
-
-void CNodeCache::preload(CJHTreeNode *node, unsigned iD, offset_t pos, IContextLogger *ctx)
-{
-    assertex(pos);
-    assertex(preloadNodes);
-    CriticalBlock block(lock);
-    CKeyIdAndPos key(iD, pos);
-    CJHTreeNode *cacheNode = preloadCache.query(key);
-    if (!cacheNode)
-    {
-        cacheAdds++;
-        if (ctx) ctx->noteStatistic(StNumPreloadCacheAdds, 1);
-        preloadCacheAdds++;
-        preloadCache.add(key, *LINK(node));
-    }
-}
-
-bool CNodeCache::isPreloaded(unsigned iD, offset_t pos)
-{
-    CriticalBlock block(lock);
-    CKeyIdAndPos key(iD, pos);
-    return NULL != preloadCache.query(key);
+    return node;
 }
 
 RelaxedAtomic<unsigned> cacheAdds;
@@ -3104,8 +3082,8 @@ class IKeyManagerTest : public CppUnit::TestFixture
         buildTestKeys(false, true, false, false);
         {
             // We are going to treat as a 7-byte field then a 3-byte field, and request the datasorted by the 3-byte...
-            Owned <IKeyIndex> index1 = createKeyIndex("keyfile1.$$$", 0, false, false);
-            Owned <IKeyIndex> index2 = createKeyIndex("keyfile2.$$$", 0, false, false);
+            Owned <IKeyIndex> index1 = createKeyIndex("keyfile1.$$$", 0, false);
+            Owned <IKeyIndex> index2 = createKeyIndex("keyfile2.$$$", 0, false);
             Owned<IKeyIndexSet> keyset = createKeyIndexSet();
             keyset->addIndex(index1.getClear());
             keyset->addIndex(index2.getClear());
@@ -3319,7 +3297,7 @@ protected:
         const RtlRecord &recInfo = meta->queryRecordAccessor(true);
         buildTestKeys(variable, useTrailingHeader, noSeek, quickCompressed);
         {
-            Owned <IKeyIndex> index1 = createKeyIndex("keyfile1.$$$", 0, false, false);
+            Owned <IKeyIndex> index1 = createKeyIndex("keyfile1.$$$", 0, false);
             Owned <IKeyManager> tlk1 = createLocalKeyManager(recInfo, index1, NULL, false, false);
             Owned<IStringSet> sset1 = createStringSet(10);
             sset1->addRange("0000000001", "0000000100");
@@ -3350,7 +3328,7 @@ protected:
             ASSERT(ssetx->numValues() == (unsigned) -1);
 
 
-            Owned <IKeyIndex> index2 = createKeyIndex("keyfile2.$$$", 0, false, false);
+            Owned <IKeyIndex> index2 = createKeyIndex("keyfile2.$$$", 0, false);
             Owned <IKeyManager> tlk2 = createLocalKeyManager(recInfo, index2, NULL, false, false);
             Owned<IStringSet> sset2 = createStringSet(10);
             sset2->addRange("0000000001", "0000000100");

+ 7 - 6
system/jhtree/jhtree.hpp

@@ -29,6 +29,8 @@
 #include "jlog.hpp"
 #include "errorlist.h"
 
+enum NodeType : char;
+
 class BloomFilter;
 interface IIndexFilterList;
 
@@ -113,7 +115,7 @@ interface jhtree_decl IKeyIndex : public IKeyIndexBase
     virtual const IFileIO *queryFileIO() const = 0;
     virtual bool hasSpecialFileposition() const = 0;
     virtual bool needsRowBuffer() const = 0;
-    virtual bool prewarmPage(offset_t offset) = 0;
+    virtual bool prewarmPage(offset_t offset, NodeType type) = 0;
 };
 
 interface IKeyArray : extends IInterface
@@ -135,7 +137,7 @@ interface jhtree_decl IKeyIndexSet : public IKeyIndexBase
 
 interface ICacheInfoRecorder
 {
-    virtual void noteWarm(unsigned fileIdx, offset_t page, size32_t len) = 0;
+    virtual void noteWarm(unsigned fileIdx, offset_t page, size32_t len, NodeType type) = 0;
 };
 
 
@@ -145,16 +147,15 @@ extern jhtree_decl void clearKeyStoreCacheEntry(const IFileIO *io);
 extern jhtree_decl unsigned setKeyIndexCacheSize(unsigned limit);
 extern jhtree_decl void clearNodeCache();
 // these methods return previous values
-extern jhtree_decl bool setNodeCachePreload(bool preload);
 extern jhtree_decl size32_t setNodeCacheMem(size32_t cacheSize);
 extern jhtree_decl size32_t setLeafCacheMem(size32_t cacheSize);
 extern jhtree_decl size32_t setBlobCacheMem(size32_t cacheSize);
 
 extern jhtree_decl void getNodeCacheInfo(ICacheInfoRecorder &cacheInfo);
 
-extern jhtree_decl IKeyIndex *createKeyIndex(const char *filename, unsigned crc, bool isTLK, bool preloadAllowed);
-extern jhtree_decl IKeyIndex *createKeyIndex(const char *filename, unsigned crc, IFileIO &ifile, unsigned fileIdx, bool isTLK, bool preloadAllowed);
-extern jhtree_decl IKeyIndex *createKeyIndex(const char *filename, unsigned crc, IDelayedFile &ifile, unsigned fileIdx, bool isTLK, bool preloadAllowed);
+extern jhtree_decl IKeyIndex *createKeyIndex(const char *filename, unsigned crc, bool isTLK);
+extern jhtree_decl IKeyIndex *createKeyIndex(const char *filename, unsigned crc, IFileIO &ifile, unsigned fileIdx, bool isTLK);
+extern jhtree_decl IKeyIndex *createKeyIndex(const char *filename, unsigned crc, IDelayedFile &ifile, unsigned fileIdx, bool isTLK);
 
 extern jhtree_decl bool isIndexFile(const char *fileName);
 extern jhtree_decl bool isIndexFile(IFile *file);

+ 10 - 10
system/jhtree/jhtree.ipp

@@ -44,13 +44,13 @@ private:
             return useId;
         return ++nextId;
     }
-    IKeyIndex *doload(const char *fileName, unsigned crc, IReplicatedFile *part, IFileIO *iFileIO, unsigned fileIdx, IMemoryMappedFile *iMappedFile, bool isTLK, bool allowPreload);
+    IKeyIndex *doload(const char *fileName, unsigned crc, IReplicatedFile *part, IFileIO *iFileIO, unsigned fileIdx, IMemoryMappedFile *iMappedFile, bool isTLK);
 public:
     CKeyStore();
     ~CKeyStore();
-    IKeyIndex *load(const char *fileName, unsigned crc, bool isTLK, bool allowPreload);
-    IKeyIndex *load(const char *fileName, unsigned crc, IFileIO *iFileIO, unsigned fileIdx, bool isTLK, bool allowPreload);
-    IKeyIndex *load(const char *fileName, unsigned crc, IMemoryMappedFile *iMappedFile, bool isTLK, bool allowPreload);
+    IKeyIndex *load(const char *fileName, unsigned crc, bool isTLK);
+    IKeyIndex *load(const char *fileName, unsigned crc, IFileIO *iFileIO, unsigned fileIdx, bool isTLK);
+    IKeyIndex *load(const char *fileName, unsigned crc, IMemoryMappedFile *iMappedFile, bool isTLK);
     void clearCache(bool killAll);
     void clearCacheEntry(const char *name);
     void clearCacheEntry(const IFileIO *io);
@@ -94,14 +94,13 @@ protected:
     offset_t latestGetNodeOffset;
 
     CJHTreeNode *loadNode(char *nodeData, offset_t pos, bool needsCopy);
-    CJHTreeNode *getNode(offset_t offset, IContextLogger *ctx);
+    CJHTreeNode *getNode(offset_t offset, NodeType type, IContextLogger *ctx);
     CJHTreeBlobNode *getBlobNode(offset_t nodepos);
 
 
     CKeyIndex(unsigned _iD, const char *_name);
     ~CKeyIndex();
-    void init(KeyHdr &hdr, bool isTLK, bool allowPreload);
-    void cacheNodes(CNodeCache *cache, offset_t nodePos, bool isTLK);
+    void init(KeyHdr &hdr, bool isTLK);
     void loadBloomFilters();
     
 public:
@@ -133,12 +132,13 @@ public:
     virtual offset_t queryMetadataHead();
     virtual IPropertyTree * getMetadata();
 
+    unsigned getBranchDepth() const { return keyHdr->getHdrStruct()->hdrseq; }
     bool bloomFilterReject(const IIndexFilterList &segs) const;
 
     virtual unsigned getNodeSize() { return keyHdr->getNodeSize(); }
     virtual bool hasSpecialFileposition() const;
     virtual bool needsRowBuffer() const;
-    virtual bool prewarmPage(offset_t page);
+    virtual bool prewarmPage(offset_t page, NodeType type);
  
  // INodeLoader impl.
     virtual CJHTreeNode *loadNode(offset_t offset) = 0;
@@ -166,7 +166,7 @@ private:
     void cacheNodes(CNodeCache *cache, offset_t firstnode, bool isTLK);
     
 public:
-    CDiskKeyIndex(unsigned _iD, IFileIO *_io, const char *_name, bool _isTLK, bool _allowPreload);
+    CDiskKeyIndex(unsigned _iD, IFileIO *_io, const char *_name, bool _isTLK);
 
     virtual const char *queryFileName() { return name.get(); }
     virtual const IFileIO *queryFileIO() const override { return io; }
@@ -276,7 +276,7 @@ protected:
 
     const RtlRecord &recInfo;
     unsigned lastReal = 0;
-    unsigned lastFull = 0;
+    unsigned lastFull = -1;
     unsigned keyedSize = 0;
     unsigned keySegCount = 0;
 

+ 1 - 1
system/jhtree/keydiff.cpp

@@ -221,7 +221,7 @@ public:
         keyFileIO.setown(keyFile->open(IFOread));
         if(!keyFileIO)
             throw MakeStringException(0, "Could not read index file %s", filename);
-        keyIndex.setown(createKeyIndex(filename, 0, *keyFileIO, (unsigned) -1, false, false)); // MORE - should we care about crc?
+        keyIndex.setown(createKeyIndex(filename, 0, *keyFileIO, (unsigned) -1, false)); // MORE - should we care about crc?
         unsigned flags = keyIndex->getFlags();
         variableWidth = ((flags & HTREE_VARSIZE) == HTREE_VARSIZE);
         if((flags & HTREE_QUICK_COMPRESSED_KEY) == HTREE_QUICK_COMPRESSED_KEY)

+ 55 - 0
testing/regress/ecl/indexlimit2.ecl

@@ -0,0 +1,55 @@
+/*##############################################################################
+
+    HPCC SYSTEMS software Copyright (C) 2020 HPCC Systems®.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+############################################################################## */
+
+//class=file
+//class=index
+//version multiPart=false
+//version multiPart=true
+//version multiPart=true,useLocal=true
+//version multiPart=true,useTranslation=true
+
+import ^ as root;
+multiPart := #IFDEFINED(root.multiPart, false);
+useLocal := #IFDEFINED(root.useLocal, true);
+useTranslation := #IFDEFINED(root.useTranslation, false);
+
+//--- end of version configuration ---
+
+#option ('layoutTranslation', useTranslation);
+#onwarning (4523, ignore);
+#onwarning (5402, ignore);
+#onwarning (4522, ignore);
+
+import $.setup;
+Files := setup.Files(multiPart, useLocal, useTranslation);
+
+IMPORT Std;
+
+set of unsigned4 myAll := ALL : STORED('myAll');
+
+sequential(
+    output(count(nofold(limit(Files.DG_IntIndex, 64, KEYED, COUNT))));
+    output(count(nofold(limit(Files.DG_IntIndex, 63, KEYED, COUNT, SKIP))));
+
+
+    output(count(nofold(limit(Files.DG_IntIndex(DG_ParentID in myAll), 64, KEYED, COUNT))));
+    output(count(nofold(limit(Files.DG_IntIndex(DG_ParentID in myAll), 63, KEYED, COUNT, SKIP))));
+
+    output(count(Files.DG_IntIndex));
+    output(count(Files.DG_IntIndex(WILD(DG_ParentID)), KEYED));
+    output(count(Files.DG_IntIndex(KEYED(DG_ParentID in myAll)), KEYED));
+);

+ 21 - 0
testing/regress/ecl/key/indexlimit2.xml

@@ -0,0 +1,21 @@
+<Dataset name='Result 1'>
+ <Row><Result_1>64</Result_1></Row>
+</Dataset>
+<Dataset name='Result 2'>
+ <Row><Result_2>0</Result_2></Row>
+</Dataset>
+<Dataset name='Result 3'>
+ <Row><Result_3>64</Result_3></Row>
+</Dataset>
+<Dataset name='Result 4'>
+ <Row><Result_4>0</Result_4></Row>
+</Dataset>
+<Dataset name='Result 5'>
+ <Row><Result_5>64</Result_5></Row>
+</Dataset>
+<Dataset name='Result 6'>
+ <Row><Result_6>64</Result_6></Row>
+</Dataset>
+<Dataset name='Result 7'>
+ <Row><Result_7>64</Result_7></Row>
+</Dataset>

+ 1 - 1
thorlcr/activities/hashdistrib/thhashdistribslave.cpp

@@ -2505,7 +2505,7 @@ public:
         // NB: this TLK is an in-memory TLK serialized from the master - the name is for tracing by the key code only
         VStringBuffer name("index");
         name.append(queryId()).append("_tlk");
-        lookup = new CKeyLookup(*this, helper, createKeyIndex(name.str(), 0, *iFileIO, (unsigned) -1, true, false)); // MORE - crc is not 0...
+        lookup = new CKeyLookup(*this, helper, createKeyIndex(name.str(), 0, *iFileIO, (unsigned) -1, true)); // MORE - crc is not 0...
         ihash = lookup;
     }
 };

+ 1 - 1
thorlcr/activities/indexread/thindexread.cpp

@@ -162,7 +162,7 @@ protected:
                         rfn.getPath(remotePath);
                         unsigned crc = 0;
                         part->getCrc(crc);
-                        keyIndex.setown(createKeyIndex(remotePath.str(), crc, false, false));
+                        keyIndex.setown(createKeyIndex(remotePath.str(), crc, false));
                         break;
                     }
                 }

+ 1 - 1
thorlcr/activities/indexread/thindexreadslave.cpp

@@ -306,7 +306,7 @@ public:
                 StringBuffer path;
                 rfn.getPath(path); // NB: use for tracing only, IDelayedFile uses IPartDescriptor and any copy
 
-                Owned<IKeyIndex> keyIndex = createKeyIndex(path, crc, *lazyIFileIO, (unsigned) -1, false, false);
+                Owned<IKeyIndex> keyIndex = createKeyIndex(path, crc, *lazyIFileIO, (unsigned) -1, false);
                 Owned<IKeyManager> klManager = createLocalKeyManager(helper->queryDiskRecordSize()->queryRecordAccessor(true), keyIndex, nullptr, helper->hasNewSegmentMonitors(), false);
                 if (localMerge)
                 {

+ 2 - 2
thorlcr/activities/keyedjoin/thkeyedjoinslave-legacy.cpp

@@ -1637,7 +1637,7 @@ class CKeyedJoinSlave : public CSlaveActivity, implements IJoinProcessor, implem
 
             Owned<IFileIO> lazyFileIO = queryThor().queryFileCache().lookupIFileIO(*this, indexName, filePart);
             Owned<IDelayedFile> delayedFile = createDelayedFile(lazyFileIO);
-            Owned<IKeyIndex> keyIndex = createKeyIndex(filename, crc, *delayedFile, (unsigned) -1, false, false);
+            Owned<IKeyIndex> keyIndex = createKeyIndex(filename, crc, *delayedFile, (unsigned) -1, false);
             keyIndexes.append(*keyIndex.getClear());
         }
     }
@@ -1960,7 +1960,7 @@ public:
                     Owned<IFileIO> iFileIO = createIFileI(lenArray.item(p), tlkMb.toByteArray()+posArray.item(p));
                     StringBuffer name("TLK");
                     name.append('_').append(container.queryId()).append('_');
-                    tlkKeySet->addIndex(createKeyIndex(name.append(p).str(), 0, *iFileIO, (unsigned) -1, true, false)); // MORE - not the right crc
+                    tlkKeySet->addIndex(createKeyIndex(name.append(p).str(), 0, *iFileIO, (unsigned) -1, true)); // MORE - not the right crc
                 }
             }
             if (needsDiskRead)

+ 3 - 3
thorlcr/activities/keyedjoin/thkeyedjoinslave.cpp

@@ -2334,7 +2334,7 @@ class CKeyedJoinSlave : public CSlaveActivity, implements IJoinProcessor, implem
         {
             Owned<IFileIO> lazyFileIO = queryThor().queryFileCache().lookupIFileIO(*this, indexName, filePart);
             Owned<IDelayedFile> delayedFile = createDelayedFile(lazyFileIO);
-            return createKeyIndex(filename, crc, *delayedFile, (unsigned) -1, false, false);
+            return createKeyIndex(filename, crc, *delayedFile, (unsigned) -1, false);
         }
         else
         {
@@ -2343,7 +2343,7 @@ class CKeyedJoinSlave : public CSlaveActivity, implements IJoinProcessor, implem
              * The underlying IFileIO can later be closed by fhe file caching mechanism.
              */
             Owned<IFileIO> lazyIFileIO = queryThor().queryFileCache().lookupIFileIO(*this, indexName, filePart);
-            return createKeyIndex(filename, crc, *lazyIFileIO, (unsigned) -1, false, false);
+            return createKeyIndex(filename, crc, *lazyIFileIO, (unsigned) -1, false);
         }
     }
     IKeyManager *createPartKeyManager(unsigned partNo, unsigned copy)
@@ -2973,7 +2973,7 @@ public:
                     Owned<IFileIO> iFileIO = createIFileI(lenArray.item(p), tlkMb.toByteArray()+posArray.item(p));
                     StringBuffer name("TLK");
                     name.append('_').append(container.queryId()).append('_');
-                    Owned<IKeyIndex> tlkKeyIndex = createKeyIndex(name.append(p).str(), 0, *iFileIO, (unsigned) -1, true, false); // MORE - not the right crc
+                    Owned<IKeyIndex> tlkKeyIndex = createKeyIndex(name.append(p).str(), 0, *iFileIO, (unsigned) -1, true); // MORE - not the right crc
                     tlkKeyIndexes.append(*tlkKeyIndex.getClear());
                 }
             }

+ 1 - 1
thorlcr/slave/slavmain.cpp

@@ -393,7 +393,7 @@ class CKJService : public CSimpleInterfaceOf<IKJService>, implements IThreaded,
         CKeyLookupContext(CKJService &_service, CActivityContext *_activityCtx, const CLookupKey &_key)
             : CContext(_service, _activityCtx), key(_key)
         {
-            keyIndex.setown(createKeyIndex(key.fname, key.crc, false, false));
+            keyIndex.setown(createKeyIndex(key.fname, key.crc, false));
             expectedFormat.set(activityCtx->queryHelper()->queryIndexRecordSize());
             expectedFormatCrc = activityCtx->queryHelper()->getIndexFormatCrc();
         }

+ 1 - 1
tools/dumpkey/dumpkey.cpp

@@ -144,7 +144,7 @@ int main(int argc, const char **argv)
         {
             Owned <IKeyIndex> index;
             const char * keyName = files.item(idx);
-            index.setown(createKeyIndex(keyName, 0, false, false));
+            index.setown(createKeyIndex(keyName, 0, false));
             size32_t key_size = index->keySize();  // NOTE - in variable size case, this is 32767
             size32_t keyedSize = index->keyedSize();
             unsigned nodeSize = index->getNodeSize();