Joe McDonnell has posted comments on this change. ( http://gerrit.cloudera.org:8080/18191 )
Change subject: IMPALA-9433: Improved caching of HdfsFileHandles ...................................................................... Patch Set 21: (8 comments) > (12 comments) > > > > > (8 comments) > > > > > > > > Thank you for taking this on. There is a lot of history here. > > > > Originally, the file handle cache used a generic structure > like > > > > this: > > > > https://github.com/apache/impala/blob/branch-2.10.0/be/src/util/lru-cache.h > > > > > > > > In my rewrite, I switched it to remove the generic structure. > > > This > > > > heads back in the other direction. > > > > > > > > I like that you have backend tests for the generic data > > > structure, > > > > which is definitely one advantage of that approach. One > > question > > > I > > > > have about moving back to a generic structure is whether we > > would > > > > be able to add new customization to the file handle cache > case. > > I > > > > had been thinking about adding a file structure that could > > > contain > > > > additional per-file data and/or stats. Is that possible with > > the > > > > new generic structure? > > > > > > "had been thinking about adding a file structure that could > > contain > > > additional per-file data and/or stat" > > > > > > I was thinking about similar things (e.g. caching processed > > > Parquet/ORC headers), but this seems a somewhat different > feature > > > to me - while we want to cache more than one file handle per > file > > > and apply LRU logic per handle, we want to cache data for a > file > > > only once and apply LRU logic per file. > > > > Yeah, it's unclear whether we would ever want to extend the file > > handle cache > > to deal with other things. Separate data structures may be > cleaner > > even > > if it means duplicating filename strings or other things. The > file > > handle > > cache is pretty unusual in structure and historically we haven't > > extended > > it. I don't have any strong objection to a generic structure. I > > just wanted > > to think through whether there are any extensions that would end > up > > getting more complicated. > > > > For more ordinary caches that don't need duplication, we should > be > > using the > > cache implementations in be/src/util/cache, because that also > gets > > us different > > cache eviction policies. > > Moving the caching to a separate class definitely helps with > testing. > > I think making it generic helps with encapsulation too, as the > caching algorithm (even if it is somewhat specialized to a use > case) has nothing to do with the stored data. Moreover, making it > generic helped with unit testing too, as I didn't have to juggle > around file handles to test out the caching feature. > > As I understand, the requirements were simple enough to fit it in a > generic structure. This would help in the future to decide - if we > ever want to use something similar to this - whether if it's easier > to extend with some configurability or not. We can specialize this > more towards file handle caching, although the arguments for > templated key/value are still applicable in that case, maybe we can > change the name and place of the code for something less generic. > > Regarding to per-file data/stats, we could squeeze it in (e.g. > unordered_map<string, pair<Data, list<Handler>> but I don't think > that is a good direction, it breaks the encapsulation of the cache. > I would rather put a container in FileHandleCache class next to the > cache and do handle based operations/metrics during creation > (GetFileHandle() new entry branch) and do access based > operation/metrics in FileHandleCache::Accessor This generic design is good, and we don't need to overthink this. My feeling is that the odds of us reusing the generic structure for something else are pretty low. (We'll find out! Maybe I'm wrong.) If you believe that, then I think an intermediate point is a non-generic structure that allows mocking the FileHandle. In other words, it allows for writing backend tests without creating real file handles, but it also isn't dealing with arbitrary types. Either way, I think if we need to extend this, then we will find a way to make it work. I'm not concerned. http://gerrit.cloudera.org:8080/#/c/18191/21/be/src/runtime/io/handle-cache.inline.h File be/src/runtime/io/handle-cache.inline.h: http://gerrit.cloudera.org:8080/#/c/18191/21/be/src/runtime/io/handle-cache.inline.h@167 PS21, Line 167: // Opening a file handle requires talking to the NameNode so it can take some time. : RETURN_IF_ERROR(accessor_tmp.Get()->Init(hdfs_monitor_)); > Yes, it will be in_use, does not lock the cache and not available for other Ok, great! http://gerrit.cloudera.org:8080/#/c/18191/21/be/src/util/lru-multi-cache.h File be/src/util/lru-multi-cache.h: http://gerrit.cloudera.org:8080/#/c/18191/21/be/src/util/lru-multi-cache.h@170 PS21, Line 170: Container_internal& container; : typename Container_internal::iterator it; > Something has to own the objects. The point of unordered_map<string, list> I was thinking about whether there is a way to avoid having the element keep a link to itself. Intrusive lists don't require that, but then you need to figure out ownership. Implementing ownership yourself is doable, but it probably isn't worth it. If the Accessor stored an iterator to the std::list element for the ValueType_internal rather than a pointer, then I think the self-link wouldn't be necessary. For std::list, iterators are not invalidated unless the element itself is deleted, so it is safe to save this. The iterator provides direct access to the ValueType_internal as well. We'd need to figure out how to initialize an empty Accessor, but that seems solvable. http://gerrit.cloudera.org:8080/#/c/18191/21/be/src/util/lru-multi-cache.h@252 PS21, Line 252: ValueType_internal* p_value_internal_; Consider making this an iterator to the std::list element http://gerrit.cloudera.org:8080/#/c/18191/21/be/src/util/lru-multi-cache.inline.h File be/src/util/lru-multi-cache.inline.h: http://gerrit.cloudera.org:8080/#/c/18191/21/be/src/util/lru-multi-cache.inline.h@147 PS21, Line 147: // Move the object to the back of the owning list as it is no longer available : container.splice(container.end(), container, container_it); > No other reason, just what you mentioned. With moving the mtime to the key, Ok, that makes sense. http://gerrit.cloudera.org:8080/#/c/18191/21/be/src/util/lru-multi-cache.inline.h@175 PS21, Line 175: container.emplace_back((*this), stored_key, container, std::forward<Args>(args)...); If you wanted to get an iterator back, the regular emplace() call returns an iterator to the new element. So, if you do emplace(container.end(), Args...) that would get the iterator to the element added. http://gerrit.cloudera.org:8080/#/c/18191/21/be/src/util/lru-multi-cache.inline.h@201 PS21, Line 201: // Move the object to the front, keep LRU relation in owning list too to : // be able to age out unused objects : container.splice(container.begin(), container, p_value_internal->it); > I agree that moving during get is not necessary, but I think it is useful d Basically, once you decide to move the in_use=true to the end of the list in Get(), then you also need to move things here. This makes sense. http://gerrit.cloudera.org:8080/#/c/18191/21/be/src/util/lru-multi-cache.inline.h@222 PS21, Line 222: if (container.size() == 1) { : // Last element, owning list can be removed to prevent aging : hash_table_.erase(p_value_internal->key); : } else { : // Remove from owning list : container.erase(p_value_internal->it); : } > At this point, the element is in use, not in the LRU list. It gets unlinked Ok, so can we add a DCHECK that this is in use? Maybe also add a comment that it is not on the LRU list. http://gerrit.cloudera.org:8080/#/c/18191/21/be/src/util/lru-multi-cache.inline.h@251 PS21, Line 251: // Remove from LRU cache : value_internal.member_hook.unlink(); Nit: Can we add a DCHECK that this is not in use? -- To view, visit http://gerrit.cloudera.org:8080/18191 To unsubscribe, visit http://gerrit.cloudera.org:8080/settings Gerrit-Project: Impala-ASF Gerrit-Branch: master Gerrit-MessageType: comment Gerrit-Change-Id: I6b5c5e9e2b5db2847ab88c41f667c9ca1b03d51a Gerrit-Change-Number: 18191 Gerrit-PatchSet: 21 Gerrit-Owner: Gergely Fürnstáhl <gfurnst...@cloudera.com> Gerrit-Reviewer: Csaba Ringhofer <csringho...@cloudera.com> Gerrit-Reviewer: Gergely Fürnstáhl <gfurnst...@cloudera.com> Gerrit-Reviewer: Impala Public Jenkins <impala-public-jenk...@cloudera.com> Gerrit-Reviewer: Joe McDonnell <joemcdonn...@cloudera.com> Gerrit-Reviewer: Zoltan Borok-Nagy <borokna...@cloudera.com> Gerrit-Comment-Date: Wed, 23 Feb 2022 19:33:16 +0000 Gerrit-HasComments: Yes