Symbol CatapultのCacheについて学ぶ - 1 -


CatapultのCacheについて調べる。これはとても重要。

ブロックデータではなくCacheに保存しておくことで、それをバリデータやオブザーバで簡単に呼び出すことが出来る。おそらく基本的には各Pluginをまたがずに保存すべき。つまりmosaicプラグインならその範囲内でCacheを保存し、その範囲内で呼び出す。（一部例外はあるらしい）

今回は`mosaic definitison transaction`に関して、調べてみようと思う。

# Pliuginを見る

まず、プラグイン本体である`MosaicDefinitionTransactionPlugin.cpp`を見てみる。
``` /symbol/client/catapult/plugins/txes/mosaic/src/plugins/MosaicDefinitionTransactionPlugin.cpp
...
// 3. registration
auto properties = MosaicProperties(transaction.Flags, transaction.Divisibility, transaction.Duration);
sub.notify(MosaicNonceNotification(context.SignerAddress, transaction.Nonce, transaction.Id));
sub.notify(MosaicPropertiesNotification(properties));
sub.notify(MosaicDefinitionNotification(context.SignerAddress, transaction.Id, properties));
```
明らかに怪しいのはここですね。だいぶ見慣れてきました。
3つの通知を飛ばしてます。
通知に関してはここを見ます。

``` /symbol/client/catapult/plugins/txes/mosaic/src/model/MosaicNotifications.h
/// Mosaic properties were provided.
DEFINE_MOSAIC_NOTIFICATION(Properties, 0x0001, Validator);

/// Mosaic was defined.
DEFINE_MOSAIC_NOTIFICATION(Definition, 0x0002, All);

/// Mosaic nonce and id were provided.
DEFINE_MOSAIC_NOTIFICATION(Nonce, 0x0003, Validator);

/// Mosaic supply was changed.
DEFINE_MOSAIC_NOTIFICATION(Supply_Change, 0x0004, All);

/// Mosaic rental fee has been sent.
DEFINE_MOSAIC_NOTIFICATION(Rental_Fee, 0x0005, Observer);
```
今回はCacheに関してなのでバリデータは関係ないです、オブザーバだけ見てみます。
3つの中でCHANNELがAll or ObserverなのはMosaicDefinitionNotificationだけですね。

# Observerを見る

``` /symbol/client/catapult/plugins/txes/mosaic/src/observers/MosaicDefinitionObserver.cpp
namespace {
    model::MosaicFlags Xor(model::MosaicFlags lhs, model::MosaicFlags rhs) {
        return static_cast<model::MosaicFlags>(utils::to_underlying_type(lhs) ^ utils::to_underlying_type(rhs));
    }

    model::MosaicProperties MergeProperties(
            const model::MosaicProperties& currentProperties,
            const model::MosaicProperties& notificationProperties,
            NotifyMode mode) {
        auto flags = Xor(currentProperties.flags(), notificationProperties.flags());
        auto divisibility = static_cast<uint8_t>(currentProperties.divisibility() ^ notificationProperties.divisibility());
        auto duration = NotifyMode::Commit == mode
                ? currentProperties.duration() + notificationProperties.duration()
                : currentProperties.duration() - notificationProperties.duration();
        return model::MosaicProperties(flags, divisibility, duration);
    }

    auto ApplyNotification(
            state::MosaicEntry& currentMosaicEntry,
            const model::MosaicDefinitionNotification& notification,
            NotifyMode mode) {
        const auto& currentDefinition = currentMosaicEntry.definition();
        auto newProperties = MergeProperties(currentDefinition.properties(), notification.Properties, mode);
        auto revision = NotifyMode::Commit == mode ? currentDefinition.revision() + 1 : currentDefinition.revision() - 1;
        auto definition = state::MosaicDefinition(currentDefinition.startHeight(), notification.Owner, revision, newProperties);
        return state::MosaicEntry(notification.MosaicId, definition);
    }
}

DEFINE_OBSERVER(MosaicDefinition, model::MosaicDefinitionNotification, [](
        const model::MosaicDefinitionNotification& notification,
        const ObserverContext& context) {
    auto& mosaicCache = context.Cache.sub<cache::MosaicCache>();

    // mosaic supply will always be zero when a mosaic definition is observed
    auto mosaicIter = mosaicCache.find(notification.MosaicId);
    if (mosaicIter.tryGet()) {
        // copy existing mosaic entry before removing
        auto mosaicEntry = mosaicIter.get();
        mosaicCache.remove(notification.MosaicId);

        if (NotifyMode::Rollback == context.Mode && 1 == mosaicEntry.definition().revision())
            return;

        mosaicCache.insert(ApplyNotification(mosaicEntry, notification, context.Mode));
    } else {
        auto definition = state::MosaicDefinition(context.Height, notification.Owner, 1, notification.Properties);
        mosaicCache.insert(state::MosaicEntry(notification.MosaicId, definition));
    }
})
```

色々書かれているけどポイントはここでしょう。
``` cpp
mosaicCache.insert(state::MosaicEntry(notification.MosaicId, definition));
mosaicCache.remove(notification.MosaicId);
```

どうやらキャッシュに追加するにはinsertで削除するのはkeyを引数にしたremoveですね。
要するにここで保存してるんだけど、具体的な中身が気になったので掘り下げてみます。

これをスタートした時点で見つけたのはここ
``` /symbol/client/catapult/plugins/txes/mosaic/src/state/MosaicEntrySerializer.cpp
void MosaicEntrySerializer::Save(const MosaicEntry& entry, io::OutputStream& output) {
    io::Write(output, entry.mosaicId());
    io::Write(output, entry.supply());
    SaveDefinition(output, entry.definition());
}
```
最終的にここにたどり着くはず。だけど過程を知りたくなってしまった。

:::note info
結論から言うとたどり着かない。推測の範疇になるが、各プラグインのオブザーバーではコンテナにinsertするまでを行い、どこかのタイミングでまとめてsaveしているような感じがした。

ここからそんなにちゃんと読まなくていいです。僕のメモになってます。

ただ、雰囲気だけでも見てるとキャッシュを保存するための必要な構造体などがなんとなく見えてきます。
:::

# .insertを調べる

まず、mosaicCacheについてです。

``` cpp
auto& mosaicCache = context.Cache.sub<cache::MosaicCache>();
```

``` cpp
const ObserverContext& context
```

contextはObserverContextの参照です

``` /symbol/client/catapult/src/catapult/observers/ObserverContext.h
public:
    /// Catapult cache.
    cache::CatapultCacheDelta& Cache;
```

CacheはCatapultCacheDelta型の参照を持つメンバ変数です

``` /symbol/client/catapult/src/catapult/cache/CatapultCacheDelta.h
/// Gets a specific sub cache delta view.
template<typename TCache>
typename TCache::CacheDeltaType& sub() {
    return *static_cast<typename TCache::CacheDeltaType*>(m_subViews[TCache::Id]->get());
}
```

sub関数はTCacheというテンプレートパラメータを持つ関数

つまりMosaicCacheをテンプレートとしているsub関数になる

続いてMosaicCacheを見る

``` /symbol/client/catapult/plugins/txes/mosaic/src/cache/MosaicCache.h
/// Cache composed of mosaic information.
using BasicMosaicCache = BasicCache<MosaicCacheDescriptor, MosaicCacheTypes::BaseSets>;

/// Synchronized cache composed of mosaic information.
class MosaicCache : public SynchronizedCache<BasicMosaicCache> {
public:
    DEFINE_CACHE_CONSTANTS(Mosaic)

public:
    /// Creates a cache around \a config.
    explicit MosaicCache(const CacheConfiguration& config) : SynchronizedCache<BasicMosaicCache>(BasicMosaicCache(config))
    {}
};
```

MosaicCacheは`public SynchronizedCache<BasicMosaicCache>`を継承している。
BasicMosaicCacheをテンプレートとしたSynchronizedCacheである。

で、BasicMosaicCacheってなんやねん
``` cpp
using BasicMosaicCache = BasicCache<MosaicCacheDescriptor, MosaicCacheTypes::BaseSets>;
```
MosaicCacheDescriptor, MosaicCacheTypes::BaseSetsをテンプレートにしたBasicCacheのインスタンスでした。

まぁBasicCacheを見るよね
``` /symbol/client/catapult/src/catapult/cache/BasicCache.h
/// Basic cache implementation that supports multiple views and committing.
/// \note Typically, TSubViewArgs will expand to zero or one types.
template<typename TCacheDescriptor, typename TBaseSet, typename... TSubViewArgs>
class BasicCache : public utils::MoveOnly {
public:
    using CacheValueType = typename TCacheDescriptor::ValueType;
    using CacheViewType = typename TCacheDescriptor::CacheViewType;
    using CacheDeltaType = typename TCacheDescriptor::CacheDeltaType;
    using CacheReadOnlyType = typename CacheViewType::ReadOnlyView;
```
CacheValueType, CacheViewType, CacheDeltaType, CacheReadOnlyTypeという型エイリアスを使うのでTCacheDescriptorはそのあたりの定義が必要なわけですね

じゃあ、MosaicCacheDescriptorはどこかと言うとここです

``` /symbol/client/catapult/plugins/txes/mosaic/src/cache/MosaicCacheTypes.h
struct MosaicCacheDescriptor {
	public:
		static constexpr auto Name = "MosaicCache";

	public:
		// key value types
		using KeyType = MosaicId;
		using ValueType = state::MosaicEntry;

		// cache types
		using CacheType = MosaicCache;
		using CacheDeltaType = MosaicCacheDelta;
		using CacheViewType = MosaicCacheView;

		using Serializer = MosaicEntryPrimarySerializer;
		using PatriciaTree = MosaicPatriciaTree;
```
ここでsub関数に戻ってみます

``` /symbol/client/catapult/src/catapult/cache/CatapultCacheDelta.h
/// Gets a specific sub cache delta view.
template<typename TCache>
typename TCache::CacheDeltaType& sub() {
    return *static_cast<typename TCache::CacheDeltaType*>(m_subViews[TCache::Id]->get());
}
```

これね、ここまで来てやっとこの関数ではMosaicCacheがテンプレートなので、そのCacheDeltaType、つまりMosaicCacheDeltaを返しているのが分かった。

``` /symbol/client/catapult/plugins/txes/mosaic/src/cache/MosaicCacheDelta.h
/// Mixins used by the mosaic cache delta.
struct MosaicCacheDeltaMixins
        : public PatriciaTreeCacheMixins<MosaicCacheTypes::PrimaryTypes::BaseSetDeltaType, MosaicCacheDescriptor> {
    using Touch = HeightBasedTouchMixin<
        typename MosaicCacheTypes::PrimaryTypes::BaseSetDeltaType,
        typename MosaicCacheTypes::HeightGroupingTypes::BaseSetDeltaType>;
};

/// Basic delta on top of the mosaic cache.
class BasicMosaicCacheDelta
        : public utils::MoveOnly
        , public MosaicCacheDeltaMixins::Size
        , public MosaicCacheDeltaMixins::Contains
        , public MosaicCacheDeltaMixins::ConstAccessor
        , public MosaicCacheDeltaMixins::MutableAccessor
        , public MosaicCacheDeltaMixins::PatriciaTreeDelta
        , public MosaicCacheDeltaMixins::ActivePredicate
        , public MosaicCacheDeltaMixins::BasicInsertRemove
        , public MosaicCacheDeltaMixins::Touch
        , public MosaicCacheDeltaMixins::DeltaElements {
public:
    using ReadOnlyView = MosaicCacheTypes::CacheReadOnlyType;

public:
    /// Creates a delta around \a mosaicSets.
    explicit BasicMosaicCacheDelta(const MosaicCacheTypes::BaseSetDeltaPointers& mosaicSets);

public:
    using MosaicCacheDeltaMixins::ConstAccessor::find;
    using MosaicCacheDeltaMixins::MutableAccessor::find;

public:
    /// Inserts the mosaic \a entry into the cache.
    void insert(const state::MosaicEntry& entry);

    /// Removes the value identified by \a mosaicId from the cache.
    void remove(MosaicId mosaicId);

private:
    MosaicCacheTypes::PrimaryTypes::BaseSetDeltaPointerType m_pEntryById;
    MosaicCacheTypes::HeightGroupingTypes::BaseSetDeltaPointerType m_pMosaicIdsByExpiryHeight;
};

/// Delta on top of the mosaic cache.
class MosaicCacheDelta : public ReadOnlyViewSupplier<BasicMosaicCacheDelta> {
public:
    /// Creates a delta around \a mosaicSets.
    explicit MosaicCacheDelta(const MosaicCacheTypes::BaseSetDeltaPointers& mosaicSets) : ReadOnlyViewSupplier(mosaicSets)
    {}
};

```
MosaicCacheDeltaってのはBasicMosaicCacheDeltaをテンプレートとしたReadOnlyViewSupplier型だそうです
そこはあんまり関係ないからReadOnlyViewSupplierは放置。

MosaicCacheDeltaを見る。
``` /symbol/client/catapult/plugins/txes/mosaic/src/cache/MosaicCacheDelta.cpp
void BasicMosaicCacheDelta::insert(const state::MosaicEntry& entry) {
    MosaicCacheDeltaMixins::BasicInsertRemove::insert(entry);
    UpdateExpiryMap(*m_pMosaicIdsByExpiryHeight, entry);
}
```
ほら、一歩進んだ感。Saveまではまだまだ遠いかも。

しかしどうも怪しいのは
``` cpp
MosaicCacheDeltaMixins::BasicInsertRemove::insert(entry);
```

ですね。
このBasicInsertRemoveを探す。

BasicMosaicCacheDeltaはpublic MosaicCacheDeltaMixins::BasicInsertRemoveを継承していました。
んでMosaicCacheDeltaMixinsってのは上にもありますが
``` cpp
/// Mixins used by the mosaic cache delta.
struct MosaicCacheDeltaMixins
        : public PatriciaTreeCacheMixins<MosaicCacheTypes::PrimaryTypes::BaseSetDeltaType, MosaicCacheDescriptor> {
    using Touch = HeightBasedTouchMixin<
        typename MosaicCacheTypes::PrimaryTypes::BaseSetDeltaType,
        typename MosaicCacheTypes::HeightGroupingTypes::BaseSetDeltaType>;
};
```
です。

つまりMosaicCacheTypes::PrimaryTypes::BaseSetDeltaType, MosaicCacheDescriptorをテンプレートとしたPatriciaTreeCacheMixinsを継承しているわけです

PatriciaTreeCacheMixinsを見るしかないでしょ

``` /symbol/client/catapult/src/catapult/cache/CacheMixinAliases.h
/// Grouping of all basic cache mixins for a single set.
template<typename TSet, typename TCacheDescriptor>
struct BasicCacheMixins {
    using Size = SizeMixin<TSet>;
    using Contains = ContainsMixin<TSet, TCacheDescriptor>;
    using Iteration = IterationMixin<TSet>;

    using ConstAccessor = ConstAccessorMixin<TSet, TCacheDescriptor>;
    using MutableAccessor = MutableAccessorMixin<TSet, TCacheDescriptor>;

    template<typename TValueAdapter>
    using ConstAccessorWithAdapter = ConstAccessorMixin<TSet, TCacheDescriptor, TValueAdapter>;

    template<typename TValueAdapter>
    using MutableAccessorWithAdapter = MutableAccessorMixin<TSet, TCacheDescriptor, TValueAdapter>;

    using ActivePredicate = ActivePredicateMixin<TSet, TCacheDescriptor>;
    using BasicInsertRemove = BasicInsertRemoveMixin<TSet, TCacheDescriptor>;

    using DeltaElements = deltaset::DeltaElementsMixin<TSet>;
};

/// Grouping of all basic and patricia tree cache mixins for a single set.
template<typename TSet, typename TCacheDescriptor>
struct PatriciaTreeCacheMixins : public BasicCacheMixins<TSet, TCacheDescriptor> {
    using PatriciaTreeView = PatriciaTreeMixin<typename TCacheDescriptor::PatriciaTree>;
    using PatriciaTreeDelta = PatriciaTreeDeltaMixin<TSet, typename TCacheDescriptor::PatriciaTree::DeltaType>;
};
```
PatriciaTreeCacheMixinsはTSet, TCacheDescriptorをテンプレートとしたBasicCacheMixinsですね（疲れてきた
でBasicCacheMixinsにはBasicInsertRemoveがあった！
これがTSet, TCacheDescriptorをテンプレとしたBasicInsertRemoveMixinであると。

``` /symbol/client/catapult/src/catapult/cache/CacheMixins.h
template<typename TSet, typename TCacheDescriptor>
class BasicInsertRemoveMixin {
private:
    using KeyType = typename TCacheDescriptor::KeyType;
    using ValueType = typename TCacheDescriptor::ValueType;

public:
    /// Creates a mixin around \a set.
    explicit BasicInsertRemoveMixin(TSet& set) : m_set(set)
    {}

public:
    /// Inserts \a value into the cache.
    void insert(const ValueType& value) {
        auto result = m_set.insert(value);
        if (deltaset::InsertResult::Inserted == result || deltaset::InsertResult::Unremoved == result)
            return;

        CATAPULT_LOG(error) << "insert failed with " << utils::to_underlying_type(result);
        detail::ThrowInvalidKeyError<TCacheDescriptor>("already", TCacheDescriptor::GetKeyFromValue(value));
    }
```
insert見つけたよ
```
auto result = m_set.insert(value);
```
また別のinsertが、、、
m_setってのはTSetなので、、、
それは確か、、、`MosaicCacheTypes::PrimaryTypes::BaseSetDeltaType`かな

``` /symbol/client/catapult/plugins/txes/mosaic/src/cache/MosaicCacheTypes.h
ublic:
	using PrimaryTypes = MutableUnorderedMapAdapter<MosaicCacheDescriptor, utils::BaseValueHasher<MosaicId>>;
```


``` /symbol/client/catapult/src/catapult/cache/CacheDescriptorAdapters.h
/// Defines cache types for an unordered mutable map based cache.
template<typename TDescriptor, typename TValueHasher = std::hash<typename TDescriptor::KeyType>>
using MutableUnorderedMapAdapter = detail::UnorderedMapAdapter<
    deltaset::MutableTypeTraits<typename TDescriptor::ValueType>,
    TDescriptor,
    TValueHasher>;
```

``` /symbol/client/catapult/src/catapult/cache/CacheDescriptorAdapters.h
/// Defines cache types for an unordered map based cache.
template<typename TElementTraits, typename TDescriptor, typename TValueHasher>
struct UnorderedMapAdapter {
...
public:
    /// Base set type.
    using BaseSetType = deltaset::BaseSet<TElementTraits, StorageTraits>;

    /// Base set delta type.
    using BaseSetDeltaType = typename BaseSetType::DeltaType;

    /// Base set delta pointer type.
    using BaseSetDeltaPointerType = std::shared_ptr<BaseSetDeltaType>;
};
```

``` /symbol/client/catapult/src/catapult/deltaset/BaseSet.h
template<
    typename TElementTraits,
    typename TSetTraits,
    typename TCommitPolicy = BaseSetCommitPolicy<TSetTraits>
>
class BaseSet : public utils::MoveOnly {
public:
    using ElementType = typename TElementTraits::ElementType;
    using SetType = typename TSetTraits::SetType;
    using KeyType = typename TSetTraits::KeyType;
    using FindTraits = FindTraitsT<ElementType, TSetTraits::AllowsNativeValueModification>;
    using DeltaType = BaseSetDelta<TElementTraits, TSetTraits>;
```
TElementTraitsとTSetTraitsはなんだっけ？

TElementTraits: deltaset::MutableTypeTraits<typename TDescriptor::ValueType>
TSetTraits:
```
// workaround for VS truncation
using MapStorageTraits = deltaset::MapStorageTraits<
    deltaset::ConditionalContainer<
        deltaset::MapKeyTraits<MemoryMapType>,
        StorageMapType,
        MemoryMapType
    >,
    Converter,
    MemoryMapType
>;

struct StorageTraits : public MapStorageTraits {};
```


``` /symbol/client/catapult/src/catapult/deltaset/BaseSetDelta.h
public:
    /// Inserts \a element into this set.
    /// \note The algorithm relies on the data used for comparing elements being immutable.
    InsertResult insert(const ElementType& element) {
        return insert(element, ElementMutabilityTag());
    }

    /// Creates an element around the passed arguments (\a args) and inserts the element into this set.
    template<typename... TArgs>
    InsertResult emplace(TArgs&&... args) {
        return insert(ElementType(std::forward<TArgs>(args)...));
    }

private:
    InsertResult insert(const ElementType& element, MutableTypeTag) {
        // copy the storage before erasing in case element is sourced from the same container being updated
        const auto& key = TSetTraits::ToKey(element);
        auto storage = TSetTraits::ToStorage(element);
        markKey(key);

        auto insertResult = InsertResult::Inserted;
        decltype(m_copiedElements)* pTargetElements;
        auto removedIter = m_removedElements.find(key);
        if (m_removedElements.cend() != removedIter) {
            m_removedElements.erase(removedIter);
            pTargetElements = Contains(m_addedElements, key) ? &m_addedElements : &m_copiedElements;
            insertResult = InsertResult::Unremoved;
        } else if (Contains(m_originalElements, key)) {
            pTargetElements = &m_copiedElements; // original element, possibly modified
            insertResult = InsertResult::Updated;
        } else {
            pTargetElements = &m_addedElements; // not an original element
            if (Contains(m_addedElements, key))
                insertResult = InsertResult::Updated;
        }

        pTargetElements->erase(key);
        pTargetElements->insert(std::move(storage));
        return insertResult;
    }
```

ま、またinsert.........

``` cpp
pTargetElements->insert(std::move(storage));
```

ここでのこれはつまり
``` cpp
using MemorySetType = typename TSetTraits::MemorySetType;
```

さぁてTSetTraitsはなんだったかと言うと
```
// workaround for VS truncation
using MapStorageTraits = deltaset::MapStorageTraits<
    deltaset::ConditionalContainer<
        deltaset::MapKeyTraits<MemoryMapType>,
        StorageMapType,
        MemoryMapType
    >,
    Converter,
    MemoryMapType
>;

struct StorageTraits : public MapStorageTraits {};
```

でした。
``` /symbol/client/catapult/src/catapult/deltaset/BaseSetDefaultTraits.h
/// Base set compatible traits for stl map types.
template<typename TMap, typename TElementToKeyConverter, typename TMemoryMapType = TMap>
struct MapStorageTraits {
    using SetType = TMap;
    using MemorySetType = TMemoryMapType;
```
今度はこれだ MemoryMapType
```
using MemoryMapType = std::unordered_map<typename TDescriptor::KeyType, typename TDescriptor::ValueType, TValueHasher>;
```

ここまで来てついに分かったのは、コンテナにキャッシュデータの作成はするが、実際にSaveするのはこのタイミングではなく、どこかまとめてsaveAll()が実行されるタイミングがあるのではないか。

プラグインとは別のコアなどこか。
`/symbol/client/catapult/src/catapult/cache/CacheChangesSerializer.h`
`/symbol/client/catapult/src/catapult/cache/CacheChangesStorageAdapter.h`
`/symbol/client/catapult/src/catapult/local/server/FileStateChangeStorage.cpp`

というわけで深掘り終わり。

分かったので追記、長いから興味ある人だけ
<details><summary>saveAllが呼ばれてる箇所</summary>

``` /home/toshi/symbol/client/catapult/src/catapult/process/broker/main.cpp
int main(int argc, const char** argv) {
	using namespace catapult;
	return process::ProcessMain(argc, argv, Process_Name, [argc, argv](auto&& config, const auto&) {
		// create bootstrapper
		OptimizeConfigurationForBroker(config);
		auto resourcesPath = process::GetResourcesPath(argc, argv).generic_string();
		auto disposition = extensions::ProcessDisposition::Production;
		auto pBootstrapper = std::make_unique<extensions::ProcessBootstrapper>(config, resourcesPath, disposition, Process_Name);

		// register extension(s)
		pBootstrapper->loadExtensions();

		// create the local node
		return local::CreateBroker(std::move(pBootstrapper));
	});
}
```
 broker起動時に`CreateBroker`を返す

``` /home/toshi/symbol/client/catapult/src/catapult/local/broker/Broker.cpp
std::unique_ptr<Broker> CreateBroker(std::unique_ptr<extensions::ProcessBootstrapper>&& pBootstrapper) {
    return CreateAndBootHost<DefaultBroker>(std::move(pBootstrapper));
}
```
CreateAndBootHostを返す
``` /home/toshi/symbol/client/catapult/src/catapult/local/HostUtils.h
/// Creates and boots a host with \a args.
template<typename THost, typename... TArgs>
std::unique_ptr<THost> CreateAndBootHost(TArgs&&... args) {
    // create and boot the host
    auto pHost = std::make_unique<THost>(std::forward<TArgs>(args)...);
    try {
        pHost->boot();
    } catch (const std::exception& ex) {
        // log the exception and rethrow as a new exception in case the exception source is a dynamic plugin
        // (this avoids a potential crash in the calling code, which would occur if the host destructor unloads the source plugin)
        CATAPULT_LOG(fatal) << UNHANDLED_EXCEPTION_MESSAGE("boot");
        CATAPULT_THROW_RUNTIME_ERROR(ex.what());
    }

    return pHost;
}
```
```
pHost->boot();
```

``` /home/toshi/symbol/client/catapult/src/catapult/local/broker/Broker.cpp
public:
    void boot() {
        CATAPULT_LOG(info) << "registering system plugins";
        m_pluginModules = LoadAllPlugins(*m_pBootstrapper);

        CATAPULT_LOG(debug) << "initializing cache";
        m_catapultCache = m_pluginManager.createCache();

        utils::StackLogger stackLogger("booting broker", utils::LogLevel::info);
        startIngestion();
    }
```

※ちなみにここで`LoadAllPlugins`も実行されてた

cacheに関しては`startIngestion();`に注目

``` /home/toshi/symbol/client/catapult/src/catapult/local/broker/Broker.cpp
void startIngestion() {
    using namespace catapult::subscribers;

    auto pServiceGroup = m_pBootstrapper->pool().pushServiceGroup("scheduler");
    auto pScheduler = pServiceGroup->pushService(thread::CreateScheduler);
    pScheduler->addTask(createIngestionTask("block_change", *m_pBlockChangeSubscriber, ReadNextBlockChange));
    pScheduler->addTask(createIngestionTask("unconfirmed_transactions_change", *m_pUtChangeSubscriber, ReadNextUtChange));
    pScheduler->addTask(createIngestionTask("partial_transactions_change", *m_pPtChangeSubscriber, ReadNextPtChange));
    pScheduler->addTask(createIngestionTask("finalization", *m_pFinalizationSubscriber, ReadNextFinalization));
    pScheduler->addTask(createIngestionTask("state_change", *m_pStateChangeSubscriber, [&catapultCache = m_catapultCache](
            auto& inputStream,
            auto& subscriber) {
        return ReadNextStateChange(inputStream, catapultCache.changesStorages(), subscriber);
    }));
    pScheduler->addTask(createIngestionTask("transaction_status", *m_pTransactionStatusSubscriber, ReadNextTransactionStatus));
}
```
ここ
```
pScheduler->addTask(createIngestionTask("state_change", *m_pStateChangeSubscriber, [&catapultCache = m_catapultCache](
						auto& inputStream,
						auto& subscriber) {
					return ReadNextStateChange(inputStream, catapultCache.changesStorages(), subscriber);
				}));
```
``` /home/toshi/symbol/client/catapult/src/catapult/subscribers/StateChangeReader.cpp
void ReadNextStateChange(
        io::InputStream& inputStream,
        const CacheChangesStorages& cacheChangesStorages,
        StateChangeSubscriber& subscriber) {
    auto operationType = static_cast<StateChangeOperationType>(io::Read8(inputStream));

    switch (operationType) {
    case StateChangeOperationType::Score_Change:
        return ReadAndNotifyScoreChange(inputStream, subscriber);
    case StateChangeOperationType::State_Change:
        return ReadAndNotifyStateChange(inputStream, cacheChangesStorages, subscriber);
    }

    CATAPULT_THROW_INVALID_ARGUMENT_1("invalid state change operation type", static_cast<uint16_t>(operationType));
}
```
```
void ReadAndNotifyStateChange(
        io::InputStream& inputStream,
        const CacheChangesStorages& cacheChangesStorages,
        StateChangeSubscriber& subscriber) {
    auto chainScoreDelta = model::ChainScore::Delta(static_cast<int64_t>(io::Read64(inputStream)));
    auto height = io::Read<Height>(inputStream);
    auto cacheChanges = ReadCacheChanges(inputStream, cacheChangesStorages);
    subscriber.notifyStateChange({ std::move(cacheChanges), chainScoreDelta, height });
}
```

``` /home/toshi/symbol/client/catapult/src/catapult/local/server/FileStateChangeStorage.cpp
void notifyStateChange(const subscribers::StateChangeInfo& stateChangeInfo) override {
    write(subscribers::StateChangeOperationType::State_Change);

    io::Write(*m_pOutputStream, stateChangeInfo.ScoreDelta);
    io::Write(*m_pOutputStream, stateChangeInfo.Height);

    for (const auto& pStorage : m_cacheChangesStoragesSupplier())
        pStorage->saveAll(stateChangeInfo.CacheChanges, *m_pOutputStream);

    m_pOutputStream->flush();
}
```
ここでsaveAllしてる
</details>

# とりあえず長いので一旦やめる

何も得ていないようにも感じますがそんなことはありません。

少なくとも分かったことはPluginでCacheを使うなら

``` cpp
namespace catapult {
	namespace cache {
		class BasicMosaicCacheDelta;
		class BasicMosaicCacheView;
		struct MosaicBaseSetDeltaPointers;
		struct MosaicBaseSets;
		class MosaicCache;
		class MosaicCacheDelta;
		class MosaicCacheView;
		struct MosaicEntryPrimarySerializer;
		struct MosaicHeightGroupingSerializer;
		class MosaicPatriciaTree;

		template<typename TCache, typename TCacheDelta, typename TCacheKey, typename TGetResult>
		class ReadOnlyArtifactCache;
	}
}
```
これだけのはclassやstructが必要ということは分かった。
それとは別でMosaicDefinition, MosaicEntry, MosaicEntrySerializerが必要みたいだった

これはファイルにすると

```
/plugin
│
├── src
│   ├── cache
│   │   ├── MosaicBaseSets.h
│   │   ├── MosaicCache.h
│   │   ├── MosaicCacheDelta.cpp
│   │   ├── MosaicCacheDelta.h
│   │   ├── MosaicCacheSerializers.h
│   │   ├── MosaicCacheStorage.h
│   │   ├── MosaicCacheTypes.h
│   │   └── MosaicCacheView.h
│   ├── ...
│   ├── state
│   │   ├── MosaicDefinition.cpp
│   │   ├── MosaicDefinition.h
│   │   ├── MosaicEntry.cpp
│   │   ├── MosaicEntry.h
│   │   ├── MosaicEntrySerializer.cpp
│   │   └── MosaicEntrySerializer.h
│   └── ...
...
```

こんな感じだった。これはプラグインと1対1ではなく残したいキャッシュに対して1対1になると思う。
また、MosaicDefinitionに関してはMosaicEntryの引数の一部なので残したい物によって変わりそう。（不要な場合もあるかも）

今回はここでやめておいて、次回（あるか分からんけど）はそれぞれのファイルの中身について掘り下げたい。その時に今回の調査は役に立ちそう。何よりテンプレートという概念について理解できた。C#のジェネリックのようなちょっと違うような。