CatapultのCacheについて調べる。これはとても重要。
ブロックデータではなくCacheに保存しておくことで、それをバリデータやオブザーバで簡単に呼び出すことが出来る。おそらく基本的には各Pluginをまたがずに保存すべき。つまりmosaicプラグインならその範囲内でCacheを保存し、その範囲内で呼び出す。(一部例外はあるらしい)
今回はmosaic definitison transactionに関して、調べてみようと思う。
Pliuginを見る
まず、プラグイン本体である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つの通知を飛ばしてます。
通知に関してはここを見ます。
/// 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を見る
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));
}
})
色々書かれているけどポイントはここでしょう。
mosaicCache.insert(state::MosaicEntry(notification.MosaicId, definition));
mosaicCache.remove(notification.MosaicId);
どうやらキャッシュに追加するにはinsertで削除するのはkeyを引数にしたremoveですね。
要するにここで保存してるんだけど、具体的な中身が気になったので掘り下げてみます。
これをスタートした時点で見つけたのはここ
void MosaicEntrySerializer::Save(const MosaicEntry& entry, io::OutputStream& output) {
io::Write(output, entry.mosaicId());
io::Write(output, entry.supply());
SaveDefinition(output, entry.definition());
}
最終的にここにたどり着くはず。だけど過程を知りたくなってしまった。
結論から言うとたどり着かない。推測の範疇になるが、各プラグインのオブザーバーではコンテナにinsertするまでを行い、どこかのタイミングでまとめてsaveしているような感じがした。
ここからそんなにちゃんと読まなくていいです。僕のメモになってます。
ただ、雰囲気だけでも見てるとキャッシュを保存するための必要な構造体などがなんとなく見えてきます。
.insertを調べる
まず、mosaicCacheについてです。
auto& mosaicCache = context.Cache.sub<cache::MosaicCache>();
const ObserverContext& context
contextはObserverContextの参照です
public:
/// Catapult cache.
cache::CatapultCacheDelta& Cache;
CacheはCatapultCacheDelta型の参照を持つメンバ変数です
/// 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を見る
/// 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ってなんやねん
using BasicMosaicCache = BasicCache<MosaicCacheDescriptor, MosaicCacheTypes::BaseSets>;
MosaicCacheDescriptor, MosaicCacheTypes::BaseSetsをテンプレートにしたBasicCacheのインスタンスでした。
まぁBasicCacheを見るよね
/// 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はどこかと言うとここです
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関数に戻ってみます
/// 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を返しているのが分かった。
/// 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を見る。
void BasicMosaicCacheDelta::insert(const state::MosaicEntry& entry) {
MosaicCacheDeltaMixins::BasicInsertRemove::insert(entry);
UpdateExpiryMap(*m_pMosaicIdsByExpiryHeight, entry);
}
ほら、一歩進んだ感。Saveまではまだまだ遠いかも。
しかしどうも怪しいのは
MosaicCacheDeltaMixins::BasicInsertRemove::insert(entry);
ですね。
このBasicInsertRemoveを探す。
BasicMosaicCacheDeltaはpublic MosaicCacheDeltaMixins::BasicInsertRemoveを継承していました。
んでMosaicCacheDeltaMixinsってのは上にもありますが
/// 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を見るしかないでしょ
/// 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であると。
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かな
ublic:
using PrimaryTypes = MutableUnorderedMapAdapter<MosaicCacheDescriptor, utils::BaseValueHasher<MosaicId>>;
/// 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>;
/// 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>;
};
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
TSetTraits:
// workaround for VS truncation
using MapStorageTraits = deltaset::MapStorageTraits<
deltaset::ConditionalContainer<
deltaset::MapKeyTraits<MemoryMapType>,
StorageMapType,
MemoryMapType
>,
Converter,
MemoryMapType
>;
struct StorageTraits : public MapStorageTraits {};
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.........
pTargetElements->insert(std::move(storage));
ここでのこれはつまり
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 {};
でした。
/// 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
というわけで深掘り終わり。
分かったので追記、長いから興味ある人だけ
saveAllが呼ばれてる箇所
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を返す
std::unique_ptr<Broker> CreateBroker(std::unique_ptr<extensions::ProcessBootstrapper>&& pBootstrapper) {
return CreateAndBootHost<DefaultBroker>(std::move(pBootstrapper));
}
CreateAndBootHostを返す
/// 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();
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();に注目
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);
}));
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 });
}
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してる
とりあえず長いので一旦やめる
何も得ていないようにも感じますがそんなことはありません。
少なくとも分かったことはPluginでCacheを使うなら
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#のジェネリックのようなちょっと違うような。