#pragma once #include "Reflection/Reflection.h" #include "script/ThreadRef.h" #include "V8Tree/Instance.h" #include "rbx/RunningAverage.h" #include "v8datamodel/DataStoreService.h" #include "util/LuaWebService.h" #include "signal.h" #include namespace RBX { extern const char* const sGlobalDataStore; class DataStore :public DescribedNonCreatable { typedef DescribedNonCreatable Super; class CachedRecord { private: Reflection::Variant variant; std::string serialized; Time accessTimeStamp; public: CachedRecord(){}; const Reflection::Variant& getVariant(bool touch = true); const std::string& getSerialized() { return serialized; } const Time& getTime() { return accessTimeStamp; } void update(const Reflection::Variant& variant, const std::string& serialized); }; struct EventSlot { Lua::WeakFunctionRef callback; EventSlot(Lua::WeakFunctionRef); void fire(Reflection::Variant value); }; bool isLegacy; typedef std::map CachedKeys; CachedKeys cachedKeys; typedef std::map > > OnUpdateKeys; OnUpdateKeys onUpdateKeys; typedef std::map KeyTimestamps; KeyTimestamps lastSetByKey; enum RefetchState { RefetchOnUpdateKeys, RefetchCachedKeys, RefetchDone }; RefetchState refetchState; std::string nextKeyToRefetch; bool backendProcessing; std::string constructPostDataForKey(const std::string& key, unsigned index = 0); std::string constructGetUrl(); std::string constructSetUrl(const std::string& key, unsigned valueLength); std::string constructSetIfUrl(const std::string& key, unsigned valueLength, unsigned expectedValueLength); std::string constructIncrementUrl(const std::string& key, int delta); void processSet(std::string key, std::string* response, std::exception* exception, boost::function resumeFunction, boost::function errorFunction); void processFetchSingleKey(std::string* response, std::exception* exception, std::string key, bool expectSubKey, boost::function callback, boost::function errorFunction); void lockAcquiredProcessFetchSingleKey(shared_ptr response, shared_ptr exception, std::string key, bool expectSubKey, boost::function callback, boost::function errorFunction); bool updateCachedKey(const std::string& key, const Reflection::Variant& value); void createFetchNewKeyRequest(const std::string& key, boost::function callback, boost::function errorFunction, DataStoreService::HttpRequest& request); // Has to be const reference to Lua::WeakFunctionRef so boost::bind won't copy it to pass by value // Lua::WeakFunctionRef needs access to Lua state to copy, so can't be done safely in http threadpool void processSetIf(std::string key, shared_ptr transform, std::string* response, std::exception* exception, boost::function)> resumeFunction, boost::function errorFunction); void lockAcquiredProcessSetIf(std::string key, shared_ptr transform, shared_ptr response, shared_ptr exception, boost::function)> resumeFunction, boost::function errorFunction); void runTransformFunction(std::string key, const shared_ptr transform, boost::function)> resumeFunction, boost::function errorFunction); void processFetchCachedKeys(std::string* response, std::exception* exception); void lockAcquiredProcessFetchCachedKeys(shared_ptr response, shared_ptr exception); bool checkAccess(const std::string& key, boost::function* errorFunction); bool checkStudioApiAccess(boost::function errorFunction); static std::string serializeVariant(const Reflection::Variant& variant, bool* hasNonJsonType); static bool deserializeVariant(const std::string& webValue, Reflection::Variant& result); void sendBatchGet(std::stringstream& keysList); void accumulateKeyToFetch(const std::string& key, std::stringstream& keysList, int& counter); protected: std::string serviceUrl; std::string name, scope; std::string scopeUrlEncodedIfNeeded, nameUrlEncodedIfNeeded; virtual bool checkValueIsAllowed(const Reflection::Variant&) { return true; }; virtual const char* getDataStoreTypeString() { return "standard"; } virtual bool queueOrExecuteSet(DataStoreService::HttpRequest& request); std::string urlEncodeIfNeeded(const std::string& input); public: DataStore(const std::string& name, const std::string& scope, bool legacy); static const char* urlApiPath() { return "persistence"; } void getAsync(std::string key, boost::function resumeFunction, boost::function errorFunction); void updateAsync(std::string key, Lua::WeakFunctionRef transformFunc, boost::function)> resumeFunction, boost::function errorFunction); void setAsync(std::string key, Reflection::Variant value, boost::function resumeFunction, boost::function errorFunction); void incrementAsync(std::string key, int delta, boost::function resumeFunction, boost::function errorFunction); rbx::signals::connection onUpdate(std::string key, Lua::WeakFunctionRef callback); /*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider); void refetchCachedKeys(int* budget); void resetRefetch(); bool isKeyThrottled(const std::string& key, Time timestamp); void setKeySetTimestamp(const std::string& key, Time timestamp); DataStoreService* getParentDataStoreService(); private: void runResumeFunction(std::string key, boost::function resumeFunction); }; extern const char* const sOrderedDataStore; class OrderedDataStore : public DescribedNonCreatable { typedef DescribedNonCreatable Super; std::string constructGetSortedUrl(bool isAscending, int pagesize, const double* minValue, const double* maxValue); protected: virtual bool checkValueIsAllowed(const Reflection::Variant& v); virtual const char* getDataStoreTypeString() { return "sorted"; } virtual bool queueOrExecuteSet(DataStoreService::HttpRequest& request); public: OrderedDataStore(const std::string& name, const std::string& scope); void getSortedAsync(bool isAscending, int pagesize, Reflection::Variant minValue, Reflection::Variant maxValue, boost::function) > resumeFunction, boost::function errorFunction); }; extern const char* const sDataStorePages; class DataStorePages : public DescribedNonCreatable { weak_ptr ds; std::string requestUrl; std::string exclusiveStartKey; void processFetch(std::string* response, std::exception* exception, boost::function resumeFunction, boost::function errorFunction); void lockAcquiredProcessFetch(shared_ptr response, shared_ptr exception, boost::function resumeFunction, boost::function errorFunction); public: DataStorePages(weak_ptr ds, const std::string& requestUrl); void fetchNextChunk(boost::function resumeFunction, boost::function errorFunction); }; }