WL#17287: Cache dict_index_t objects while parsing redo log

Affects: Server-26.x   —   Status: Complete

The goal of this task is to improve speed of parsing the redo log.
The current bottleneck is mlog_parse_index() which has to be called for many
redo log types which deal with index records, and are prefixed with a
description of the index, as a necessary context for identifying fields and
their types in what follows.
This function is slow, as it has to allocate memory for a temporary dict_index_t
and dict_table_t objects, fill them in using complicated logic.

Via experiments we have established that a following set of changes improves
performance, and each of them has valuable contribution even when others are
present:

1. avoid allocating small strings for column names, as they aren't used anyway
2. initialize the mem_heap_t with a larger first block of memory, to avoid
several small allocations
3. cache the result of the parsing instead of throwing it away

This WL is addressing 3.
NFR-1: No regression in terms of correctness of redo log recovery: existing MTRs related to recovery should still pass.

NFR-2: No regression in terms of performance of redo log recovery: the duration of redo log recovery should not increase. 

NFR-3: It should improve performance of Read Replica's Applier in terms of LSN/s.

There are no observable changes to interfaces, file formats, SQL syntax etc.
This is just a change in implementation which improves performance.
This patch introduces:

* `ut::Per_thread` which is an alternative to `thread_local T`, with an
  explicit control over the moment all instances of T are destructed.
* `ut::prefix_cache` which is an LRU cache which maps
  strings to `V`, supporting search for longest common prefix of a given
  buffer which is currently mapped to any `V`.
* Uses above two primitives to create per-thread cache from redo log
  fragments to dict_index_t instances, which can be used by the redo
  log parser to determine longest prefix of the buffer which is
  recognized as already seen descrption of a cached dict_index_t.

To be more specific:

* `recv_sys_t`  which is the type of the global `recv_sys` will now have an
  extra field of type `ib::redo::Redo_applier_registry per_thread_applier`, 
  which is a thin owning container for 
  `ut::Per_thread>` with a `reset()`
  function to free all the `Redo_applier` instances
* the `Redo_applier::Impl` will have a new field `Parsed_index_lru_cache
  m_parsed_index_cache`, which is a thin owning wrapper around
  `ut::prefix_cache` which manages life-cyle of the `dict_index_t`
  objects

As for the internal implementation of the two new reusable classes:

* `ut::Per_thread` uses an unordered_map from `std::thread::id` to
  `Value`, and its constructor takes `std::function factory` which is 
  used to create "empty" instances of `Value`. This is meant to make it possible
  to use custom allocators, such us our `ut::make_unique` which tracks memory
  usage in PFS. The unordered_map is protected by a mutex for the duration of 
  looking up the instance meant for the current thread, so callers should then
  cache the returned `Value*` locally for subsequent reuse without mutex. We 
  assume value-semantics for `Value`, but the user can, and does use Value 
  which is itself a unique_ptr for example.
* the `ut::prefix_cache` is using an `std::map` from sequences
  of CharType into structs which combine Value with an iterator from LRU list. 
  So the Entries are both, in the map, and in the LRU, with the quite natural 
  mechanics for lookup, and insert. The most important operation,
  `find_value_for_prefix_of` is using a rather simple approach of using STL's 
  lower_bound (which is exactly what we need, once we flip the order to
  `std::greater<>`). Other, more complicated designs (such as trying to remember

  longest common prefix along the path as we descend the R-B-tree) were also
  tried, but didn't improve performance, yet complicated the code.