Files
2025-09-18 17:55:52 -04:00

174 lines
8.0 KiB
C++

#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 <deque>
namespace RBX {
extern const char* const sGlobalDataStore;
class DataStore
:public DescribedNonCreatable<DataStore, Instance, sGlobalDataStore, Reflection::ClassDescriptor::RUNTIME_LOCAL>
{
typedef DescribedNonCreatable<DataStore, Instance, sGlobalDataStore, Reflection::ClassDescriptor::RUNTIME_LOCAL> 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<std::string, CachedRecord> CachedKeys;
CachedKeys cachedKeys;
typedef std::map<std::string, shared_ptr<rbx::signal<void(Reflection::Variant)> > > OnUpdateKeys;
OnUpdateKeys onUpdateKeys;
typedef std::map<std::string,Time> 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<void()> resumeFunction, boost::function<void(std::string)> errorFunction);
void processFetchSingleKey(std::string* response, std::exception* exception, std::string key, bool expectSubKey, boost::function<void()> callback, boost::function<void(std::string)> errorFunction);
void lockAcquiredProcessFetchSingleKey(shared_ptr<std::string> response, shared_ptr<std::exception> exception, std::string key, bool expectSubKey, boost::function<void()> callback, boost::function<void(std::string)> errorFunction);
bool updateCachedKey(const std::string& key, const Reflection::Variant& value);
void createFetchNewKeyRequest(const std::string& key, boost::function<void()> callback, boost::function<void(std::string)> 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<Lua::WeakFunctionRef> transform, std::string* response, std::exception* exception, boost::function<void(shared_ptr<const Reflection::Tuple>)> resumeFunction, boost::function<void(std::string)> errorFunction);
void lockAcquiredProcessSetIf(std::string key, shared_ptr<Lua::WeakFunctionRef> transform, shared_ptr<std::string> response, shared_ptr<std::exception> exception, boost::function<void(shared_ptr<const Reflection::Tuple>)> resumeFunction, boost::function<void(std::string)> errorFunction);
void runTransformFunction(std::string key, const shared_ptr<Lua::WeakFunctionRef> transform,
boost::function<void(shared_ptr<const Reflection::Tuple>)> resumeFunction,
boost::function<void(std::string)> errorFunction);
void processFetchCachedKeys(std::string* response, std::exception* exception);
void lockAcquiredProcessFetchCachedKeys(shared_ptr<std::string> response, shared_ptr<std::exception> exception);
bool checkAccess(const std::string& key, boost::function<void(std::string)>* errorFunction);
bool checkStudioApiAccess(boost::function<void(std::string)> 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<void(Reflection::Variant)> resumeFunction, boost::function<void(std::string)> errorFunction);
void updateAsync(std::string key, Lua::WeakFunctionRef transformFunc, boost::function<void(shared_ptr<const Reflection::Tuple>)> resumeFunction, boost::function<void(std::string)> errorFunction);
void setAsync(std::string key, Reflection::Variant value, boost::function<void()> resumeFunction, boost::function<void(std::string)> errorFunction);
void incrementAsync(std::string key, int delta, boost::function<void(Reflection::Variant)> resumeFunction, boost::function<void(std::string)> 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<void(Reflection::Variant)> resumeFunction);
};
extern const char* const sOrderedDataStore;
class OrderedDataStore :
public DescribedNonCreatable<OrderedDataStore, DataStore, sOrderedDataStore, Reflection::ClassDescriptor::RUNTIME_LOCAL>
{
typedef DescribedNonCreatable<DataStore, DataStore, sOrderedDataStore, Reflection::ClassDescriptor::RUNTIME_LOCAL> 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<void(shared_ptr<Instance>) > resumeFunction, boost::function<void(std::string)> errorFunction);
};
extern const char* const sDataStorePages;
class DataStorePages :
public DescribedNonCreatable<DataStorePages, Pages, sDataStorePages, Reflection::ClassDescriptor::RUNTIME_LOCAL>
{
weak_ptr<OrderedDataStore> ds;
std::string requestUrl;
std::string exclusiveStartKey;
void processFetch(std::string* response, std::exception* exception, boost::function<void()> resumeFunction, boost::function<void(std::string)> errorFunction);
void lockAcquiredProcessFetch(shared_ptr<std::string> response, shared_ptr<std::exception> exception, boost::function<void()> resumeFunction, boost::function<void(std::string)> errorFunction);
public:
DataStorePages(weak_ptr<OrderedDataStore> ds, const std::string& requestUrl);
void fetchNextChunk(boost::function<void()> resumeFunction, boost::function<void(std::string)> errorFunction);
};
}