2020-06-23 18:40:00 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
2022-01-11 11:57:38 -05:00
2022-06-10 17:21:00 -04:00
# include "DerivedDataBackendInterface.h"
2022-05-31 23:25:37 -04:00
# include "DerivedDataLegacyCacheStore.h"
2022-06-02 11:06:47 -04:00
# include "Templates/Tuple.h"
2022-05-31 23:25:37 -04:00
2020-06-23 18:40:00 -04:00
# if WITH_HTTP_DDC_BACKEND
2022-01-11 11:57:38 -05:00
# include "Compression/CompressedBuffer.h"
# include "Containers/StringView.h"
2020-06-23 18:40:00 -04:00
# include "Containers/Ticker.h"
2022-01-11 11:57:38 -05:00
# include "DerivedDataCacheKey.h"
2021-04-28 16:22:18 -04:00
# include "DerivedDataCacheRecord.h"
2022-01-11 11:57:38 -05:00
# include "DerivedDataCacheUsageStats.h"
2022-01-14 10:53:17 -05:00
# include "DerivedDataChunk.h"
2022-04-05 16:43:41 -04:00
# include "DerivedDataRequestOwner.h"
2022-01-10 13:43:40 -05:00
# include "DerivedDataValue.h"
2022-07-06 05:59:22 -04:00
# include "DesktopPlatformModule.h"
2020-06-23 18:40:00 -04:00
# include "Dom/JsonObject.h"
2022-04-05 16:43:41 -04:00
# include "HAL/IConsoleManager.h"
2022-05-31 22:01:53 -04:00
# include "Http/HttpClient.h"
2021-02-22 11:11:09 -04:00
# include "IO/IoHash.h"
2022-01-06 11:05:57 -05:00
# include "Memory/SharedBuffer.h"
2022-05-31 22:01:53 -04:00
# include "Misc/App.h"
# include "Misc/CommandLine.h"
# include "Misc/ConfigCacheIni.h"
2020-06-23 18:40:00 -04:00
# include "Misc/FileHelper.h"
# include "Misc/ScopeLock.h"
2021-01-11 11:34:27 -04:00
# include "Misc/StringBuilder.h"
2020-06-23 18:40:00 -04:00
# include "ProfilingDebugging/CountersTrace.h"
2022-01-11 11:57:38 -05:00
# include "ProfilingDebugging/CpuProfilerTrace.h"
2022-01-10 13:43:40 -05:00
# include "Serialization/CompactBinary.h"
# include "Serialization/CompactBinaryPackage.h"
# include "Serialization/CompactBinaryValidation.h"
2022-02-02 07:35:19 -05:00
# include "Serialization/CompactBinaryWriter.h"
2022-05-18 09:58:59 -04:00
2022-05-27 07:04:22 -04:00
# if PLATFORM_MICROSOFT
# include "Microsoft/WindowsHWrapper.h"
# include "Microsoft/AllowMicrosoftPlatformTypes.h"
2022-05-26 18:07:41 -04:00
# include <winsock2.h>
# include <ws2tcpip.h>
2022-05-27 07:04:22 -04:00
# include "Microsoft/HideMicrosoftPlatformTypes.h"
2022-05-26 18:07:41 -04:00
# else
# include <sys/types.h>
# include <sys/socket.h>
# include <netdb.h>
2021-04-28 16:22:18 -04:00
# endif
2022-02-24 10:24:10 -05:00
# define UE_HTTPDDC_GET_REQUEST_POOL_SIZE 48
# define UE_HTTPDDC_PUT_REQUEST_POOL_SIZE 16
2022-04-05 16:43:41 -04:00
# define UE_HTTPDDC_NONBLOCKING_REQUEST_POOL_SIZE 128
2020-06-23 18:40:00 -04:00
# define UE_HTTPDDC_MAX_FAILED_LOGIN_ATTEMPTS 16
# define UE_HTTPDDC_MAX_ATTEMPTS 4
2022-02-14 14:43:39 -05:00
namespace UE : : DerivedData
2021-04-28 16:22:18 -04:00
{
2022-07-06 05:59:22 -04:00
static bool bHttpEnableOidc = false ;
static FAutoConsoleVariableRef CVarHttpEnableOidc (
TEXT ( " DDC.Http.EnableOidc " ) ,
bHttpEnableOidc ,
TEXT ( " If true, Oidc tokens are used, otherwise legacy shared credentials are used. " ) ,
ECVF_Default ) ;
2020-06-23 18:40:00 -04:00
TRACE_DECLARE_INT_COUNTER ( HttpDDC_Get , TEXT ( " HttpDDC Get " ) ) ;
TRACE_DECLARE_INT_COUNTER ( HttpDDC_GetHit , TEXT ( " HttpDDC Get Hit " ) ) ;
TRACE_DECLARE_INT_COUNTER ( HttpDDC_Put , TEXT ( " HttpDDC Put " ) ) ;
TRACE_DECLARE_INT_COUNTER ( HttpDDC_PutHit , TEXT ( " HttpDDC Put Hit " ) ) ;
TRACE_DECLARE_INT_COUNTER ( HttpDDC_BytesReceived , TEXT ( " HttpDDC Bytes Received " ) ) ;
TRACE_DECLARE_INT_COUNTER ( HttpDDC_BytesSent , TEXT ( " HttpDDC Bytes Sent " ) ) ;
2022-02-24 10:24:10 -05:00
2022-04-05 16:43:41 -04:00
template < typename T >
class TRefCountedUniqueFunction final : public FThreadSafeRefCountedObject
{
public :
explicit TRefCountedUniqueFunction ( T & & InFunction ) : Function ( MoveTemp ( InFunction ) )
{
}
const T & GetFunction ( ) const { return Function ; }
private :
T Function ;
} ;
2021-03-04 09:35:03 -04:00
2022-05-31 22:01:53 -04:00
static bool ShouldAbortForShutdown ( )
2021-04-05 14:36:58 -04:00
{
return ! GIsBuildMachine & & FDerivedDataBackend : : Get ( ) . IsShuttingDown ( ) ;
}
2022-02-02 07:35:19 -05:00
static bool IsValueDataReady ( FValue & Value , const ECachePolicy Policy )
{
if ( ! EnumHasAnyFlags ( Policy , ECachePolicy : : Query ) )
{
Value = Value . RemoveData ( ) ;
return true ;
}
if ( Value . HasData ( ) )
{
if ( EnumHasAnyFlags ( Policy , ECachePolicy : : SkipData ) )
{
Value = Value . RemoveData ( ) ;
}
return true ;
}
return false ;
} ;
2022-05-31 22:01:53 -04:00
struct FHttpCacheStoreParams
{
FString Host ;
FString Namespace ;
FString StructuredNamespace ;
FString OAuthProvider ;
FString OAuthClientId ;
FString OAuthSecret ;
FString OAuthScope ;
2022-07-06 05:59:22 -04:00
FString OAuthProviderIdentifier ;
FString OAuthAccessToken ;
2022-06-06 13:33:27 -04:00
bool bResolveHostCanonicalName = true ;
2022-05-31 22:01:53 -04:00
bool bReadOnly = false ;
void Parse ( const TCHAR * NodeName , const TCHAR * Config ) ;
} ;
2020-06-23 18:40:00 -04:00
//----------------------------------------------------------------------------------------------------------
2022-02-14 14:43:39 -05:00
// FHttpCacheStore
2020-06-23 18:40:00 -04:00
//----------------------------------------------------------------------------------------------------------
2022-01-11 11:57:38 -05:00
/**
* Backend for a HTTP based caching service ( Jupiter ) .
2022-05-31 22:01:53 -04:00
*/
class FHttpCacheStore final : public ILegacyCacheStore
2022-01-11 11:57:38 -05:00
{
public :
/**
2022-05-31 22:01:53 -04:00
* Creates the cache store client , checks health status and attempts to acquire an access token .
2022-01-11 11:57:38 -05:00
*/
2022-05-31 22:01:53 -04:00
explicit FHttpCacheStore ( const FHttpCacheStoreParams & Params ) ;
2022-01-11 11:57:38 -05:00
2022-02-14 14:43:39 -05:00
~ FHttpCacheStore ( ) ;
2022-01-11 11:57:38 -05:00
/**
2022-05-31 22:01:53 -04:00
* Checks is cache service is usable ( reachable and accessible ) .
2022-01-11 11:57:38 -05:00
* @ return true if usable
*/
2022-05-31 22:01:53 -04:00
inline bool IsUsable ( ) const { return bIsUsable ; }
2022-01-11 11:57:38 -05:00
2022-05-31 22:01:53 -04:00
void Put (
2022-01-11 11:57:38 -05:00
TConstArrayView < FCachePutRequest > Requests ,
IRequestOwner & Owner ,
2022-05-31 22:01:53 -04:00
FOnCachePutComplete & & OnComplete ) final ;
void Get (
2022-01-11 11:57:38 -05:00
TConstArrayView < FCacheGetRequest > Requests ,
IRequestOwner & Owner ,
2022-05-31 22:01:53 -04:00
FOnCacheGetComplete & & OnComplete ) final ;
void PutValue (
2022-02-02 07:35:19 -05:00
TConstArrayView < FCachePutValueRequest > Requests ,
IRequestOwner & Owner ,
2022-05-31 22:01:53 -04:00
FOnCachePutValueComplete & & OnComplete ) final ;
void GetValue (
2022-02-02 07:35:19 -05:00
TConstArrayView < FCacheGetValueRequest > Requests ,
IRequestOwner & Owner ,
2022-05-31 22:01:53 -04:00
FOnCacheGetValueComplete & & OnComplete ) final ;
void GetChunks (
2022-01-18 04:47:59 -05:00
TConstArrayView < FCacheGetChunkRequest > Requests ,
2022-01-11 11:57:38 -05:00
IRequestOwner & Owner ,
2022-05-31 22:01:53 -04:00
FOnCacheGetChunkComplete & & OnComplete ) final ;
void LegacyStats ( FDerivedDataCacheStatsNode & OutNode ) final ;
bool LegacyDebugOptions ( FBackendDebugOptions & Options ) final ;
2022-01-11 11:57:38 -05:00
2022-02-14 14:43:39 -05:00
static FHttpCacheStore * GetAny ( )
2022-01-11 11:57:38 -05:00
{
return AnyInstance ;
}
const FString & GetDomain ( ) const { return Domain ; }
const FString & GetNamespace ( ) const { return Namespace ; }
const FString & GetStructuredNamespace ( ) const { return StructuredNamespace ; }
const FString & GetOAuthProvider ( ) const { return OAuthProvider ; }
const FString & GetOAuthClientId ( ) const { return OAuthClientId ; }
const FString & GetOAuthSecret ( ) const { return OAuthSecret ; }
2022-03-23 18:05:53 -04:00
const FString & GetOAuthScope ( ) const { return OAuthScope ; }
2022-07-06 05:59:22 -04:00
const FString & GetOAuthProviderIdentifier ( ) const { return OAuthProviderIdentifier ; }
const FString & GetOAuthAccessToken ( ) const { return OAuthAccessToken ; }
2022-01-11 11:57:38 -05:00
private :
FString Domain ;
2022-04-05 16:43:41 -04:00
FString EffectiveDomain ;
2022-01-11 11:57:38 -05:00
FString Namespace ;
FString StructuredNamespace ;
FString OAuthProvider ;
FString OAuthClientId ;
FString OAuthSecret ;
2022-03-23 18:05:53 -04:00
FString OAuthScope ;
2022-07-06 05:59:22 -04:00
FString OAuthProviderIdentifier ;
FString OAuthAccessToken ;
2022-01-11 11:57:38 -05:00
FDerivedDataCacheUsageStats UsageStats ;
FBackendDebugOptions DebugOptions ;
2022-05-11 09:10:24 -04:00
TUniquePtr < FHttpSharedData > SharedData ;
2022-05-18 09:58:59 -04:00
TUniquePtr < FHttpRequestPool > GetRequestPools [ 2 ] ;
TUniquePtr < FHttpRequestPool > PutRequestPools [ 2 ] ;
TUniquePtr < FHttpRequestPool > NonBlockingRequestPools ;
2022-07-12 15:36:38 -04:00
FCriticalSection AccessCs ;
2022-05-11 09:10:24 -04:00
TUniquePtr < FHttpAccessToken > Access ;
2022-07-12 15:36:38 -04:00
FTSTicker : : FDelegateHandle RefreshAccessTokenHandle ;
uint32 FailedLoginAttempts = 0 ;
bool bIsUsable = false ;
bool bReadOnly = false ;
2022-02-14 14:43:39 -05:00
static inline FHttpCacheStore * AnyInstance = nullptr ;
2022-01-11 11:57:38 -05:00
bool IsServiceReady ( ) ;
bool AcquireAccessToken ( ) ;
2022-07-12 15:36:38 -04:00
void SetAccessToken ( FStringView Token , double RefreshDelay = 0.0 ) ;
2022-05-31 12:42:17 -04:00
bool ShouldRetryOnError ( FHttpRequest : : EResult Result , int64 ResponseCode ) ;
bool ShouldRetryOnError ( int64 ResponseCode ) { return ShouldRetryOnError ( FHttpRequest : : EResult : : Success , ResponseCode ) ; }
2022-01-11 11:57:38 -05:00
2022-04-05 16:43:41 -04:00
enum class OperationCategory
{
Get ,
2022-06-29 21:31:09 -04:00
Put ,
2022-04-05 16:43:41 -04:00
} ;
2022-06-29 21:31:09 -04:00
template < OperationCategory Category >
2022-05-18 09:58:59 -04:00
FHttpRequest * WaitForHttpRequestForOwner ( IRequestOwner & Owner , bool bUnboundedOverflow , FHttpRequestPool * & OutPool )
2022-04-05 16:43:41 -04:00
{
if ( ! FHttpRequest : : AllowAsync ( ) )
{
if ( Category = = OperationCategory : : Get )
{
OutPool = GetRequestPools [ IsInGameThread ( ) ] . Get ( ) ;
}
else
{
OutPool = PutRequestPools [ IsInGameThread ( ) ] . Get ( ) ;
}
return OutPool - > WaitForFreeRequest ( ) ;
}
else
{
OutPool = NonBlockingRequestPools . Get ( ) ;
return OutPool - > WaitForFreeRequest ( bUnboundedOverflow ) ;
}
}
2022-01-11 11:57:38 -05:00
2022-04-05 16:43:41 -04:00
struct FGetCacheRecordOnlyResponse
{
FSharedString Name ;
FCacheKey Key ;
uint64 UserData = 0 ;
uint64 BytesReceived = 0 ;
FOptionalCacheRecord Record ;
EStatus Status = EStatus : : Error ;
} ;
using FOnGetCacheRecordOnlyComplete = TUniqueFunction < void ( FGetCacheRecordOnlyResponse & & Response ) > ;
void GetCacheRecordOnlyAsync (
IRequestOwner & Owner ,
const FSharedString & Name ,
2022-01-11 11:57:38 -05:00
const FCacheKey & Key ,
const FCacheRecordPolicy & Policy ,
2022-04-05 16:43:41 -04:00
uint64 UserData ,
FOnGetCacheRecordOnlyComplete & & OnComplete ) ;
2022-02-02 07:35:19 -05:00
2022-04-05 16:43:41 -04:00
void GetCacheRecordAsync (
IRequestOwner & Owner ,
const FSharedString & Name ,
const FCacheKey & Key ,
const FCacheRecordPolicy & Policy ,
uint64 UserData ,
TUniqueFunction < void ( FCacheGetResponse & & Response , uint64 BytesReceived ) > & & OnComplete ) ;
void PutCacheRecordAsync (
IRequestOwner & Owner ,
const FSharedString & Name ,
const FCacheRecord & Record ,
const FCacheRecordPolicy & Policy ,
uint64 UserData ,
TUniqueFunction < void ( FCachePutResponse & & Response , uint64 BytesSent ) > & & OnComplete ) ;
void PutCacheValueAsync (
IRequestOwner & Owner ,
const FSharedString & Name ,
2022-02-02 07:35:19 -05:00
const FCacheKey & Key ,
const FValue & Value ,
ECachePolicy Policy ,
2022-04-05 16:43:41 -04:00
uint64 UserData ,
TUniqueFunction < void ( FCachePutValueResponse & & Response , uint64 BytesSent ) > & & OnComplete ) ;
2022-02-02 07:35:19 -05:00
2022-04-05 16:43:41 -04:00
void GetCacheValueAsync (
IRequestOwner & Owner ,
FSharedString Name ,
2022-02-02 07:35:19 -05:00
const FCacheKey & Key ,
ECachePolicy Policy ,
2022-04-05 16:43:41 -04:00
uint64 UserData ,
FOnCacheGetValueComplete & & OnComplete ) ;
2022-02-02 07:35:19 -05:00
2022-04-05 16:43:41 -04:00
void RefCachedDataProbablyExistsBatchAsync (
IRequestOwner & Owner ,
TConstArrayView < FCacheGetValueRequest > ValueRefs ,
FOnCacheGetValueComplete & & OnComplete ) ;
class FPutPackageOp ;
class FGetRecordOp ;
2022-04-05 14:18:39 -04:00
} ;
2022-04-05 16:43:41 -04:00
//----------------------------------------------------------------------------------------------------------
// FHttpCacheStore::FPutPackageOp
//----------------------------------------------------------------------------------------------------------
class FHttpCacheStore : : FPutPackageOp final : public FThreadSafeRefCountedObject
{
public :
struct FCachePutPackageResponse
{
FSharedString Name ;
FCacheKey Key ;
uint64 UserData = 0 ;
uint64 BytesSent = 0 ;
EStatus Status = EStatus : : Error ;
} ;
using FOnCachePutPackageComplete = TUniqueFunction < void ( FCachePutPackageResponse & & Response ) > ;
/** Performs a multi-request operation for uploading a package of content. */
static void PutPackage (
FHttpCacheStore & CacheStore ,
IRequestOwner & Owner ,
const FSharedString & Name ,
FCacheKey Key ,
FCbPackage & & Package ,
FCacheRecordPolicy Policy ,
uint64 UserData ,
FOnCachePutPackageComplete & & OnComplete ) ;
private :
FHttpCacheStore & CacheStore ;
IRequestOwner & Owner ;
const FSharedString Name ;
const FCacheKey Key ;
const uint64 UserData ;
std : : atomic < uint64 > BytesSent ;
const FCbObject PackageObject ;
const FIoHash PackageObjectHash ;
const uint32 TotalBlobUploads ;
std : : atomic < uint32 > SuccessfulBlobUploads ;
std : : atomic < uint32 > PendingBlobUploads ;
FOnCachePutPackageComplete OnComplete ;
struct FCachePutRefResponse
{
FSharedString Name ;
FCacheKey Key ;
uint64 UserData = 0 ;
uint64 BytesSent = 0 ;
TConstArrayView < FIoHash > NeededBlobHashes ;
EStatus Status = EStatus : : Error ;
} ;
using FOnCachePutRefComplete = TUniqueFunction < void ( FCachePutRefResponse & & Response ) > ;
FPutPackageOp (
FHttpCacheStore & InCacheStore ,
IRequestOwner & InOwner ,
const FSharedString & InName ,
const FCacheKey & InKey ,
uint64 InUserData ,
uint64 InBytesSent ,
const FCbObject & InPackageObject ,
const FIoHash & InPackageObjectHash ,
uint32 InTotalBlobUploads ,
FOnCachePutPackageComplete & & InOnComplete ) ;
static void PutRefAsync (
FHttpCacheStore & CacheStore ,
IRequestOwner & Owner ,
const FSharedString & Name ,
FCacheKey Key ,
FCbObject Object ,
FIoHash ObjectHash ,
uint64 UserData ,
bool bFinalize ,
FOnCachePutRefComplete & & OnComplete ) ;
static void OnPackagePutRefComplete (
FHttpCacheStore & CacheStore ,
IRequestOwner & Owner ,
const FSharedString & Name ,
const FCacheKey & Key ,
FCbPackage & & Package ,
FCacheRecordPolicy Policy ,
uint64 UserData ,
FOnCachePutPackageComplete & & OnComplete ,
FCachePutRefResponse & & Response ) ;
FHttpRequest : : ECompletionBehavior OnCompressedBlobUploadComplete (
2022-05-31 12:42:17 -04:00
FHttpRequest : : EResult HttpResult ,
2022-04-05 16:43:41 -04:00
FHttpRequest * Request ) ;
void OnPutRefFinalizationComplete (
FCachePutRefResponse & & Response ) ;
FCachePutPackageResponse MakeResponse ( uint64 InBytesSent , EStatus Status )
{
return FCachePutPackageResponse { Name , Key , UserData , InBytesSent , Status } ;
} ;
} ;
//----------------------------------------------------------------------------------------------------------
// FHttpCacheStore::FGetRecordOp
//----------------------------------------------------------------------------------------------------------
class FHttpCacheStore : : FGetRecordOp final : public FThreadSafeRefCountedObject
{
public :
/** Performs a multi-request operation for downloading a record. */
static void GetRecord (
FHttpCacheStore & CacheStore ,
IRequestOwner & Owner ,
const FSharedString & Name ,
const FCacheKey & Key ,
const FCacheRecordPolicy & Policy ,
uint64 UserData ,
TUniqueFunction < void ( FCacheGetResponse & & Response , uint64 BytesReceived ) > & & OnComplete ) ;
struct FGetCachedDataBatchResponse
{
FSharedString Name ;
FCacheKey Key ;
int32 ValueIndex ;
uint64 BytesReceived = 0 ;
FCompressedBuffer DataBuffer ;
EStatus Status = EStatus : : Error ;
} ;
using FOnGetCachedDataBatchComplete = TUniqueFunction < void ( FGetCachedDataBatchResponse & & Response ) > ;
/** Utility method for fetching a batch of value data. */
2022-06-29 21:31:09 -04:00
template < typename ValueType , typename ValueIdGetterType >
2022-04-05 16:43:41 -04:00
static void GetDataBatch (
FHttpCacheStore & CacheStore ,
IRequestOwner & Owner ,
FSharedString Name ,
const FCacheKey & Key ,
TConstArrayView < ValueType > Values ,
ValueIdGetterType ValueIdGetter ,
FOnGetCachedDataBatchComplete & & OnComplete ) ;
private :
FHttpCacheStore & CacheStore ;
IRequestOwner & Owner ;
const FSharedString Name ;
const FCacheKey Key ;
const uint64 UserData ;
std : : atomic < uint64 > BytesReceived ;
TArray < FCompressedBuffer > FetchedBuffers ;
const TArray < FValueWithId > RequiredGets ;
const TArray < FValueWithId > RequiredHeads ;
FCacheRecordBuilder RecordBuilder ;
const uint32 TotalOperations ;
std : : atomic < uint32 > SuccessfulOperations ;
std : : atomic < uint32 > PendingOperations ;
TUniqueFunction < void ( FCacheGetResponse & & Response , uint64 BytesReceived ) > OnComplete ;
FGetRecordOp (
FHttpCacheStore & InCacheStore ,
IRequestOwner & InOwner ,
const FSharedString & InName ,
const FCacheKey & InKey ,
uint64 InUserData ,
uint64 InBytesReceived ,
TArray < FValueWithId > & & InRequiredGets ,
TArray < FValueWithId > & & InRequiredHeads ,
FCacheRecordBuilder & & InRecordBuilder ,
TUniqueFunction < void ( FCacheGetResponse & & Response , uint64 BytesReceived ) > & & InOnComplete ) ;
static void OnOnlyRecordComplete (
FHttpCacheStore & CacheStore ,
IRequestOwner & Owner ,
const FCacheRecordPolicy & Policy ,
TUniqueFunction < void ( FCacheGetResponse & & Response , uint64 BytesReceived ) > & & OnComplete ,
FGetCacheRecordOnlyResponse & & Response ) ;
struct FCachedDataProbablyExistsBatchResponse
{
FSharedString Name ;
FCacheKey Key ;
int32 ValueIndex ;
EStatus Status = EStatus : : Error ;
} ;
using FOnCachedDataProbablyExistsBatchComplete = TUniqueFunction < void ( FCachedDataProbablyExistsBatchResponse & & Response ) > ;
void DataProbablyExistsBatch (
TConstArrayView < FValueWithId > Values ,
FOnCachedDataProbablyExistsBatchComplete & & OnComplete ) ;
void FinishDataStep ( bool bSuccess , uint64 InBytesReceived ) ;
} ;
void FHttpCacheStore : : FPutPackageOp : : PutPackage (
FHttpCacheStore & CacheStore ,
IRequestOwner & Owner ,
const FSharedString & Name ,
FCacheKey Key ,
FCbPackage & & Package ,
FCacheRecordPolicy Policy ,
uint64 UserData ,
FOnCachePutPackageComplete & & OnComplete )
{
// TODO: Jupiter currently always overwrites. It doesn't have a "write if not present" feature (for records or attachments),
// but would require one to implement all policy correctly.
// Initial record upload
PutRefAsync ( CacheStore , Owner , Name , Key , Package . GetObject ( ) , Package . GetObjectHash ( ) , UserData , false ,
[ & CacheStore , & Owner , Name = FSharedString ( Name ) , Key , Package = MoveTemp ( Package ) , Policy , UserData , OnComplete = MoveTemp ( OnComplete ) ] ( FCachePutRefResponse & & Response ) mutable
{
return OnPackagePutRefComplete ( CacheStore , Owner , Name , Key , MoveTemp ( Package ) , Policy , UserData , MoveTemp ( OnComplete ) , MoveTemp ( Response ) ) ;
} ) ;
}
FHttpCacheStore : : FPutPackageOp : : FPutPackageOp (
FHttpCacheStore & InCacheStore ,
IRequestOwner & InOwner ,
const FSharedString & InName ,
const FCacheKey & InKey ,
uint64 InUserData ,
uint64 InBytesSent ,
const FCbObject & InPackageObject ,
const FIoHash & InPackageObjectHash ,
uint32 InTotalBlobUploads ,
FOnCachePutPackageComplete & & InOnComplete )
: CacheStore ( InCacheStore )
, Owner ( InOwner )
, Name ( InName )
, Key ( InKey )
, UserData ( InUserData )
, BytesSent ( InBytesSent )
, PackageObject ( InPackageObject )
, PackageObjectHash ( InPackageObjectHash )
, TotalBlobUploads ( InTotalBlobUploads )
, SuccessfulBlobUploads ( 0 )
, PendingBlobUploads ( InTotalBlobUploads )
, OnComplete ( MoveTemp ( InOnComplete ) )
{
}
void FHttpCacheStore : : FPutPackageOp : : PutRefAsync (
FHttpCacheStore & CacheStore ,
IRequestOwner & Owner ,
const FSharedString & Name ,
FCacheKey Key ,
FCbObject Object ,
FIoHash ObjectHash ,
uint64 UserData ,
bool bFinalize ,
FOnCachePutRefComplete & & OnComplete )
{
FString Bucket ( Key . Bucket . ToString ( ) ) ;
Bucket . ToLowerInline ( ) ;
TStringBuilder < 256 > RefsUri ;
RefsUri < < " api/v1/refs/ " < < CacheStore . StructuredNamespace < < " / " < < Bucket < < " / " < < Key . Hash ;
if ( bFinalize )
{
RefsUri < < " /finalize/ " < < ObjectHash ;
}
2022-05-18 09:58:59 -04:00
FHttpRequestPool * Pool = nullptr ;
2022-04-05 16:43:41 -04:00
FHttpRequest * Request = CacheStore . WaitForHttpRequestForOwner < OperationCategory : : Put > ( Owner , bFinalize /* bUnboundedOverflow */ , Pool ) ;
auto OnHttpRequestComplete = [ & CacheStore , & Owner , Name = FSharedString ( Name ) , Key , Object , UserData , bFinalize , OnComplete = MoveTemp ( OnComplete ) ]
2022-05-31 12:42:17 -04:00
( FHttpRequest : : EResult HttpResult , FHttpRequest * Request )
2022-04-05 16:43:41 -04:00
{
TRACE_CPUPROFILER_EVENT_SCOPE ( HttpDDC_PutRefAsync_OnHttpRequestComplete ) ;
if ( Owner . IsCanceled ( ) )
{
OnComplete ( { Name , Key , UserData , Request - > GetBytesSent ( ) , { } , EStatus : : Canceled } ) ;
return FHttpRequest : : ECompletionBehavior : : Done ;
}
int64 ResponseCode = Request - > GetResponseCode ( ) ;
if ( FHttpRequest : : IsSuccessResponse ( ResponseCode ) )
{
TArray < FIoHash > NeededBlobHashes ;
// Useful when debugging issues related to compressed/uncompressed blobs being returned from Jupiter
const bool bPutRefBlobsAlways = false ;
if ( bPutRefBlobsAlways & & ! bFinalize )
{
Object . IterateAttachments ( [ & NeededBlobHashes ] ( FCbFieldView AttachmentFieldView )
2022-06-29 21:31:09 -04:00
{
FIoHash AttachmentHash = AttachmentFieldView . AsHash ( ) ;
if ( ! AttachmentHash . IsZero ( ) )
2022-04-05 16:43:41 -04:00
{
2022-06-29 21:31:09 -04:00
NeededBlobHashes . Add ( AttachmentHash ) ;
}
} ) ;
2022-04-05 16:43:41 -04:00
}
else if ( TSharedPtr < FJsonObject > ResponseObject = Request - > GetResponseAsJsonObject ( ) )
{
TArray < FString > NeedsArrayStrings ;
ResponseObject - > TryGetStringArrayField ( TEXT ( " needs " ) , NeedsArrayStrings ) ;
NeededBlobHashes . Reserve ( NeedsArrayStrings . Num ( ) ) ;
for ( const FString & NeededString : NeedsArrayStrings )
{
FIoHash BlobHash ;
LexFromString ( BlobHash , * NeededString ) ;
if ( ! BlobHash . IsZero ( ) )
{
NeededBlobHashes . Add ( BlobHash ) ;
}
}
}
OnComplete ( { Name , Key , UserData , Request - > GetBytesSent ( ) , NeededBlobHashes , EStatus : : Ok } ) ;
return FHttpRequest : : ECompletionBehavior : : Done ;
}
if ( ! ShouldAbortForShutdown ( ) & & CacheStore . ShouldRetryOnError ( HttpResult , ResponseCode ) & & ( ( Request - > GetAttempts ( ) + 1 ) < UE_HTTPDDC_MAX_ATTEMPTS ) )
{
return FHttpRequest : : ECompletionBehavior : : Retry ;
}
OnComplete ( { Name , Key , UserData , Request - > GetBytesSent ( ) , { } , EStatus : : Error } ) ;
return FHttpRequest : : ECompletionBehavior : : Done ;
} ;
if ( bFinalize )
{
2022-06-30 11:53:43 -04:00
Request - > EnqueueAsyncPost ( Owner , Pool , * RefsUri , FCompositeBuffer ( ) , MoveTemp ( OnHttpRequestComplete ) , EHttpMediaType : : FormUrlEncoded ) ;
2022-04-05 16:43:41 -04:00
}
else
{
2022-05-31 12:42:17 -04:00
Request - > AddHeader ( TEXTVIEW ( " X-Jupiter-IoHash " ) , WriteToString < 48 > ( ObjectHash ) ) ;
2022-06-30 11:53:43 -04:00
Request - > EnqueueAsyncPut ( Owner , Pool , * RefsUri , Object . GetBuffer ( ) , MoveTemp ( OnHttpRequestComplete ) , EHttpMediaType : : CbObject ) ;
2022-04-05 16:43:41 -04:00
}
}
void FHttpCacheStore : : FPutPackageOp : : OnPackagePutRefComplete (
FHttpCacheStore & CacheStore ,
IRequestOwner & Owner ,
const FSharedString & Name ,
const FCacheKey & Key ,
FCbPackage & & Package ,
FCacheRecordPolicy Policy ,
uint64 UserData ,
FOnCachePutPackageComplete & & OnComplete ,
FCachePutRefResponse & & Response )
{
if ( Response . Status ! = EStatus : : Ok )
{
if ( Response . Status = = EStatus : : Error )
{
UE_LOG ( LogDerivedDataCache , Log , TEXT ( " %s: Failed to put reference object for put of %s from '%s' " ) ,
2022-05-31 22:01:53 -04:00
* CacheStore . Domain , * WriteToString < 96 > ( Response . Key ) , * Response . Name ) ;
2022-04-05 16:43:41 -04:00
}
return OnComplete ( FCachePutPackageResponse { Name , Key , UserData , Response . BytesSent , Response . Status } ) ;
}
struct FCompressedBlobUpload
{
FIoHash Hash ;
FSharedBuffer BlobBuffer ;
FCompressedBlobUpload ( const FIoHash & InHash , FSharedBuffer & & InBlobBuffer ) : Hash ( InHash ) , BlobBuffer ( InBlobBuffer )
{
}
} ;
TArray < FCompressedBlobUpload > CompressedBlobUploads ;
// TODO: blob uploading and finalization should be replaced with a single batch compressed blob upload endpoint in the future.
TStringBuilder < 128 > ExpectedHashes ;
bool bExpectedHashesSerialized = false ;
// Needed blob upload (if any missing)
for ( const FIoHash & NeededBlobHash : Response . NeededBlobHashes )
{
if ( const FCbAttachment * Attachment = Package . FindAttachment ( NeededBlobHash ) )
{
FSharedBuffer TempBuffer ;
if ( Attachment - > IsCompressedBinary ( ) )
{
TempBuffer = Attachment - > AsCompressedBinary ( ) . GetCompressed ( ) . ToShared ( ) ;
}
else if ( Attachment - > IsBinary ( ) )
{
TempBuffer = FValue : : Compress ( Attachment - > AsCompositeBinary ( ) ) . GetData ( ) . GetCompressed ( ) . ToShared ( ) ;
}
else
{
TempBuffer = FValue : : Compress ( Attachment - > AsObject ( ) . GetBuffer ( ) ) . GetData ( ) . GetCompressed ( ) . ToShared ( ) ;
}
CompressedBlobUploads . Emplace ( NeededBlobHash , MoveTemp ( TempBuffer ) ) ;
}
else
{
if ( ! bExpectedHashesSerialized )
{
bool bFirstHash = true ;
for ( const FCbAttachment & PackageAttachment : Package . GetAttachments ( ) )
{
if ( ! bFirstHash )
{
ExpectedHashes < < TEXT ( " , " ) ;
}
ExpectedHashes < < PackageAttachment . GetHash ( ) ;
bFirstHash = false ;
}
bExpectedHashesSerialized = true ;
}
UE_LOG ( LogDerivedDataCache , Log , TEXT ( " %s: Server reported needed hash '%s' that is outside the set of expected hashes (%s) for put of %s from '%s' " ) ,
2022-05-31 22:01:53 -04:00
* CacheStore . Domain , * WriteToString < 96 > ( NeededBlobHash ) , ExpectedHashes . ToString ( ) , * WriteToString < 96 > ( Response . Key ) , * Response . Name ) ;
2022-04-05 16:43:41 -04:00
}
}
if ( CompressedBlobUploads . IsEmpty ( ) )
{
// No blobs need to be uploaded. No finalization necessary.
return OnComplete ( FCachePutPackageResponse { Name , Key , UserData , Response . BytesSent , EStatus : : Ok } ) ;
}
// Having this be a ref ensures we don't have the op reach 0 ref count as we queue up multiple operations which MAY execute synchronously
TRefCountPtr < FPutPackageOp > PutPackageOp = new FPutPackageOp (
CacheStore ,
Owner ,
Response . Name ,
Response . Key ,
Response . UserData ,
Response . BytesSent ,
Package . GetObject ( ) ,
Package . GetObjectHash ( ) ,
( uint32 ) CompressedBlobUploads . Num ( ) ,
MoveTemp ( OnComplete )
) ;
FRequestBarrier Barrier ( Owner ) ;
for ( const FCompressedBlobUpload & CompressedBlobUpload : CompressedBlobUploads )
{
TStringBuilder < 256 > CompressedBlobsUri ;
CompressedBlobsUri < < " api/v1/compressed-blobs/ " < < CacheStore . StructuredNamespace < < " / " < < CompressedBlobUpload . Hash ;
2022-05-18 09:58:59 -04:00
FHttpRequestPool * Pool = nullptr ;
2022-04-05 16:43:41 -04:00
FHttpRequest * Request = CacheStore . WaitForHttpRequestForOwner < OperationCategory : : Put > ( Owner , true /* bUnboundedOverflow */ , Pool ) ;
2022-05-31 12:42:17 -04:00
Request - > EnqueueAsyncPut ( Owner , Pool , * CompressedBlobsUri , FCompositeBuffer ( CompressedBlobUpload . BlobBuffer ) ,
[ PutPackageOp ] ( FHttpRequest : : EResult HttpResult , FHttpRequest * Request )
2022-04-05 16:43:41 -04:00
{
return PutPackageOp - > OnCompressedBlobUploadComplete ( HttpResult , Request ) ;
2022-05-31 12:42:17 -04:00
} ,
2022-06-30 11:53:43 -04:00
EHttpMediaType : : CompressedBinary ) ;
2022-04-05 16:43:41 -04:00
}
}
FHttpRequest : : ECompletionBehavior FHttpCacheStore : : FPutPackageOp : : OnCompressedBlobUploadComplete (
2022-05-31 12:42:17 -04:00
FHttpRequest : : EResult HttpResult ,
2022-04-05 16:43:41 -04:00
FHttpRequest * Request )
{
int64 ResponseCode = Request - > GetResponseCode ( ) ;
bool bIsSuccessResponse = FHttpRequest : : IsSuccessResponse ( ResponseCode ) ;
if ( ! bIsSuccessResponse & & ! ShouldAbortForShutdown ( ) & & ! Owner . IsCanceled ( ) & & CacheStore . ShouldRetryOnError ( HttpResult , ResponseCode ) & & ( ( Request - > GetAttempts ( ) + 1 ) < UE_HTTPDDC_MAX_ATTEMPTS ) )
{
return FHttpRequest : : ECompletionBehavior : : Retry ;
}
BytesSent . fetch_add ( Request - > GetBytesSent ( ) , std : : memory_order_relaxed ) ;
if ( bIsSuccessResponse )
{
SuccessfulBlobUploads . fetch_add ( 1 , std : : memory_order_relaxed ) ;
}
if ( PendingBlobUploads . fetch_sub ( 1 , std : : memory_order_relaxed ) = = 1 )
{
if ( Owner . IsCanceled ( ) )
{
OnComplete ( MakeResponse ( BytesSent . load ( std : : memory_order_relaxed ) , EStatus : : Canceled ) ) ;
return FHttpRequest : : ECompletionBehavior : : Done ;
}
uint32 LocalSuccessfulBlobUploads = SuccessfulBlobUploads . load ( std : : memory_order_relaxed ) ;
if ( LocalSuccessfulBlobUploads = = TotalBlobUploads )
{
// Perform finalization
PutRefAsync ( CacheStore , Owner , Name , Key , PackageObject , PackageObjectHash , UserData , true ,
[ PutPackageOp = TRefCountPtr < FPutPackageOp > ( this ) ] ( FCachePutRefResponse & & Response )
{
return PutPackageOp - > OnPutRefFinalizationComplete ( MoveTemp ( Response ) ) ;
} ) ;
}
else
{
uint32 FailedBlobUploads = ( uint32 ) ( TotalBlobUploads - LocalSuccessfulBlobUploads ) ;
UE_LOG ( LogDerivedDataCache , Log , TEXT ( " %s: Failed to put %d/%d blobs for put of %s from '%s' " ) ,
2022-05-31 22:01:53 -04:00
* CacheStore . Domain , FailedBlobUploads , TotalBlobUploads , * WriteToString < 96 > ( Key ) , * Name ) ;
2022-04-05 16:43:41 -04:00
OnComplete ( MakeResponse ( BytesSent . load ( std : : memory_order_relaxed ) , EStatus : : Error ) ) ;
}
}
return FHttpRequest : : ECompletionBehavior : : Done ;
}
void FHttpCacheStore : : FPutPackageOp : : OnPutRefFinalizationComplete (
FCachePutRefResponse & & Response )
{
BytesSent . fetch_add ( Response . BytesSent , std : : memory_order_relaxed ) ;
if ( Response . Status = = EStatus : : Error )
{
UE_LOG ( LogDerivedDataCache , Log , TEXT ( " %s: Failed to finalize reference object for put of %s from '%s' " ) ,
2022-05-31 22:01:53 -04:00
* CacheStore . Domain , * WriteToString < 96 > ( Key ) , * Name ) ;
2022-04-05 16:43:41 -04:00
}
return OnComplete ( MakeResponse ( BytesSent . load ( std : : memory_order_relaxed ) , Response . Status ) ) ;
}
void FHttpCacheStore : : FGetRecordOp : : GetRecord (
FHttpCacheStore & CacheStore ,
IRequestOwner & Owner ,
const FSharedString & Name ,
const FCacheKey & Key ,
const FCacheRecordPolicy & Policy ,
uint64 UserData ,
TUniqueFunction < void ( FCacheGetResponse & & Response , uint64 BytesReceived ) > & & OnComplete )
{
CacheStore . GetCacheRecordOnlyAsync ( Owner , Name , Key , Policy , UserData , [ & CacheStore , & Owner , Policy = FCacheRecordPolicy ( Policy ) , OnComplete = MoveTemp ( OnComplete ) ] ( FGetCacheRecordOnlyResponse & & Response ) mutable
{
OnOnlyRecordComplete ( CacheStore , Owner , Policy , MoveTemp ( OnComplete ) , MoveTemp ( Response ) ) ;
} ) ;
}
2022-06-29 21:31:09 -04:00
template < typename ValueType , typename ValueIdGetterType >
2022-04-05 16:43:41 -04:00
void FHttpCacheStore : : FGetRecordOp : : GetDataBatch (
FHttpCacheStore & CacheStore ,
IRequestOwner & Owner ,
FSharedString Name ,
const FCacheKey & Key ,
TConstArrayView < ValueType > Values ,
ValueIdGetterType ValueIdGetter ,
FOnGetCachedDataBatchComplete & & OnComplete )
{
if ( Values . IsEmpty ( ) )
{
return ;
}
FRequestBarrier Barrier ( Owner ) ;
TRefCountedUniqueFunction < FOnGetCachedDataBatchComplete > * CompletionFunction = new TRefCountedUniqueFunction < FOnGetCachedDataBatchComplete > ( MoveTemp ( OnComplete ) ) ;
TRefCountPtr < TRefCountedUniqueFunction < FOnGetCachedDataBatchComplete > > BatchOnCompleteRef ( CompletionFunction ) ;
for ( int32 ValueIndex = 0 ; ValueIndex < Values . Num ( ) ; + + ValueIndex )
{
const ValueType & Value = Values [ ValueIndex ] ;
const FIoHash & RawHash = Value . GetRawHash ( ) ;
2022-05-18 09:58:59 -04:00
FHttpRequestPool * Pool = nullptr ;
2022-04-05 16:43:41 -04:00
FHttpRequest * Request = CacheStore . WaitForHttpRequestForOwner < OperationCategory : : Get > ( Owner , true /* bUnboundedOverflow */ , Pool ) ;
auto OnHttpRequestComplete = [ & CacheStore , & Owner , Name = FSharedString ( Name ) , Key = FCacheKey ( Key ) , ValueIndex , Value = Value . RemoveData ( ) , ValueIdGetter , OnCompletePtr = TRefCountPtr < TRefCountedUniqueFunction < FOnGetCachedDataBatchComplete > > ( CompletionFunction ) ]
2022-05-31 12:42:17 -04:00
( FHttpRequest : : EResult HttpResult , FHttpRequest * Request )
2022-04-05 16:43:41 -04:00
{
TRACE_CPUPROFILER_EVENT_SCOPE ( HttpDDC_GetDataBatch_OnHttpRequestComplete ) ;
int64 ResponseCode = Request - > GetResponseCode ( ) ;
bool bHit = false ;
FCompressedBuffer CompressedBuffer ;
if ( FHttpRequest : : IsSuccessResponse ( ResponseCode ) )
{
FString ReceivedContentType ;
if ( Request - > GetHeader ( " Content-Type " , ReceivedContentType ) )
{
if ( ReceivedContentType = = TEXT ( " application/x-ue-comp " ) )
{
CompressedBuffer = FCompressedBuffer : : FromCompressed ( Request - > MoveResponseBufferToShared ( ) ) ;
bHit = true ;
}
else if ( ReceivedContentType = = TEXT ( " application/octet-stream " ) )
{
CompressedBuffer = FValue : : Compress ( Request - > MoveResponseBufferToShared ( ) ) . GetData ( ) ;
bHit = true ;
}
else
{
bHit = false ;
}
}
else
{
CompressedBuffer = FCompressedBuffer : : FromCompressed ( Request - > MoveResponseBufferToShared ( ) ) ;
bHit = true ;
}
}
if ( ! ShouldAbortForShutdown ( ) & & ! Owner . IsCanceled ( ) & & CacheStore . ShouldRetryOnError ( HttpResult , ResponseCode ) & & ( ( Request - > GetAttempts ( ) + 1 ) < UE_HTTPDDC_MAX_ATTEMPTS ) )
{
return FHttpRequest : : ECompletionBehavior : : Retry ;
}
if ( ! bHit )
{
UE_LOG ( LogDerivedDataCache , Verbose ,
TEXT ( " %s: Cache miss with missing value %s with hash %s for %s from '%s' " ) ,
2022-05-31 22:01:53 -04:00
* CacheStore . Domain , * ValueIdGetter ( Value ) , * WriteToString < 48 > ( Value . GetRawHash ( ) ) , * WriteToString < 96 > ( Key ) ,
2022-04-05 16:43:41 -04:00
* Name ) ;
OnCompletePtr - > GetFunction ( ) ( { Name , Key , ValueIndex , Request - > GetBytesReceived ( ) , { } , EStatus : : Error } ) ;
}
else if ( CompressedBuffer . GetRawHash ( ) ! = Value . GetRawHash ( ) )
{
UE_LOG ( LogDerivedDataCache , Display ,
TEXT ( " %s: Cache miss with corrupted value %s with hash %s for %s from '%s' " ) ,
2022-05-31 22:01:53 -04:00
* CacheStore . Domain , * ValueIdGetter ( Value ) , * WriteToString < 48 > ( Value . GetRawHash ( ) ) ,
2022-04-05 16:43:41 -04:00
* WriteToString < 96 > ( Key ) , * Name ) ;
OnCompletePtr - > GetFunction ( ) ( { Name , Key , ValueIndex , Request - > GetBytesReceived ( ) , { } , EStatus : : Error } ) ;
}
else
{
OnCompletePtr - > GetFunction ( ) ( { Name , Key , ValueIndex , Request - > GetBytesReceived ( ) , MoveTemp ( CompressedBuffer ) , EStatus : : Ok } ) ;
}
return FHttpRequest : : ECompletionBehavior : : Done ;
} ;
TStringBuilder < 256 > CompressedBlobsUri ;
CompressedBlobsUri < < " api/v1/compressed-blobs/ " < < CacheStore . StructuredNamespace < < " / " < < RawHash ;
2022-06-30 11:53:43 -04:00
Request - > EnqueueAsyncDownload ( Owner , Pool , * CompressedBlobsUri , MoveTemp ( OnHttpRequestComplete ) , EHttpMediaType : : Any , { 404 } ) ;
2022-04-05 16:43:41 -04:00
}
}
FHttpCacheStore : : FGetRecordOp : : FGetRecordOp (
FHttpCacheStore & InCacheStore ,
IRequestOwner & InOwner ,
const FSharedString & InName ,
const FCacheKey & InKey ,
uint64 InUserData ,
uint64 InBytesReceived ,
TArray < FValueWithId > & & InRequiredGets ,
TArray < FValueWithId > & & InRequiredHeads ,
FCacheRecordBuilder & & InRecordBuilder ,
TUniqueFunction < void ( FCacheGetResponse & & Response , uint64 BytesReceived ) > & & InOnComplete )
: CacheStore ( InCacheStore )
, Owner ( InOwner )
, Name ( InName )
, Key ( InKey )
, UserData ( InUserData )
, BytesReceived ( InBytesReceived )
, RequiredGets ( MoveTemp ( InRequiredGets ) )
, RequiredHeads ( MoveTemp ( InRequiredHeads ) )
, RecordBuilder ( MoveTemp ( InRecordBuilder ) )
, TotalOperations ( RequiredGets . Num ( ) + RequiredHeads . Num ( ) )
, SuccessfulOperations ( 0 )
, PendingOperations ( TotalOperations )
, OnComplete ( MoveTemp ( InOnComplete ) )
{
FetchedBuffers . AddDefaulted ( RequiredGets . Num ( ) ) ;
}
void FHttpCacheStore : : FGetRecordOp : : OnOnlyRecordComplete (
FHttpCacheStore & CacheStore ,
IRequestOwner & Owner ,
const FCacheRecordPolicy & Policy ,
TUniqueFunction < void ( FCacheGetResponse & & Response , uint64 BytesReceived ) > & & OnComplete ,
FGetCacheRecordOnlyResponse & & Response )
{
FCacheRecordBuilder RecordBuilder ( Response . Key ) ;
if ( Response . Status ! = EStatus : : Ok )
{
return OnComplete ( { Response . Name , RecordBuilder . Build ( ) , Response . UserData , Response . Status } , Response . BytesReceived ) ;
}
if ( ! EnumHasAnyFlags ( Policy . GetRecordPolicy ( ) , ECachePolicy : : SkipMeta ) )
{
RecordBuilder . SetMeta ( FCbObject ( Response . Record . Get ( ) . GetMeta ( ) ) ) ;
}
// TODO: There is not currently a batched GET endpoint for Jupiter. Once there is, all payload data should be fetched in one call.
// In the meantime, we try to keep the code structured in a way that is friendly to future batching of GETs.
TArray < FValueWithId > RequiredGets ;
TArray < FValueWithId > RequiredHeads ;
for ( FValueWithId Value : Response . Record . Get ( ) . GetValues ( ) )
{
const ECachePolicy ValuePolicy = Policy . GetValuePolicy ( Value . GetId ( ) ) ;
if ( IsValueDataReady ( Value , ValuePolicy ) )
{
RecordBuilder . AddValue ( MoveTemp ( Value ) ) ;
}
else
{
if ( EnumHasAnyFlags ( ValuePolicy , ECachePolicy : : SkipData ) )
{
RequiredHeads . Emplace ( Value ) ;
}
else
{
RequiredGets . Emplace ( Value ) ;
}
}
}
if ( RequiredGets . IsEmpty ( ) & & RequiredHeads . IsEmpty ( ) )
{
return OnComplete ( { Response . Name , RecordBuilder . Build ( ) , Response . UserData , Response . Status } , Response . BytesReceived ) ;
}
// Having this be a ref ensures we don't have the op reach 0 ref count in between the start of the exist batch operation and the get batch operation
TRefCountPtr < FGetRecordOp > GetRecordOp = new FGetRecordOp (
CacheStore ,
Owner ,
Response . Name ,
Response . Key ,
Response . UserData ,
Response . BytesReceived ,
MoveTemp ( RequiredGets ) ,
MoveTemp ( RequiredHeads ) ,
MoveTemp ( RecordBuilder ) ,
MoveTemp ( OnComplete )
) ;
auto IdGetter = [ ] ( const FValueWithId & Value )
{
return FString ( WriteToString < 16 > ( Value . GetId ( ) ) ) ;
} ;
{
FRequestBarrier Barrier ( Owner ) ;
GetRecordOp - > DataProbablyExistsBatch ( GetRecordOp - > RequiredHeads , [ GetRecordOp ] ( FCachedDataProbablyExistsBatchResponse & & Response )
{
GetRecordOp - > FinishDataStep ( Response . Status = = EStatus : : Ok , 0 ) ;
} ) ;
GetDataBatch < FValueWithId > ( CacheStore , Owner , Response . Name , Response . Key , GetRecordOp - > RequiredGets , IdGetter , [ GetRecordOp ] ( FGetCachedDataBatchResponse & & Response )
{
GetRecordOp - > FetchedBuffers [ Response . ValueIndex ] = MoveTemp ( Response . DataBuffer ) ;
GetRecordOp - > FinishDataStep ( Response . Status = = EStatus : : Ok , Response . BytesReceived ) ;
} ) ;
}
}
void FHttpCacheStore : : FGetRecordOp : : DataProbablyExistsBatch (
TConstArrayView < FValueWithId > Values ,
FOnCachedDataProbablyExistsBatchComplete & & InOnComplete )
{
if ( Values . IsEmpty ( ) )
{
return ;
}
2022-05-18 09:58:59 -04:00
FHttpRequestPool * Pool = nullptr ;
2022-04-05 16:43:41 -04:00
FHttpRequest * Request = CacheStore . WaitForHttpRequestForOwner < OperationCategory : : Get > ( Owner , true /* bUnboundedOverflow */ , Pool ) ;
TStringBuilder < 256 > CompressedBlobsUri ;
CompressedBlobsUri < < " api/v1/compressed-blobs/ " < < CacheStore . StructuredNamespace < < " /exists? " ;
bool bFirstItem = true ;
for ( const FValueWithId & Value : Values )
{
if ( ! bFirstItem )
{
CompressedBlobsUri < < " & " ;
}
CompressedBlobsUri < < " id= " < < Value . GetRawHash ( ) ;
bFirstItem = false ;
}
2022-05-31 12:42:17 -04:00
auto OnHttpRequestComplete = [ this , Values = TArray < FValueWithId > ( Values ) , InOnComplete = MoveTemp ( InOnComplete ) ] ( FHttpRequest : : EResult HttpResult , FHttpRequest * Request )
2022-04-05 16:43:41 -04:00
{
TRACE_CPUPROFILER_EVENT_SCOPE ( HttpDDC_DataProbablyExistsBatch_OnHttpRequestComplete ) ;
int64 ResponseCode = Request - > GetResponseCode ( ) ;
if ( FHttpRequest : : IsSuccessResponse ( ResponseCode ) )
{
if ( TSharedPtr < FJsonObject > ResponseObject = Request - > GetResponseAsJsonObject ( ) )
{
TArray < FString > NeedsArrayStrings ;
if ( ResponseObject - > TryGetStringArrayField ( TEXT ( " needs " ) , NeedsArrayStrings ) )
{
if ( NeedsArrayStrings . IsEmpty ( ) )
{
for ( int32 ValueIndex = 0 ; ValueIndex < Values . Num ( ) ; + + ValueIndex )
{
const FValueWithId & Value = Values [ ValueIndex ] ;
2022-05-20 11:32:33 -04:00
UE_LOG ( LogDerivedDataCache , VeryVerbose ,
TEXT ( " %s: Cache exists hit for %s with hash %s for %s from '%s' " ) ,
2022-05-31 22:01:53 -04:00
* CacheStore . Domain , * WriteToString < 16 > ( Value . GetId ( ) ) , * WriteToString < 48 > ( Value . GetRawHash ( ) ) , * WriteToString < 96 > ( Key ) ,
2022-04-05 16:43:41 -04:00
* Name ) ;
2022-05-20 11:32:33 -04:00
InOnComplete ( { Name , Key , ValueIndex , EStatus : : Ok } ) ;
2022-04-05 16:43:41 -04:00
}
return FHttpRequest : : ECompletionBehavior : : Done ;
}
}
TBitArray < > ResultStatus ( true , Values . Num ( ) ) ;
for ( const FString & NeedsString : NeedsArrayStrings )
{
2022-05-20 11:32:33 -04:00
const FIoHash NeedHash ( NeedsString ) ;
2022-04-05 16:43:41 -04:00
for ( int32 ValueIndex = 0 ; ValueIndex < Values . Num ( ) ; + + ValueIndex )
{
const FValueWithId & Value = Values [ ValueIndex ] ;
if ( ResultStatus [ ValueIndex ] & & NeedHash = = Value . GetRawHash ( ) )
{
ResultStatus [ ValueIndex ] = false ;
break ;
}
}
}
for ( int32 ValueIndex = 0 ; ValueIndex < Values . Num ( ) ; + + ValueIndex )
{
const FValueWithId & Value = Values [ ValueIndex ] ;
if ( ResultStatus [ ValueIndex ] )
{
UE_LOG ( LogDerivedDataCache , VeryVerbose ,
TEXT ( " %s: Cache exists hit for %s with hash %s for %s from '%s' " ) ,
2022-05-31 22:01:53 -04:00
* CacheStore . Domain , * WriteToString < 16 > ( Value . GetId ( ) ) , * WriteToString < 48 > ( Value . GetRawHash ( ) ) , * WriteToString < 96 > ( Key ) ,
2022-04-05 16:43:41 -04:00
* Name ) ;
InOnComplete ( { Name , Key , ValueIndex , EStatus : : Ok } ) ;
}
else
{
UE_LOG ( LogDerivedDataCache , Verbose ,
TEXT ( " %s: Cache exists miss with missing value %s with hash %s for %s from '%s' " ) ,
2022-05-31 22:01:53 -04:00
* CacheStore . Domain , * WriteToString < 16 > ( Value . GetId ( ) ) , * WriteToString < 48 > ( Value . GetRawHash ( ) ) , * WriteToString < 96 > ( Key ) ,
2022-04-05 16:43:41 -04:00
* Name ) ;
InOnComplete ( { Name , Key , ValueIndex , EStatus : : Error } ) ;
}
}
}
else
{
for ( int32 ValueIndex = 0 ; ValueIndex < Values . Num ( ) ; + + ValueIndex )
{
UE_LOG ( LogDerivedDataCache , Log ,
TEXT ( " %s: Cache exists returned invalid results. " ) ,
2022-05-31 22:01:53 -04:00
* CacheStore . Domain ) ;
2022-04-05 16:43:41 -04:00
InOnComplete ( { Name , Key , ValueIndex , EStatus : : Error } ) ;
}
}
return FHttpRequest : : ECompletionBehavior : : Done ;
}
if ( ! ShouldAbortForShutdown ( ) & & ! Owner . IsCanceled ( ) & & CacheStore . ShouldRetryOnError ( HttpResult , ResponseCode ) & & ( ( Request - > GetAttempts ( ) + 1 ) < UE_HTTPDDC_MAX_ATTEMPTS ) )
{
return FHttpRequest : : ECompletionBehavior : : Retry ;
}
for ( int32 ValueIndex = 0 ; ValueIndex < Values . Num ( ) ; + + ValueIndex )
{
const FValueWithId & Value = Values [ ValueIndex ] ;
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " %s: Cache miss with failed HTTP request for %s from '%s' " ) ,
2022-05-31 22:01:53 -04:00
* CacheStore . Domain , * WriteToString < 96 > ( Key ) , * Name ) ;
2022-04-05 16:43:41 -04:00
InOnComplete ( { Name , Key , ValueIndex , EStatus : : Error } ) ;
}
return FHttpRequest : : ECompletionBehavior : : Done ;
} ;
2022-05-31 12:42:17 -04:00
FCompositeBuffer DummyBuffer ;
2022-06-30 11:53:43 -04:00
Request - > EnqueueAsyncPost ( Owner , Pool , * CompressedBlobsUri , DummyBuffer , MoveTemp ( OnHttpRequestComplete ) , EHttpMediaType : : FormUrlEncoded ) ;
2022-04-05 16:43:41 -04:00
}
void FHttpCacheStore : : FGetRecordOp : : FinishDataStep ( bool bSuccess , uint64 InBytesReceived )
{
BytesReceived . fetch_add ( InBytesReceived , std : : memory_order_relaxed ) ;
if ( bSuccess )
{
SuccessfulOperations . fetch_add ( 1 , std : : memory_order_relaxed ) ;
}
if ( PendingOperations . fetch_sub ( 1 , std : : memory_order_acq_rel ) = = 1 )
{
EStatus Status = EStatus : : Error ;
uint32 LocalSuccessfulOperations = SuccessfulOperations . load ( std : : memory_order_relaxed ) ;
if ( LocalSuccessfulOperations = = TotalOperations )
{
for ( int32 Index = 0 ; Index < RequiredHeads . Num ( ) ; + + Index )
{
RecordBuilder . AddValue ( RequiredHeads [ Index ] . RemoveData ( ) ) ;
}
for ( int32 Index = 0 ; Index < RequiredGets . Num ( ) ; + + Index )
{
RecordBuilder . AddValue ( FValueWithId ( RequiredGets [ Index ] . GetId ( ) , FetchedBuffers [ Index ] ) ) ;
}
Status = EStatus : : Ok ;
}
OnComplete ( { Name , RecordBuilder . Build ( ) , UserData , Status } , BytesReceived . load ( std : : memory_order_relaxed ) ) ;
}
}
2022-05-31 22:01:53 -04:00
FHttpCacheStore : : FHttpCacheStore ( const FHttpCacheStoreParams & Params )
: Domain ( Params . Host )
, EffectiveDomain ( Params . Host )
, Namespace ( Params . Namespace )
, StructuredNamespace ( Params . StructuredNamespace )
, OAuthProvider ( Params . OAuthProvider )
, OAuthClientId ( Params . OAuthClientId )
, OAuthSecret ( Params . OAuthSecret )
, OAuthScope ( Params . OAuthScope )
2022-07-06 05:59:22 -04:00
, OAuthProviderIdentifier ( Params . OAuthProviderIdentifier )
, OAuthAccessToken ( Params . OAuthAccessToken )
2022-05-31 22:01:53 -04:00
, bReadOnly ( Params . bReadOnly )
2020-06-23 18:40:00 -04:00
{
2022-05-11 09:10:24 -04:00
SharedData = MakeUnique < FHttpSharedData > ( ) ;
2020-06-23 18:40:00 -04:00
if ( IsServiceReady ( ) & & AcquireAccessToken ( ) )
{
2022-02-08 01:56:17 -05:00
FString OriginalDomainPrefix ;
TAnsiStringBuilder < 64 > DomainResolveName ;
if ( Domain . StartsWith ( TEXT ( " http:// " ) ) )
{
DomainResolveName < < Domain . RightChop ( 7 ) ;
OriginalDomainPrefix = TEXT ( " http:// " ) ;
}
else if ( Domain . StartsWith ( TEXT ( " https:// " ) ) )
{
DomainResolveName < < Domain . RightChop ( 8 ) ;
OriginalDomainPrefix = TEXT ( " https:// " ) ;
}
else
{
DomainResolveName < < Domain ;
}
addrinfo * AddrResult = nullptr ;
addrinfo AddrHints ;
FMemory : : Memset ( & AddrHints , 0 , sizeof ( AddrHints ) ) ;
AddrHints . ai_flags = AI_CANONNAME ;
AddrHints . ai_family = AF_UNSPEC ;
2022-05-31 22:01:53 -04:00
if ( Params . bResolveHostCanonicalName & & ! : : getaddrinfo ( * DomainResolveName , nullptr , & AddrHints , & AddrResult ) )
2022-02-08 01:56:17 -05:00
{
if ( AddrResult - > ai_canonname )
{
// Swap the domain with a canonical name from DNS so that if we are using regional redirection, we pin to a region.
EffectiveDomain = OriginalDomainPrefix + ANSI_TO_TCHAR ( AddrResult - > ai_canonname ) ;
UE_LOG ( LogDerivedDataCache , Display ,
TEXT ( " %s: Pinned to %s based on DNS canonical name. " ) ,
* Domain , * EffectiveDomain ) ;
}
else
{
EffectiveDomain = Domain ;
}
: : freeaddrinfo ( AddrResult ) ;
}
else
{
EffectiveDomain = Domain ;
}
2022-05-18 09:58:59 -04:00
GetRequestPools [ 0 ] = MakeUnique < FHttpRequestPool > ( * Domain , * EffectiveDomain , Access . Get ( ) , SharedData . Get ( ) , UE_HTTPDDC_GET_REQUEST_POOL_SIZE ) ;
GetRequestPools [ 1 ] = MakeUnique < FHttpRequestPool > ( * Domain , * EffectiveDomain , Access . Get ( ) , SharedData . Get ( ) , UE_HTTPDDC_GET_REQUEST_POOL_SIZE ) ;
PutRequestPools [ 0 ] = MakeUnique < FHttpRequestPool > ( * Domain , * EffectiveDomain , Access . Get ( ) , SharedData . Get ( ) , UE_HTTPDDC_PUT_REQUEST_POOL_SIZE ) ;
PutRequestPools [ 1 ] = MakeUnique < FHttpRequestPool > ( * Domain , * EffectiveDomain , Access . Get ( ) , SharedData . Get ( ) , UE_HTTPDDC_PUT_REQUEST_POOL_SIZE ) ;
2022-04-05 16:43:41 -04:00
// Allowing the non-blocking requests to overflow to double their pre-allocated size before we start waiting for one to free up.
2022-05-18 09:58:59 -04:00
NonBlockingRequestPools = MakeUnique < FHttpRequestPool > ( * Domain , * EffectiveDomain , Access . Get ( ) , SharedData . Get ( ) , UE_HTTPDDC_NONBLOCKING_REQUEST_POOL_SIZE , UE_HTTPDDC_NONBLOCKING_REQUEST_POOL_SIZE ) ;
2020-06-23 18:40:00 -04:00
bIsUsable = true ;
}
2021-07-19 13:09:47 -04:00
AnyInstance = this ;
2020-06-23 18:40:00 -04:00
}
2022-02-14 14:43:39 -05:00
FHttpCacheStore : : ~ FHttpCacheStore ( )
2020-06-23 18:40:00 -04:00
{
2022-07-12 15:36:38 -04:00
if ( RefreshAccessTokenHandle . IsValid ( ) )
{
FTSTicker : : GetCoreTicker ( ) . RemoveTicker ( RefreshAccessTokenHandle ) ;
}
2021-07-19 13:09:47 -04:00
if ( AnyInstance = = this )
{
AnyInstance = nullptr ;
}
2020-06-23 18:40:00 -04:00
}
2022-05-31 22:01:53 -04:00
bool FHttpCacheStore : : LegacyDebugOptions ( FBackendDebugOptions & InOptions )
2020-06-23 18:40:00 -04:00
{
2021-11-07 23:43:01 -05:00
DebugOptions = InOptions ;
return true ;
2020-06-23 18:40:00 -04:00
}
2022-02-14 14:43:39 -05:00
bool FHttpCacheStore : : IsServiceReady ( )
2020-06-23 18:40:00 -04:00
{
2022-05-11 09:10:24 -04:00
FHttpRequest Request ( * Domain , * Domain , nullptr , SharedData . Get ( ) , false ) ;
2022-05-31 12:42:17 -04:00
FHttpRequest : : EResult Result = Request . PerformBlockingDownload ( TEXT ( " health/ready " ) , nullptr ) ;
2020-06-23 18:40:00 -04:00
2022-05-31 12:42:17 -04:00
if ( Result = = FHttpRequest : : EResult : : Success & & Request . GetResponseCode ( ) = = 200 )
2020-06-23 18:40:00 -04:00
{
2022-02-08 01:56:17 -05:00
UE_LOG ( LogDerivedDataCache , Display , TEXT ( " %s: HTTP DDC service status: %s. " ) , * Request . GetName ( ) , * Request . GetResponseAsString ( ) ) ;
2020-06-23 18:40:00 -04:00
return true ;
}
else
{
2022-02-08 01:56:17 -05:00
UE_LOG ( LogDerivedDataCache , Warning , TEXT ( " %s: Unable to reach HTTP DDC service at %s. Status: %d . Response: %s " ) , * Request . GetName ( ) , * Domain , Request . GetResponseCode ( ) , * Request . GetResponseAsString ( ) ) ;
2020-06-23 18:40:00 -04:00
}
return false ;
}
2022-02-14 14:43:39 -05:00
bool FHttpCacheStore : : AcquireAccessToken ( )
2020-06-23 18:40:00 -04:00
{
2022-03-09 16:48:12 -05:00
if ( Domain . StartsWith ( TEXT ( " http://localhost " ) ) )
{
2022-07-12 15:36:38 -04:00
UE_LOG ( LogDerivedDataCache , Log , TEXT ( " %s: Skipping authorization for connection to localhost. " ) , * Domain ) ;
2022-03-09 16:48:12 -05:00
return true ;
}
2022-07-12 15:36:38 -04:00
// Avoid spamming this if the service is down.
2020-06-23 18:40:00 -04:00
if ( FailedLoginAttempts > UE_HTTPDDC_MAX_FAILED_LOGIN_ATTEMPTS )
{
return false ;
}
ensureMsgf ( OAuthProvider . StartsWith ( TEXT ( " http:// " ) ) | | OAuthProvider . StartsWith ( TEXT ( " https:// " ) ) ,
2022-07-12 15:36:38 -04:00
TEXT ( " %s: OAuth provider %s must be a complete URI including the scheme. " ) , * Domain , * OAuthProvider ) ;
2020-06-23 18:40:00 -04:00
// In case many requests wants to update the token at the same time
// get the current serial while we wait to take the CS.
2022-07-12 15:36:38 -04:00
const uint32 WantsToUpdateTokenSerial = Access ? Access - > GetSerial ( ) : 0 ;
FScopeLock Lock ( & AccessCs ) ;
// If the token was updated while we waited to take the lock, then it should now be valid.
if ( Access & & Access - > GetSerial ( ) > WantsToUpdateTokenSerial )
{
return true ;
}
if ( ! OAuthAccessToken . IsEmpty ( ) )
{
SetAccessToken ( OAuthAccessToken ) ;
return true ;
}
2020-06-23 18:40:00 -04:00
2022-07-06 05:59:22 -04:00
if ( bHttpEnableOidc & & ! OAuthProviderIdentifier . IsEmpty ( ) )
{
FString AccessTokenString ;
FDateTime TokenExpiresAt ;
2022-07-12 15:36:38 -04:00
if ( FDesktopPlatformModule : : Get ( ) - > GetOidcAccessToken ( FPaths : : RootDir ( ) , FPaths : : GetProjectFilePath ( ) , OAuthProviderIdentifier , /* Unattended */ true , GWarn , AccessTokenString , TokenExpiresAt ) )
2020-06-23 18:40:00 -04:00
{
2022-07-12 15:36:38 -04:00
const double ExpiryTimeSeconds = ( TokenExpiresAt - FDateTime : : UtcNow ( ) ) . GetTotalSeconds ( ) ;
UE_LOG ( LogDerivedDataCache , Display ,
TEXT ( " %s: OidcToken: Logged in to HTTP DDC services. Expires at %s which is in %.0f seconds. " ) ,
* Domain , * TokenExpiresAt . ToString ( ) , ExpiryTimeSeconds ) ;
SetAccessToken ( AccessTokenString , ExpiryTimeSeconds ) ;
2020-06-23 18:40:00 -04:00
return true ;
}
2022-07-12 15:36:38 -04:00
UE_LOG ( LogDerivedDataCache , Error , TEXT ( " %s: OidcToken: Failed to log in to HTTP services. " ) , * Domain ) ;
FailedLoginAttempts + + ;
return false ;
}
2020-06-23 18:40:00 -04:00
2022-07-12 15:36:38 -04:00
const uint32 SchemeEnd = OAuthProvider . Find ( TEXT ( " :// " ) ) + 3 ;
const uint32 DomainEnd = OAuthProvider . Find ( TEXT ( " / " ) , ESearchCase : : CaseSensitive , ESearchDir : : FromStart , SchemeEnd ) ;
FString AuthDomain ( DomainEnd , * OAuthProvider ) ;
FString Uri ( * OAuthProvider + DomainEnd + 1 ) ;
2020-06-23 18:40:00 -04:00
2022-07-12 15:36:38 -04:00
FHttpRequest Request ( * AuthDomain , * AuthDomain , nullptr , SharedData . Get ( ) , false ) ;
FHttpRequest : : EResult Result = FHttpRequest : : EResult : : Success ;
if ( OAuthProvider . StartsWith ( TEXT ( " http://localhost " ) ) )
{
// Simple unauthenticated call to a local endpoint that mimics the result from an OIDC provider.
Result = Request . PerformBlockingDownload ( * Uri , nullptr ) ;
}
else
{
// Needs client id and secret to authenticate with an actual OIDC provider.
// If contents of the secret string is a file path, resolve and read form data.
if ( OAuthSecret . StartsWith ( TEXT ( " file:// " ) ) )
{
FString FilePath = OAuthSecret . Mid ( 7 , OAuthSecret . Len ( ) - 7 ) ;
FString SecretFileContents ;
if ( FFileHelper : : LoadFileToString ( SecretFileContents , * FilePath ) )
2022-03-09 16:48:12 -05:00
{
2022-07-12 15:36:38 -04:00
// Overwrite the filepath with the actual content.
OAuthSecret = SecretFileContents ;
2022-03-09 16:48:12 -05:00
}
2022-07-12 15:36:38 -04:00
else
2020-06-23 18:40:00 -04:00
{
2022-07-12 15:36:38 -04:00
UE_LOG ( LogDerivedDataCache , Warning , TEXT ( " %s: Failed to read OAuth form data file (%s). " ) , * Domain , * OAuthSecret ) ;
return false ;
2020-06-23 18:40:00 -04:00
}
}
2022-07-12 15:36:38 -04:00
TUtf8StringBuilder < 256 > OAuthFormData ;
OAuthFormData
< < ANSITEXTVIEW ( " client_id= " ) < < OAuthClientId
< < ANSITEXTVIEW ( " &scope= " ) < < OAuthScope
< < ANSITEXTVIEW ( " &grant_type=client_credentials " )
< < ANSITEXTVIEW ( " &client_secret= " ) < < OAuthSecret ;
Result = Request . PerformBlockingPost ( * Uri , FCompositeBuffer ( FSharedBuffer : : MakeView ( MakeMemoryView ( OAuthFormData ) ) ) , EHttpMediaType : : FormUrlEncoded ) ;
}
if ( Result = = FHttpRequest : : EResult : : Success & & Request . GetResponseCode ( ) = = 200 )
{
if ( TSharedPtr < FJsonObject > ResponseObject = Request . GetResponseAsJsonObject ( ) )
2020-06-23 18:40:00 -04:00
{
2022-07-12 15:36:38 -04:00
FString AccessTokenString ;
double ExpiryTimeSeconds = 0.0 ;
if ( ResponseObject - > TryGetStringField ( TEXT ( " access_token " ) , AccessTokenString ) & &
ResponseObject - > TryGetNumberField ( TEXT ( " expires_in " ) , ExpiryTimeSeconds ) )
{
UE_LOG ( LogDerivedDataCache , Display ,
TEXT ( " %s: Logged in to HTTP DDC services. Expires in %.0f seconds. " ) , * Domain , ExpiryTimeSeconds ) ;
SetAccessToken ( AccessTokenString , ExpiryTimeSeconds ) ;
return true ;
}
2020-06-23 18:40:00 -04:00
}
}
2022-07-12 15:36:38 -04:00
UE_LOG ( LogDerivedDataCache , Warning , TEXT ( " %s: Failed to log in to HTTP services. Server responded with code %d. " ) , * Domain , Request . GetResponseCode ( ) ) ;
FailedLoginAttempts + + ;
2020-06-23 18:40:00 -04:00
return false ;
}
2022-07-12 15:36:38 -04:00
void FHttpCacheStore : : SetAccessToken ( FStringView Token , double RefreshDelay )
{
if ( RefreshAccessTokenHandle . IsValid ( ) )
{
FTSTicker : : GetCoreTicker ( ) . RemoveTicker ( RefreshAccessTokenHandle ) ;
RefreshAccessTokenHandle . Reset ( ) ;
}
if ( ! Access )
{
Access = MakeUnique < FHttpAccessToken > ( ) ;
}
Access - > SetToken ( Token ) ;
// Schedule a refresh of the token ahead of expiry time (this will not work in commandlets)
constexpr float RefreshGracePeriod = 20.0f ;
if ( RefreshDelay > RefreshGracePeriod & & ! IsRunningCommandlet ( ) )
{
RefreshAccessTokenHandle = FTSTicker : : GetCoreTicker ( ) . AddTicker ( FTickerDelegate : : CreateLambda (
[ this ] ( float DeltaTime )
{
AcquireAccessToken ( ) ;
return false ;
}
) , RefreshDelay - RefreshGracePeriod ) ;
}
// Reset failed login attempts, the service is indeed alive.
FailedLoginAttempts = 0 ;
}
2022-05-31 12:42:17 -04:00
bool FHttpCacheStore : : ShouldRetryOnError ( FHttpRequest : : EResult Result , int64 ResponseCode )
2020-06-23 18:40:00 -04:00
{
2022-05-31 12:42:17 -04:00
if ( Result = = FHttpRequest : : EResult : : FailedTimeout )
2022-04-05 16:43:41 -04:00
{
return true ;
}
2020-06-23 18:40:00 -04:00
// Access token might have expired, request a new token and try again.
if ( ResponseCode = = 401 & & AcquireAccessToken ( ) )
{
return true ;
}
// Too many requests, make a new attempt
if ( ResponseCode = = 429 )
{
return true ;
}
return false ;
}
2022-04-05 16:43:41 -04:00
void FHttpCacheStore : : GetCacheRecordOnlyAsync (
IRequestOwner & Owner ,
const FSharedString & Name ,
2022-04-05 15:17:17 -04:00
const FCacheKey & Key ,
2022-04-05 16:43:41 -04:00
const FCacheRecordPolicy & Policy ,
uint64 UserData ,
FOnGetCacheRecordOnlyComplete & & OnComplete )
2022-04-05 15:17:17 -04:00
{
2022-04-05 16:43:41 -04:00
auto MakeResponse = [ Name = FSharedString ( Name ) , Key , UserData ] ( uint64 BytesReceived , EStatus Status )
{
return FGetCacheRecordOnlyResponse { Name , Key , UserData , BytesReceived , { } , Status } ;
} ;
2022-01-10 13:43:40 -05:00
if ( ! IsUsable ( ) )
{
UE_LOG ( LogDerivedDataCache , VeryVerbose ,
2022-04-05 16:43:41 -04:00
TEXT ( " %s: Skipped get of %s from '%s' because this cache store is not available " ) ,
2022-05-31 22:01:53 -04:00
* Domain , * WriteToString < 96 > ( Key ) , * Name ) ;
2022-04-05 16:43:41 -04:00
return OnComplete ( MakeResponse ( 0 , EStatus : : Error ) ) ;
2022-01-10 13:43:40 -05:00
}
// Skip the request if querying the cache is disabled.
2022-05-31 22:01:53 -04:00
if ( ! EnumHasAnyFlags ( Policy . GetRecordPolicy ( ) , ECachePolicy : : QueryRemote ) )
2022-01-10 13:43:40 -05:00
{
2022-04-05 16:43:41 -04:00
UE_LOG ( LogDerivedDataCache , VeryVerbose , TEXT ( " %s: Skipped get of %s from '%s' due to cache policy " ) ,
2022-05-31 22:01:53 -04:00
* Domain , * WriteToString < 96 > ( Key ) , * Name ) ;
2022-04-05 16:43:41 -04:00
return OnComplete ( MakeResponse ( 0 , EStatus : : Error ) ) ;
2022-01-10 13:43:40 -05:00
}
2022-03-31 10:06:47 -04:00
if ( DebugOptions . ShouldSimulateGetMiss ( Key ) )
2022-01-10 13:43:40 -05:00
{
2022-04-05 16:43:41 -04:00
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " %s: Simulated miss for get of %s from '%s' " ) ,
2022-05-31 22:01:53 -04:00
* Domain , * WriteToString < 96 > ( Key ) , * Name ) ;
2022-04-05 16:43:41 -04:00
return OnComplete ( MakeResponse ( 0 , EStatus : : Error ) ) ;
2022-01-10 13:43:40 -05:00
}
2022-04-05 16:43:41 -04:00
FString Bucket ( Key . Bucket . ToString ( ) ) ;
Bucket . ToLowerInline ( ) ;
2022-02-24 10:24:10 -05:00
2022-04-05 16:43:41 -04:00
TStringBuilder < 256 > RefsUri ;
RefsUri < < " api/v1/refs/ " < < StructuredNamespace < < " / " < < Bucket < < " / " < < Key . Hash ;
2022-05-18 09:58:59 -04:00
FHttpRequestPool * Pool = nullptr ;
2022-04-05 16:43:41 -04:00
FHttpRequest * Request = WaitForHttpRequestForOwner < OperationCategory : : Get > ( Owner , false /* bUnboundedOverflow */ , Pool ) ;
auto OnHttpRequestComplete = [ this , & Owner , Name = FSharedString ( Name ) , Key , UserData , OnComplete = MoveTemp ( OnComplete ) ]
2022-05-31 12:42:17 -04:00
( FHttpRequest : : EResult HttpResult , FHttpRequest * Request )
2022-01-10 13:43:40 -05:00
{
2022-04-05 16:43:41 -04:00
TRACE_CPUPROFILER_EVENT_SCOPE ( HttpDDC_GetCacheRecordOnlyAsync_OnHttpRequestComplete ) ;
2022-01-10 13:43:40 -05:00
2022-04-05 16:43:41 -04:00
int64 ResponseCode = Request - > GetResponseCode ( ) ;
if ( FHttpRequest : : IsSuccessResponse ( ResponseCode ) )
2022-01-10 13:43:40 -05:00
{
2022-04-05 16:43:41 -04:00
FSharedBuffer ResponseBuffer = Request - > MoveResponseBufferToShared ( ) ;
if ( ValidateCompactBinary ( ResponseBuffer , ECbValidateMode : : Default ) ! = ECbValidateError : : None )
2022-02-24 10:24:10 -05:00
{
2022-04-05 16:43:41 -04:00
UE_LOG ( LogDerivedDataCache , Log , TEXT ( " %s: Cache miss with invalid package for %s from '%s' " ) ,
2022-05-31 22:01:53 -04:00
* Domain , * WriteToString < 96 > ( Key ) , * Name ) ;
2022-04-05 16:43:41 -04:00
OnComplete ( { Name , Key , UserData , Request - > GetBytesReceived ( ) , { } , EStatus : : Error } ) ;
return FHttpRequest : : ECompletionBehavior : : Done ;
2022-02-24 10:24:10 -05:00
}
2022-01-10 13:43:40 -05:00
2022-04-05 16:43:41 -04:00
FOptionalCacheRecord Record = FCacheRecord : : Load ( FCbPackage ( FCbObject ( ResponseBuffer ) ) ) ;
if ( Record . IsNull ( ) )
2022-01-10 13:43:40 -05:00
{
2022-04-05 16:43:41 -04:00
UE_LOG ( LogDerivedDataCache , Log , TEXT ( " %s: Cache miss with record load failure for %s from '%s' " ) ,
2022-05-31 22:01:53 -04:00
* Domain , * WriteToString < 96 > ( Key ) , * Name ) ;
2022-04-05 16:43:41 -04:00
OnComplete ( { Name , Key , UserData , Request - > GetBytesReceived ( ) , { } , EStatus : : Error } ) ;
return FHttpRequest : : ECompletionBehavior : : Done ;
2022-01-10 13:43:40 -05:00
}
2022-04-05 16:43:41 -04:00
OnComplete ( { Name , Key , UserData , Request - > GetBytesReceived ( ) , MoveTemp ( Record ) , EStatus : : Ok } ) ;
return FHttpRequest : : ECompletionBehavior : : Done ;
2022-01-10 13:43:40 -05:00
}
2022-04-05 16:43:41 -04:00
if ( ! ShouldAbortForShutdown ( ) & & ! Owner . IsCanceled ( ) & & ShouldRetryOnError ( HttpResult , ResponseCode ) & & ( ( Request - > GetAttempts ( ) + 1 ) < UE_HTTPDDC_MAX_ATTEMPTS ) )
2022-01-14 10:53:17 -05:00
{
2022-04-05 16:43:41 -04:00
return FHttpRequest : : ECompletionBehavior : : Retry ;
2022-01-14 10:53:17 -05:00
}
2022-04-05 16:43:41 -04:00
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " %s: Cache miss with missing package for %s from '%s' " ) ,
2022-05-31 22:01:53 -04:00
* Domain , * WriteToString < 96 > ( Key ) , * Name ) ;
2022-04-05 16:43:41 -04:00
OnComplete ( { Name , Key , UserData , Request - > GetBytesReceived ( ) , { } , EStatus : : Error } ) ;
return FHttpRequest : : ECompletionBehavior : : Done ;
} ;
2022-06-30 11:53:43 -04:00
Request - > EnqueueAsyncDownload ( Owner , Pool , * RefsUri , MoveTemp ( OnHttpRequestComplete ) , EHttpMediaType : : CbObject , { 401 , 404 } ) ;
2022-01-10 13:43:40 -05:00
}
2022-04-05 16:43:41 -04:00
void FHttpCacheStore : : PutCacheRecordAsync (
IRequestOwner & Owner ,
const FSharedString & Name ,
const FCacheRecord & Record ,
const FCacheRecordPolicy & Policy ,
uint64 UserData ,
TUniqueFunction < void ( FCachePutResponse & & Response , uint64 BytesSent ) > & & OnComplete )
2022-04-05 14:18:39 -04:00
{
2022-04-05 16:43:41 -04:00
const FCacheKey & Key = Record . GetKey ( ) ;
auto MakeResponse = [ Name = FSharedString ( Name ) , Key = FCacheKey ( Key ) , UserData ] ( EStatus Status )
{
return FCachePutResponse { Name , Key , UserData , Status } ;
} ;
2022-05-31 22:01:53 -04:00
if ( bReadOnly )
2022-04-05 14:18:39 -04:00
{
UE_LOG ( LogDerivedDataCache , VeryVerbose ,
2022-04-05 16:43:41 -04:00
TEXT ( " %s: Skipped put of %s from '%s' because this cache store is read-only " ) ,
2022-05-31 22:01:53 -04:00
* Domain , * WriteToString < 96 > ( Key ) , * Name ) ;
2022-04-05 16:43:41 -04:00
return OnComplete ( MakeResponse ( EStatus : : Error ) , 0 ) ;
2022-04-05 14:18:39 -04:00
}
// Skip the request if storing to the cache is disabled.
2022-04-05 16:43:41 -04:00
const ECachePolicy RecordPolicy = Policy . GetRecordPolicy ( ) ;
2022-05-31 22:01:53 -04:00
if ( ! EnumHasAnyFlags ( RecordPolicy , ECachePolicy : : StoreRemote ) )
2022-04-05 14:18:39 -04:00
{
2022-04-05 16:43:41 -04:00
UE_LOG ( LogDerivedDataCache , VeryVerbose , TEXT ( " %s: Skipped put of %s from '%s' due to cache policy " ) ,
2022-05-31 22:01:53 -04:00
* Domain , * WriteToString < 96 > ( Key ) , * Name ) ;
2022-04-05 16:43:41 -04:00
return OnComplete ( MakeResponse ( EStatus : : Error ) , 0 ) ;
2022-04-05 14:18:39 -04:00
}
if ( DebugOptions . ShouldSimulatePutMiss ( Key ) )
{
2022-04-05 16:43:41 -04:00
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " %s: Simulated miss for put of %s from '%s' " ) ,
2022-05-31 22:01:53 -04:00
* Domain , * WriteToString < 96 > ( Key ) , * Name ) ;
2022-04-05 16:43:41 -04:00
return OnComplete ( MakeResponse ( EStatus : : Error ) , 0 ) ;
2022-04-05 14:18:39 -04:00
}
// TODO: Jupiter currently always overwrites. It doesn't have a "write if not present" feature (for records or attachments),
// but would require one to implement all policy correctly.
FString Bucket ( Key . Bucket . ToString ( ) ) ;
Bucket . ToLowerInline ( ) ;
2022-02-02 07:35:19 -05:00
2022-04-05 16:43:41 -04:00
FCbPackage Package = Record . Save ( ) ;
FPutPackageOp : : PutPackage ( * this , Owner , Name , Key , MoveTemp ( Package ) , Policy , UserData , [ MakeResponse = MoveTemp ( MakeResponse ) , OnComplete = MoveTemp ( OnComplete ) ] ( FPutPackageOp : : FCachePutPackageResponse & & Response )
{
OnComplete ( MakeResponse ( Response . Status ) , Response . BytesSent ) ;
} ) ;
}
void FHttpCacheStore : : PutCacheValueAsync (
IRequestOwner & Owner ,
const FSharedString & Name ,
const FCacheKey & Key ,
const FValue & Value ,
const ECachePolicy Policy ,
uint64 UserData ,
TUniqueFunction < void ( FCachePutValueResponse & & Response , uint64 BytesSent ) > & & OnComplete )
{
auto MakeResponse = [ Name = FSharedString ( Name ) , Key = FCacheKey ( Key ) , UserData ] ( EStatus Status )
{
return FCachePutValueResponse { Name , Key , UserData , Status } ;
} ;
2022-05-31 22:01:53 -04:00
if ( bReadOnly )
2022-04-05 16:43:41 -04:00
{
UE_LOG ( LogDerivedDataCache , VeryVerbose ,
TEXT ( " %s: Skipped put of %s from '%s' because this cache store is read-only " ) ,
2022-05-31 22:01:53 -04:00
* Domain , * WriteToString < 96 > ( Key ) , * Name ) ;
2022-04-05 16:43:41 -04:00
return OnComplete ( MakeResponse ( EStatus : : Error ) , 0 ) ;
}
// Skip the request if storing to the cache is disabled.
2022-05-31 22:01:53 -04:00
if ( ! EnumHasAnyFlags ( Policy , ECachePolicy : : StoreRemote ) )
2022-04-05 16:43:41 -04:00
{
UE_LOG ( LogDerivedDataCache , VeryVerbose , TEXT ( " %s: Skipped put of %s from '%s' due to cache policy " ) ,
2022-05-31 22:01:53 -04:00
* Domain , * WriteToString < 96 > ( Key ) , * Name ) ;
2022-04-05 16:43:41 -04:00
return OnComplete ( MakeResponse ( EStatus : : Error ) , 0 ) ;
}
if ( DebugOptions . ShouldSimulatePutMiss ( Key ) )
{
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " %s: Simulated miss for put of %s from '%s' " ) ,
2022-05-31 22:01:53 -04:00
* Domain , * WriteToString < 96 > ( Key ) , * Name ) ;
2022-04-05 16:43:41 -04:00
return OnComplete ( MakeResponse ( EStatus : : Error ) , 0 ) ;
}
// TODO: Jupiter currently always overwrites. It doesn't have a "write if not present" feature (for records or attachments),
// but would require one to implement all policy correctly.
FString Bucket ( Key . Bucket . ToString ( ) ) ;
Bucket . ToLowerInline ( ) ;
2022-04-05 15:17:17 -04:00
2022-02-02 07:35:19 -05:00
FCbWriter Writer ;
Writer . BeginObject ( ) ;
Writer . AddBinaryAttachment ( " RawHash " , Value . GetRawHash ( ) ) ;
Writer . AddInteger ( " RawSize " , Value . GetRawSize ( ) ) ;
Writer . EndObject ( ) ;
FCbPackage Package ( Writer . Save ( ) . AsObject ( ) ) ;
2022-04-05 16:43:41 -04:00
Package . AddAttachment ( FCbAttachment ( Value . GetData ( ) ) ) ;
2022-02-02 07:35:19 -05:00
2022-04-05 16:43:41 -04:00
FPutPackageOp : : PutPackage ( * this , Owner , Name , Key , MoveTemp ( Package ) , Policy , UserData , [ MakeResponse = MoveTemp ( MakeResponse ) , OnComplete = MoveTemp ( OnComplete ) ] ( FPutPackageOp : : FCachePutPackageResponse & & Response )
2022-02-22 13:55:39 -05:00
{
2022-04-05 16:43:41 -04:00
OnComplete ( MakeResponse ( Response . Status ) , Response . BytesSent ) ;
} ) ;
2022-02-02 07:35:19 -05:00
}
2022-04-05 16:43:41 -04:00
void FHttpCacheStore : : GetCacheValueAsync (
IRequestOwner & Owner ,
FSharedString Name ,
2022-02-02 07:35:19 -05:00
const FCacheKey & Key ,
2022-04-05 16:43:41 -04:00
ECachePolicy Policy ,
uint64 UserData ,
FOnCacheGetValueComplete & & OnComplete )
2022-02-02 07:35:19 -05:00
{
if ( ! IsUsable ( ) )
{
UE_LOG ( LogDerivedDataCache , VeryVerbose ,
2022-04-05 16:43:41 -04:00
TEXT ( " %s: Skipped get of %s from '%s' because this cache store is not available " ) ,
2022-05-31 22:01:53 -04:00
* Domain , * WriteToString < 96 > ( Key ) , * Name ) ;
2022-04-05 16:43:41 -04:00
OnComplete ( { Name , Key , { } , UserData , EStatus : : Error } ) ;
return ;
2022-02-02 07:35:19 -05:00
}
// Skip the request if querying the cache is disabled.
2022-05-31 22:01:53 -04:00
if ( ! EnumHasAnyFlags ( Policy , ECachePolicy : : QueryRemote ) )
2022-02-02 07:35:19 -05:00
{
2022-04-05 16:43:41 -04:00
UE_LOG ( LogDerivedDataCache , VeryVerbose , TEXT ( " %s: Skipped get of %s from '%s' due to cache policy " ) ,
2022-05-31 22:01:53 -04:00
* Domain , * WriteToString < 96 > ( Key ) , * Name ) ;
2022-04-05 16:43:41 -04:00
OnComplete ( { Name , Key , { } , UserData , EStatus : : Error } ) ;
return ;
2022-02-02 07:35:19 -05:00
}
2022-03-31 10:06:47 -04:00
if ( DebugOptions . ShouldSimulateGetMiss ( Key ) )
2022-02-02 07:35:19 -05:00
{
2022-04-05 16:43:41 -04:00
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " %s: Simulated miss for get of %s from '%s' " ) ,
2022-05-31 22:01:53 -04:00
* Domain , * WriteToString < 96 > ( Key ) , * Name ) ;
2022-04-05 16:43:41 -04:00
OnComplete ( { Name , Key , { } , UserData , EStatus : : Error } ) ;
return ;
2022-02-02 07:35:19 -05:00
}
2022-02-24 10:24:10 -05:00
const bool bSkipData = EnumHasAnyFlags ( Policy , ECachePolicy : : SkipData ) ;
2022-02-02 07:35:19 -05:00
FString Bucket ( Key . Bucket . ToString ( ) ) ;
Bucket . ToLowerInline ( ) ;
TStringBuilder < 256 > RefsUri ;
RefsUri < < " api/v1/refs/ " < < StructuredNamespace < < " / " < < Bucket < < " / " < < Key . Hash ;
2022-05-18 09:58:59 -04:00
FHttpRequestPool * Pool = nullptr ;
2022-04-05 16:43:41 -04:00
FHttpRequest * Request = WaitForHttpRequestForOwner < OperationCategory : : Get > ( Owner , false /* bUnboundedOverflow */ , Pool ) ;
2022-04-05 15:17:17 -04:00
if ( bSkipData )
{
2022-05-31 12:42:17 -04:00
Request - > AddHeader ( TEXT ( " Accept " ) , TEXT ( " application/x-ue-cb " ) ) ;
2022-04-05 15:17:17 -04:00
}
else
{
2022-05-31 12:42:17 -04:00
Request - > AddHeader ( TEXT ( " Accept " ) , TEXT ( " application/x-jupiter-inline " ) ) ;
2022-04-05 16:43:41 -04:00
}
2022-05-31 12:42:17 -04:00
auto OnHttpRequestComplete = [ this , & Owner , Name , Key , UserData , bSkipData , OnComplete = MoveTemp ( OnComplete ) ] ( FHttpRequest : : EResult HttpResult , FHttpRequest * Request )
2022-04-05 16:43:41 -04:00
{
TRACE_CPUPROFILER_EVENT_SCOPE ( HttpDDC_GetCacheValueAsync_OnHttpRequestComplete ) ;
int64 ResponseCode = Request - > GetResponseCode ( ) ;
if ( FHttpRequest : : IsSuccessResponse ( ResponseCode ) )
2022-04-05 15:17:17 -04:00
{
2022-04-05 16:43:41 -04:00
FValue ResultValue ;
FSharedBuffer ResponseBuffer = Request - > MoveResponseBufferToShared ( ) ;
if ( bSkipData )
2022-03-02 17:30:48 -05:00
{
2022-04-05 16:43:41 -04:00
if ( ValidateCompactBinary ( ResponseBuffer , ECbValidateMode : : Default ) ! = ECbValidateError : : None )
2022-03-02 17:30:48 -05:00
{
2022-04-05 16:43:41 -04:00
UE_LOG ( LogDerivedDataCache , Display , TEXT ( " %s: Cache miss with invalid package for %s from '%s' " ) ,
2022-05-31 22:01:53 -04:00
* Domain , * WriteToString < 96 > ( Key ) , * Name ) ;
2022-04-05 16:43:41 -04:00
OnComplete ( { Name , Key , { } , UserData , EStatus : : Error } ) ;
return FHttpRequest : : ECompletionBehavior : : Done ;
2022-03-02 17:30:48 -05:00
}
2022-04-05 16:43:41 -04:00
const FCbObjectView Object = FCbObject ( ResponseBuffer ) ;
const FIoHash RawHash = Object [ " RawHash " ] . AsHash ( ) ;
const uint64 RawSize = Object [ " RawSize " ] . AsUInt64 ( MAX_uint64 ) ;
if ( RawHash . IsZero ( ) | | RawSize = = MAX_uint64 )
{
UE_LOG ( LogDerivedDataCache , Display , TEXT ( " %s: Cache miss with invalid value for %s from '%' " ) ,
2022-05-31 22:01:53 -04:00
* Domain , * WriteToString < 96 > ( Key ) , * Name ) ;
2022-04-05 16:43:41 -04:00
OnComplete ( { Name , Key , { } , UserData , EStatus : : Error } ) ;
return FHttpRequest : : ECompletionBehavior : : Done ;
}
ResultValue = FValue ( RawHash , RawSize ) ;
2022-01-10 13:43:40 -05:00
}
else
{
2022-04-05 16:43:41 -04:00
FCompressedBuffer CompressedBuffer = FCompressedBuffer : : FromCompressed ( ResponseBuffer ) ;
if ( ! CompressedBuffer )
2022-02-24 10:24:10 -05:00
{
2022-04-05 16:43:41 -04:00
FString ReceivedHashStr ;
if ( Request - > GetHeader ( " X-Jupiter-InlinePayloadHash " , ReceivedHashStr ) )
2022-02-24 10:24:10 -05:00
{
2022-04-05 16:43:41 -04:00
FIoHash ReceivedHash ( ReceivedHashStr ) ;
FIoHash ComputedHash = FIoHash : : HashBuffer ( ResponseBuffer . GetView ( ) ) ;
if ( ReceivedHash = = ComputedHash )
2022-02-24 10:24:10 -05:00
{
2022-04-05 16:43:41 -04:00
CompressedBuffer = FCompressedBuffer : : Compress ( ResponseBuffer ) ;
2022-02-24 10:24:10 -05:00
}
}
}
2022-04-05 16:43:41 -04:00
if ( ! CompressedBuffer )
{
UE_LOG ( LogDerivedDataCache , Display , TEXT ( " %s: Cache miss with invalid package for %s from '%s' " ) ,
2022-05-31 22:01:53 -04:00
* Domain , * WriteToString < 96 > ( Key ) , * Name ) ;
2022-04-05 16:43:41 -04:00
OnComplete ( { Name , Key , { } , UserData , EStatus : : Error } ) ;
return FHttpRequest : : ECompletionBehavior : : Done ;
}
ResultValue = FValue ( CompressedBuffer ) ;
2022-02-24 10:24:10 -05:00
}
2022-04-05 16:43:41 -04:00
OnComplete ( { Name , Key , ResultValue , UserData , EStatus : : Ok } ) ;
return FHttpRequest : : ECompletionBehavior : : Done ;
2022-01-10 13:43:40 -05:00
}
2022-02-24 10:24:10 -05:00
2022-06-29 21:31:09 -04:00
if ( ! ShouldAbortForShutdown ( ) & & ! Owner . IsCanceled ( ) & & ShouldRetryOnError ( HttpResult , ResponseCode ) & & ( ( Request - > GetAttempts ( ) + 1 ) < UE_HTTPDDC_MAX_ATTEMPTS ) )
{
2022-04-05 16:43:41 -04:00
return FHttpRequest : : ECompletionBehavior : : Retry ;
2022-06-29 21:31:09 -04:00
}
2022-04-05 16:43:41 -04:00
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " %s: Cache miss with failed HTTP request for %s from '%s' " ) ,
2022-05-31 22:01:53 -04:00
* Domain , * WriteToString < 96 > ( Key ) , * Name ) ;
2022-04-05 16:43:41 -04:00
OnComplete ( { Name , Key , { } , UserData , EStatus : : Error } ) ;
return FHttpRequest : : ECompletionBehavior : : Done ;
} ;
2022-06-30 11:53:43 -04:00
Request - > EnqueueAsyncDownload ( Owner , Pool , * RefsUri , MoveTemp ( OnHttpRequestComplete ) , EHttpMediaType : : UnspecifiedContentType , { 401 , 404 } ) ;
2022-01-10 13:43:40 -05:00
}
2022-04-05 16:43:41 -04:00
void FHttpCacheStore : : GetCacheRecordAsync (
IRequestOwner & Owner ,
const FSharedString & Name ,
const FCacheKey & Key ,
const FCacheRecordPolicy & Policy ,
uint64 UserData ,
TUniqueFunction < void ( FCacheGetResponse & & Response , uint64 BytesReceived ) > & & OnComplete )
{
FGetRecordOp : : GetRecord ( * this , Owner , Name , Key , Policy , UserData , MoveTemp ( OnComplete ) ) ;
}
void FHttpCacheStore : : RefCachedDataProbablyExistsBatchAsync (
IRequestOwner & Owner ,
TConstArrayView < FCacheGetValueRequest > ValueRefs ,
FOnCacheGetValueComplete & & OnComplete )
2022-03-02 17:30:48 -05:00
{
if ( ValueRefs . IsEmpty ( ) )
{
2022-04-05 16:43:41 -04:00
return ;
2022-03-02 17:30:48 -05:00
}
2022-04-05 16:43:41 -04:00
if ( ! IsUsable ( ) )
2022-03-02 17:30:48 -05:00
{
2022-04-05 16:43:41 -04:00
for ( const FCacheGetValueRequest & ValueRef : ValueRefs )
2022-06-29 21:31:09 -04:00
{
2022-04-05 16:43:41 -04:00
UE_LOG ( LogDerivedDataCache , VeryVerbose ,
TEXT ( " %s: Skipped exists check of %s from '%s' because this cache store is not available " ) ,
2022-05-31 22:01:53 -04:00
* Domain , * WriteToString < 96 > ( ValueRef . Key ) , * ValueRef . Name ) ;
2022-04-05 16:43:41 -04:00
OnComplete ( ValueRef . MakeResponse ( EStatus : : Error ) ) ;
2022-06-29 21:31:09 -04:00
}
2022-04-05 16:43:41 -04:00
return ;
2022-03-02 17:30:48 -05:00
}
TStringBuilder < 256 > RefsUri ;
RefsUri < < " api/v1/refs/ " < < StructuredNamespace ;
FCbWriter RequestWriter ;
RequestWriter . BeginObject ( ) ;
2022-04-25 11:31:36 -04:00
RequestWriter . BeginArray ( ANSITEXTVIEW ( " ops " ) ) ;
2022-03-02 17:30:48 -05:00
uint32 OpIndex = 0 ;
2022-04-05 16:43:41 -04:00
for ( const FCacheGetValueRequest & ValueRef : ValueRefs )
2022-03-02 17:30:48 -05:00
{
RequestWriter . BeginObject ( ) ;
2022-04-25 11:31:36 -04:00
RequestWriter . AddInteger ( ANSITEXTVIEW ( " opId " ) , OpIndex ) ;
RequestWriter . AddString ( ANSITEXTVIEW ( " op " ) , ANSITEXTVIEW ( " GET " ) ) ;
2022-04-05 16:43:41 -04:00
FCacheKey Key = ValueRef . Key ;
2022-03-02 17:30:48 -05:00
FString Bucket ( Key . Bucket . ToString ( ) ) ;
Bucket . ToLowerInline ( ) ;
2022-04-25 11:31:36 -04:00
RequestWriter . AddString ( ANSITEXTVIEW ( " bucket " ) , Bucket ) ;
RequestWriter . AddString ( ANSITEXTVIEW ( " key " ) , LexToString ( Key . Hash ) ) ;
RequestWriter . AddBool ( ANSITEXTVIEW ( " resolveAttachments " ) , true ) ;
2022-03-02 17:30:48 -05:00
RequestWriter . EndObject ( ) ;
+ + OpIndex ;
}
RequestWriter . EndArray ( ) ;
RequestWriter . EndObject ( ) ;
FCbFieldIterator RequestFields = RequestWriter . Save ( ) ;
2022-05-18 09:58:59 -04:00
FHttpRequestPool * Pool = nullptr ;
2022-04-05 16:43:41 -04:00
FHttpRequest * Request = WaitForHttpRequestForOwner < OperationCategory : : Get > ( Owner , false /* bUnboundedOverflow */ , Pool ) ;
2022-05-31 12:42:17 -04:00
Request - > AddHeader ( TEXT ( " Accept " ) , TEXT ( " application/x-ue-cb " ) ) ;
2022-04-05 16:43:41 -04:00
2022-05-31 12:42:17 -04:00
auto OnHttpRequestComplete = [ this , & Owner , ValueRefs = TArray < FCacheGetValueRequest > ( ValueRefs ) , OnComplete = MoveTemp ( OnComplete ) ] ( FHttpRequest : : EResult HttpResult , FHttpRequest * Request )
2022-03-02 17:30:48 -05:00
{
2022-04-05 16:43:41 -04:00
TRACE_CPUPROFILER_EVENT_SCOPE ( HttpDDC_RefCachedDataProbablyExistsBatchAsync_OnHttpRequestComplete ) ;
2022-03-02 17:30:48 -05:00
2022-04-05 16:43:41 -04:00
int64 ResponseCode = Request - > GetResponseCode ( ) ;
2022-03-02 17:30:48 -05:00
if ( FHttpRequest : : IsSuccessResponse ( ResponseCode ) )
{
FMemoryView ResponseView = MakeMemoryView ( Request - > GetResponseBuffer ( ) . GetData ( ) , Request - > GetResponseBuffer ( ) . Num ( ) ) ;
if ( ValidateCompactBinary ( ResponseView , ECbValidateMode : : Default ) ! = ECbValidateError : : None )
{
2022-04-05 16:43:41 -04:00
for ( const FCacheGetValueRequest & ValueRef : ValueRefs )
{
2022-06-29 21:31:09 -04:00
UE_LOG ( LogDerivedDataCache , Log ,
TEXT ( " %s: Cache exists returned invalid results. " ) ,
* Domain ) ;
OnComplete ( ValueRef . MakeResponse ( EStatus : : Error ) ) ;
2022-04-05 16:43:41 -04:00
}
return FHttpRequest : : ECompletionBehavior : : Done ;
2022-03-02 17:30:48 -05:00
}
const FCbObjectView ResponseObject = FCbObjectView ( Request - > GetResponseBuffer ( ) . GetData ( ) ) ;
2022-04-25 11:31:36 -04:00
FCbArrayView ResultsArrayView = ResponseObject [ ANSITEXTVIEW ( " results " ) ] . AsArrayView ( ) ;
2022-03-02 17:30:48 -05:00
if ( ResultsArrayView . Num ( ) ! = ValueRefs . Num ( ) )
{
2022-04-05 16:43:41 -04:00
for ( const FCacheGetValueRequest & ValueRef : ValueRefs )
{
2022-05-11 09:10:24 -04:00
UE_LOG ( LogDerivedDataCache , Log ,
TEXT ( " %s: Cache exists returned unexpected quantity of results (expected %d, got %d). " ) ,
2022-05-31 22:01:53 -04:00
* Domain , ValueRefs . Num ( ) , ResultsArrayView . Num ( ) ) ;
2022-05-11 09:10:24 -04:00
OnComplete ( ValueRef . MakeResponse ( EStatus : : Error ) ) ;
2022-04-05 16:43:41 -04:00
}
return FHttpRequest : : ECompletionBehavior : : Done ;
2022-03-02 17:30:48 -05:00
}
2022-04-05 16:43:41 -04:00
2022-03-02 17:30:48 -05:00
for ( FCbFieldView ResultFieldView : ResultsArrayView )
{
FCbObjectView ResultObjectView = ResultFieldView . AsObjectView ( ) ;
2022-04-25 11:31:36 -04:00
uint32 OpId = ResultObjectView [ ANSITEXTVIEW ( " opId " ) ] . AsUInt32 ( ) ;
FCbObjectView ResponseObjectView = ResultObjectView [ ANSITEXTVIEW ( " response " ) ] . AsObjectView ( ) ;
int32 StatusCode = ResultObjectView [ ANSITEXTVIEW ( " statusCode " ) ] . AsInt32 ( ) ;
2022-03-02 17:30:48 -05:00
2022-04-05 16:43:41 -04:00
if ( OpId > = ( uint32 ) ValueRefs . Num ( ) )
2022-03-02 17:30:48 -05:00
{
2022-04-05 16:43:41 -04:00
UE_LOG ( LogDerivedDataCache , Display , TEXT ( " %s: Encountered invalid opId %d while querying %d values " ) ,
2022-05-31 22:01:53 -04:00
* Domain , OpId , ValueRefs . Num ( ) ) ;
2022-03-02 17:30:48 -05:00
continue ;
}
2022-04-05 16:43:41 -04:00
const FCacheGetValueRequest & ValueRef = ValueRefs [ OpId ] ;
2022-03-02 17:30:48 -05:00
if ( ! FHttpRequest : : IsSuccessResponse ( StatusCode ) )
{
2022-04-05 16:43:41 -04:00
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " %s: Cache miss with unsuccessful response code %d for %s from '%s' " ) ,
2022-05-31 22:01:53 -04:00
* Domain , StatusCode , * WriteToString < 96 > ( ValueRef . Key ) , * ValueRef . Name ) ;
2022-04-05 16:43:41 -04:00
OnComplete ( ValueRef . MakeResponse ( EStatus : : Error ) ) ;
continue ;
}
2022-05-31 22:01:53 -04:00
if ( ! EnumHasAnyFlags ( ValueRef . Policy , ECachePolicy : : QueryRemote ) )
2022-04-05 16:43:41 -04:00
{
UE_LOG ( LogDerivedDataCache , VeryVerbose , TEXT ( " %s: Skipped exists check of %s from '%s' due to cache policy " ) ,
2022-05-31 22:01:53 -04:00
* Domain , * WriteToString < 96 > ( ValueRef . Key ) , * ValueRef . Name ) ;
2022-04-05 16:43:41 -04:00
OnComplete ( ValueRef . MakeResponse ( EStatus : : Error ) ) ;
2022-03-02 17:30:48 -05:00
continue ;
}
2022-04-25 11:31:36 -04:00
const FIoHash RawHash = ResponseObjectView [ ANSITEXTVIEW ( " RawHash " ) ] . AsHash ( ) ;
const uint64 RawSize = ResponseObjectView [ ANSITEXTVIEW ( " RawSize " ) ] . AsUInt64 ( MAX_uint64 ) ;
2022-03-02 17:30:48 -05:00
if ( RawHash . IsZero ( ) | | RawSize = = MAX_uint64 )
{
2022-04-05 16:43:41 -04:00
UE_LOG ( LogDerivedDataCache , Display , TEXT ( " %s: Cache miss with invalid value for %s from '%s' " ) ,
2022-05-31 22:01:53 -04:00
* Domain , * WriteToString < 96 > ( ValueRef . Key ) , * ValueRef . Name ) ;
2022-04-05 16:43:41 -04:00
OnComplete ( ValueRef . MakeResponse ( EStatus : : Error ) ) ;
2022-03-02 17:30:48 -05:00
continue ;
}
2022-04-05 16:43:41 -04:00
OnComplete ( { ValueRef . Name , ValueRef . Key , FValue ( RawHash , RawSize ) , ValueRef . UserData , EStatus : : Ok } ) ;
2022-03-02 17:30:48 -05:00
}
2022-04-05 16:43:41 -04:00
return FHttpRequest : : ECompletionBehavior : : Done ;
2022-03-02 17:30:48 -05:00
}
2022-04-05 16:43:41 -04:00
if ( ! ShouldAbortForShutdown ( ) & & ! Owner . IsCanceled ( ) & & ShouldRetryOnError ( HttpResult , ResponseCode ) & & ( ( Request - > GetAttempts ( ) + 1 ) < UE_HTTPDDC_MAX_ATTEMPTS ) )
{
return FHttpRequest : : ECompletionBehavior : : Retry ;
}
for ( const FCacheGetValueRequest & ValueRef : ValueRefs )
{
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " %s: Cache miss with failed HTTP request for %s from '%s' " ) ,
2022-05-31 22:01:53 -04:00
* Domain , * WriteToString < 96 > ( ValueRef . Key ) , * ValueRef . Name ) ;
2022-04-05 16:43:41 -04:00
OnComplete ( ValueRef . MakeResponse ( EStatus : : Error ) ) ;
}
return FHttpRequest : : ECompletionBehavior : : Done ;
} ;
2022-06-30 11:53:43 -04:00
Request - > EnqueueAsyncPost ( Owner , Pool , * RefsUri , FCompositeBuffer ( RequestFields . GetOuterBuffer ( ) ) , MoveTemp ( OnHttpRequestComplete ) , EHttpMediaType : : CbObject ) ;
2022-03-02 17:30:48 -05:00
}
2022-05-31 22:01:53 -04:00
void FHttpCacheStore : : LegacyStats ( FDerivedDataCacheStatsNode & OutNode )
2020-06-23 18:40:00 -04:00
{
2022-05-31 22:01:53 -04:00
OutNode = { TEXT ( " Horde Storage " ) , FString : : Printf ( TEXT ( " %s (%s) " ) , * Domain , * Namespace ) , /*bIsLocal*/ false } ;
OutNode . UsageStats . Add ( TEXT ( " " ) , UsageStats ) ;
2020-06-23 18:40:00 -04:00
}
2022-02-14 14:43:39 -05:00
void FHttpCacheStore : : Put (
2021-12-13 13:32:28 -05:00
const TConstArrayView < FCachePutRequest > Requests ,
2021-08-04 18:08:50 -04:00
IRequestOwner & Owner ,
2021-04-28 16:22:18 -04:00
FOnCachePutComplete & & OnComplete )
{
2022-04-05 16:43:41 -04:00
TRACE_CPUPROFILER_EVENT_SCOPE ( HttpDDC_Put ) ;
FRequestBarrier Barrier ( Owner ) ;
TRefCountedUniqueFunction < FOnCachePutComplete > * CompletionFunction = new TRefCountedUniqueFunction < FOnCachePutComplete > ( MoveTemp ( OnComplete ) ) ;
TRefCountPtr < TRefCountedUniqueFunction < FOnCachePutComplete > > BatchOnCompleteRef ( CompletionFunction ) ;
2022-01-10 13:43:40 -05:00
for ( const FCachePutRequest & Request : Requests )
2021-04-28 16:22:18 -04:00
{
2022-04-05 16:43:41 -04:00
PutCacheRecordAsync ( Owner , Request . Name , Request . Record , Request . Policy , Request . UserData , [ COOK_STAT ( Timer = UsageStats . TimePut ( ) , ) OnCompletePtr = TRefCountPtr < TRefCountedUniqueFunction < FOnCachePutComplete > > ( CompletionFunction ) ] ( FCachePutResponse & & Response , uint64 BytesSent ) mutable
2021-04-28 16:22:18 -04:00
{
2022-04-05 16:43:41 -04:00
TRACE_COUNTER_ADD ( HttpDDC_BytesSent , BytesSent ) ;
if ( Response . Status = = EStatus : : Ok )
{
COOK_STAT ( if ( BytesSent ) { Timer . AddHit ( BytesSent ) ; } ) ;
}
OnCompletePtr - > GetFunction ( ) ( MoveTemp ( Response ) ) ;
} ) ;
2021-04-28 16:22:18 -04:00
}
}
2022-02-14 14:43:39 -05:00
void FHttpCacheStore : : Get (
2021-12-13 13:32:28 -05:00
const TConstArrayView < FCacheGetRequest > Requests ,
2021-08-04 18:08:50 -04:00
IRequestOwner & Owner ,
2021-04-28 16:22:18 -04:00
FOnCacheGetComplete & & OnComplete )
{
2022-04-05 16:43:41 -04:00
TRACE_CPUPROFILER_EVENT_SCOPE ( HttpDDC_Get ) ;
FRequestBarrier Barrier ( Owner ) ;
TRefCountedUniqueFunction < FOnCacheGetComplete > * CompletionFunction = new TRefCountedUniqueFunction < FOnCacheGetComplete > ( MoveTemp ( OnComplete ) ) ;
TRefCountPtr < TRefCountedUniqueFunction < FOnCacheGetComplete > > BatchOnCompleteRef ( CompletionFunction ) ;
2022-01-10 13:43:40 -05:00
for ( const FCacheGetRequest & Request : Requests )
2021-04-28 16:22:18 -04:00
{
2022-04-05 16:43:41 -04:00
GetCacheRecordAsync ( Owner , Request . Name , Request . Key , Request . Policy , Request . UserData , [ COOK_STAT ( Timer = UsageStats . TimePut ( ) , ) OnCompletePtr = TRefCountPtr < TRefCountedUniqueFunction < FOnCacheGetComplete > > ( CompletionFunction ) ] ( FCacheGetResponse & & Response , uint64 BytesReceived ) mutable
2021-04-28 16:22:18 -04:00
{
2022-04-05 16:43:41 -04:00
TRACE_COUNTER_ADD ( HttpDDC_BytesReceived , BytesReceived ) ;
if ( Response . Status = = EStatus : : Ok )
{
COOK_STAT ( Timer . AddHit ( BytesReceived ) ; ) ;
}
OnCompletePtr - > GetFunction ( ) ( MoveTemp ( Response ) ) ;
} ) ;
2021-04-28 16:22:18 -04:00
}
}
2022-02-14 14:43:39 -05:00
void FHttpCacheStore : : PutValue (
2022-02-02 07:35:19 -05:00
const TConstArrayView < FCachePutValueRequest > Requests ,
IRequestOwner & Owner ,
FOnCachePutValueComplete & & OnComplete )
{
2022-04-05 16:43:41 -04:00
TRACE_CPUPROFILER_EVENT_SCOPE ( HttpDDC_PutValue ) ;
FRequestBarrier Barrier ( Owner ) ;
TRefCountedUniqueFunction < FOnCachePutValueComplete > * CompletionFunction = new TRefCountedUniqueFunction < FOnCachePutValueComplete > ( MoveTemp ( OnComplete ) ) ;
TRefCountPtr < TRefCountedUniqueFunction < FOnCachePutValueComplete > > BatchOnCompleteRef ( CompletionFunction ) ;
2022-02-02 07:35:19 -05:00
for ( const FCachePutValueRequest & Request : Requests )
{
2022-04-05 16:43:41 -04:00
PutCacheValueAsync ( Owner , Request . Name , Request . Key , Request . Value , Request . Policy , Request . UserData , [ COOK_STAT ( Timer = UsageStats . TimePut ( ) , ) OnCompletePtr = TRefCountPtr < TRefCountedUniqueFunction < FOnCachePutValueComplete > > ( CompletionFunction ) ] ( FCachePutValueResponse & & Response , uint64 BytesSent ) mutable
2022-02-02 07:35:19 -05:00
{
2022-04-05 16:43:41 -04:00
TRACE_COUNTER_ADD ( HttpDDC_BytesSent , BytesSent ) ;
if ( Response . Status = = EStatus : : Ok )
{
COOK_STAT ( if ( BytesSent ) { Timer . AddHit ( BytesSent ) ; } ) ;
}
OnCompletePtr - > GetFunction ( ) ( MoveTemp ( Response ) ) ;
} ) ;
2022-02-02 07:35:19 -05:00
}
}
2022-02-14 14:43:39 -05:00
void FHttpCacheStore : : GetValue (
2022-02-02 07:35:19 -05:00
const TConstArrayView < FCacheGetValueRequest > Requests ,
IRequestOwner & Owner ,
FOnCacheGetValueComplete & & OnComplete )
{
2022-04-05 16:43:41 -04:00
TRACE_CPUPROFILER_EVENT_SCOPE ( HttpDDC_GetValue ) ;
2022-02-24 10:24:10 -05:00
COOK_STAT ( double StartTime = FPlatformTime : : Seconds ( ) ) ;
COOK_STAT ( bool bIsInGameThread = IsInGameThread ( ) ) ;
2022-03-02 17:30:48 -05:00
bool bBatchExistsCandidate = true ;
2022-02-02 07:35:19 -05:00
for ( const FCacheGetValueRequest & Request : Requests )
{
2022-03-02 17:30:48 -05:00
if ( ! EnumHasAnyFlags ( Request . Policy , ECachePolicy : : SkipData ) )
2022-02-02 07:35:19 -05:00
{
2022-03-02 17:30:48 -05:00
bBatchExistsCandidate = false ;
break ;
2022-02-02 07:35:19 -05:00
}
2022-03-02 17:30:48 -05:00
}
if ( bBatchExistsCandidate )
{
2022-04-05 16:43:41 -04:00
RefCachedDataProbablyExistsBatchAsync ( Owner , Requests ,
[ this , COOK_STAT ( StartTime , bIsInGameThread , ) OnComplete = MoveTemp ( OnComplete ) ] ( FCacheGetValueResponse & & Response )
2022-02-02 07:35:19 -05:00
{
2022-04-05 16:43:41 -04:00
if ( Response . Status ! = EStatus : : Ok )
2022-02-24 10:24:10 -05:00
{
COOK_STAT ( UsageStats . GetStats . Accumulate ( FCookStats : : CallStats : : EHitOrMiss : : Miss , FCookStats : : CallStats : : EStatType : : Counter , 1l , bIsInGameThread ) ) ;
2022-04-05 16:43:41 -04:00
OnComplete ( MoveTemp ( Response ) ) ;
2022-02-24 10:24:10 -05:00
}
else
{
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " %s: Cache hit for %s from '%s' " ) ,
2022-05-31 22:01:53 -04:00
* Domain , * WriteToString < 96 > ( Response . Key ) , * Response . Name ) ;
2022-02-24 10:24:10 -05:00
COOK_STAT ( UsageStats . GetStats . Accumulate ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Counter , 1l , bIsInGameThread ) ) ;
2022-04-05 16:43:41 -04:00
OnComplete ( MoveTemp ( Response ) ) ;
2022-03-02 17:30:48 -05:00
}
2022-04-05 16:43:41 -04:00
COOK_STAT ( const int64 CyclesUsed = int64 ( ( FPlatformTime : : Seconds ( ) - StartTime ) / FPlatformTime : : GetSecondsPerCycle ( ) ) ) ;
COOK_STAT ( UsageStats . GetStats . Accumulate ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Cycles , CyclesUsed , bIsInGameThread ) ) ;
} ) ;
2022-03-02 17:30:48 -05:00
}
else
{
2022-04-05 16:43:41 -04:00
FRequestBarrier Barrier ( Owner ) ;
TRefCountedUniqueFunction < FOnCacheGetValueComplete > * CompletionFunction = new TRefCountedUniqueFunction < FOnCacheGetValueComplete > ( MoveTemp ( OnComplete ) ) ;
TRefCountPtr < TRefCountedUniqueFunction < FOnCacheGetValueComplete > > BatchOnCompleteRef ( CompletionFunction ) ;
int64 HitBytes = 0 ;
2022-03-02 17:30:48 -05:00
for ( const FCacheGetValueRequest & Request : Requests )
{
2022-04-05 16:43:41 -04:00
GetCacheValueAsync ( Owner , Request . Name , Request . Key , Request . Policy , Request . UserData ,
[ this , COOK_STAT ( StartTime , bIsInGameThread , ) Policy = Request . Policy , OnCompletePtr = TRefCountPtr < TRefCountedUniqueFunction < FOnCacheGetValueComplete > > ( CompletionFunction ) ] ( FCacheGetValueResponse & & Response )
2022-03-02 17:30:48 -05:00
{
2022-04-05 16:43:41 -04:00
const FOnCacheGetValueComplete & OnComplete = OnCompletePtr - > GetFunction ( ) ;
check ( OnComplete ) ;
if ( Response . Status ! = EStatus : : Ok )
2022-03-02 17:30:48 -05:00
{
COOK_STAT ( UsageStats . GetStats . Accumulate ( FCookStats : : CallStats : : EHitOrMiss : : Miss , FCookStats : : CallStats : : EStatType : : Counter , 1l , bIsInGameThread ) ) ;
2022-04-05 16:43:41 -04:00
OnComplete ( MoveTemp ( Response ) ) ;
2022-03-02 17:30:48 -05:00
}
else
{
2022-04-05 16:43:41 -04:00
if ( ! IsValueDataReady ( Response . Value , Policy ) & & ! EnumHasAnyFlags ( Policy , ECachePolicy : : SkipData ) )
{
// With inline fetching, expect we will always have a value we can use. Even SkipData/Exists can rely on the blob existing if the ref is reported to exist.
UE_LOG ( LogDerivedDataCache , Log , TEXT ( " %s: Cache miss due to inlining failure for %s from '%s' " ) ,
2022-05-31 22:01:53 -04:00
* Domain , * WriteToString < 96 > ( Response . Key ) , * Response . Name ) ;
2022-04-05 16:43:41 -04:00
COOK_STAT ( UsageStats . GetStats . Accumulate ( FCookStats : : CallStats : : EHitOrMiss : : Miss , FCookStats : : CallStats : : EStatType : : Counter , 1l , bIsInGameThread ) ) ;
OnComplete ( MoveTemp ( Response ) ) ;
}
else
{
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " %s: Cache hit for %s from '%s' " ) ,
2022-05-31 22:01:53 -04:00
* Domain , * WriteToString < 96 > ( Response . Key ) , * Response . Name ) ;
2022-04-05 16:43:41 -04:00
uint64 ValueSize = Response . Value . GetData ( ) . GetCompressedSize ( ) ;
TRACE_COUNTER_ADD ( HttpDDC_BytesReceived , ValueSize ) ;
COOK_STAT ( UsageStats . GetStats . Accumulate ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Counter , 1l , bIsInGameThread ) ) ;
OnComplete ( { Response . Name , Response . Key , Response . Value , Response . UserData , EStatus : : Ok } ) ;
COOK_STAT ( UsageStats . GetStats . Accumulate ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Bytes , ValueSize , bIsInGameThread ) ) ;
}
2022-03-02 17:30:48 -05:00
}
2022-04-05 16:43:41 -04:00
COOK_STAT ( const int64 CyclesUsed = int64 ( ( FPlatformTime : : Seconds ( ) - StartTime ) / FPlatformTime : : GetSecondsPerCycle ( ) ) ) ;
COOK_STAT ( UsageStats . GetStats . Accumulate ( FCookStats : : CallStats : : EHitOrMiss : : Hit , FCookStats : : CallStats : : EStatType : : Cycles , CyclesUsed , bIsInGameThread ) ) ;
} ) ;
2022-02-02 07:35:19 -05:00
}
}
2022-02-24 10:24:10 -05:00
2022-02-02 07:35:19 -05:00
}
2022-02-14 14:43:39 -05:00
void FHttpCacheStore : : GetChunks (
2022-01-18 04:47:59 -05:00
const TConstArrayView < FCacheGetChunkRequest > Requests ,
2021-08-04 18:08:50 -04:00
IRequestOwner & Owner ,
2022-01-18 04:47:59 -05:00
FOnCacheGetChunkComplete & & OnComplete )
2021-04-28 16:22:18 -04:00
{
2022-04-05 16:43:41 -04:00
TRACE_CPUPROFILER_EVENT_SCOPE ( HttpDDC_GetChunks ) ;
2022-01-14 10:53:17 -05:00
// TODO: This is inefficient because Jupiter doesn't allow us to get only part of a compressed blob, so we have to
// get the whole thing and then decompress only the portion we need. Furthermore, because there is no propagation
// between cache stores during chunk requests, the fetched result won't end up in the local store.
// These efficiency issues will be addressed by changes to the Hierarchy that translate chunk requests that
// are missing in local/fast stores and have to be retrieved from slow stores into record requests instead. That
// will make this code path unused/uncommon as Jupiter will most always be a slow store with a local/fast store in front of it.
// Regardless, to adhere to the functional contract, this implementation must exist.
2022-01-18 04:47:59 -05:00
TArray < FCacheGetChunkRequest , TInlineAllocator < 16 > > SortedRequests ( Requests ) ;
2022-01-14 10:53:17 -05:00
SortedRequests . StableSort ( TChunkLess ( ) ) ;
bool bHasValue = false ;
FValue Value ;
FValueId ValueId ;
FCacheKey ValueKey ;
FCompressedBuffer ValueBuffer ;
FCompressedBufferReader ValueReader ;
EStatus ValueStatus = EStatus : : Error ;
FOptionalCacheRecord Record ;
2022-01-18 04:47:59 -05:00
for ( const FCacheGetChunkRequest & Request : SortedRequests )
2021-04-28 16:22:18 -04:00
{
2022-01-14 10:53:17 -05:00
const bool bExistsOnly = EnumHasAnyFlags ( Request . Policy , ECachePolicy : : SkipData ) ;
COOK_STAT ( auto Timer = bExistsOnly ? UsageStats . TimeProbablyExists ( ) : UsageStats . TimeGet ( ) ) ;
if ( ! ( bHasValue & & ValueKey = = Request . Key & & ValueId = = Request . Id ) | | ValueReader . HasSource ( ) < ! bExistsOnly )
{
ValueStatus = EStatus : : Error ;
ValueReader . ResetSource ( ) ;
ValueKey = { } ;
ValueId . Reset ( ) ;
Value . Reset ( ) ;
bHasValue = false ;
if ( Request . Id . IsValid ( ) )
{
if ( ! ( Record & & Record . Get ( ) . GetKey ( ) = = Request . Key ) )
{
FCacheRecordPolicyBuilder PolicyBuilder ( ECachePolicy : : None ) ;
PolicyBuilder . AddValuePolicy ( Request . Id , Request . Policy ) ;
Record . Reset ( ) ;
2022-04-05 16:43:41 -04:00
FRequestOwner BlockingOwner ( EPriority : : Blocking ) ;
GetCacheRecordOnlyAsync ( BlockingOwner , Request . Name , Request . Key , PolicyBuilder . Build ( ) , 0 , [ & Record ] ( FGetCacheRecordOnlyResponse & & Response )
2022-06-29 21:31:09 -04:00
{
Record = MoveTemp ( Response . Record ) ;
} ) ;
2022-04-05 16:43:41 -04:00
BlockingOwner . Wait ( ) ;
2022-01-14 10:53:17 -05:00
}
if ( Record )
{
const FValueWithId & ValueWithId = Record . Get ( ) . GetValue ( Request . Id ) ;
bHasValue = ValueWithId . IsValid ( ) ;
Value = ValueWithId ;
ValueId = Request . Id ;
ValueKey = Request . Key ;
if ( IsValueDataReady ( Value , Request . Policy ) )
{
ValueReader . SetSource ( Value . GetData ( ) ) ;
}
else
{
2022-04-05 16:43:41 -04:00
auto IdGetter = [ ] ( const FValueWithId & Value )
2022-02-24 10:24:10 -05:00
{
2022-04-05 16:43:41 -04:00
return FString ( WriteToString < 16 > ( Value . GetId ( ) ) ) ;
2022-02-24 10:24:10 -05:00
} ;
2022-04-05 16:43:41 -04:00
FRequestOwner BlockingOwner ( EPriority : : Blocking ) ;
bool bSucceeded = false ;
FCompressedBuffer NewBuffer ;
FGetRecordOp : : GetDataBatch ( * this , BlockingOwner , Request . Name , Request . Key , : : MakeArrayView ( { ValueWithId } ) , IdGetter , [ & bSucceeded , & NewBuffer ] ( FGetRecordOp : : FGetCachedDataBatchResponse & & Response )
2022-06-29 21:31:09 -04:00
{
if ( Response . Status = = EStatus : : Ok )
2022-04-05 16:43:41 -04:00
{
2022-06-29 21:31:09 -04:00
bSucceeded = true ;
NewBuffer = MoveTemp ( Response . DataBuffer ) ;
}
} ) ;
2022-04-05 16:43:41 -04:00
BlockingOwner . Wait ( ) ;
2022-04-05 15:17:17 -04:00
2022-04-05 16:43:41 -04:00
if ( bSucceeded )
2022-04-05 15:17:17 -04:00
{
2022-04-05 16:43:41 -04:00
ValueBuffer = MoveTemp ( NewBuffer ) ;
2022-01-14 10:53:17 -05:00
ValueReader . SetSource ( ValueBuffer ) ;
}
else
{
ValueBuffer . Reset ( ) ;
ValueReader . ResetSource ( ) ;
}
}
}
}
else
{
2022-02-02 08:18:44 -05:00
ValueKey = Request . Key ;
2022-04-05 16:43:41 -04:00
{
FRequestOwner BlockingOwner ( EPriority : : Blocking ) ;
bool bSucceeded = false ;
GetCacheValueAsync ( BlockingOwner , Request . Name , Request . Key , Request . Policy , 0 , [ & bSucceeded , & Value ] ( FCacheGetValueResponse & & Response )
2022-06-29 21:31:09 -04:00
{
Value = MoveTemp ( Response . Value ) ;
bSucceeded = Response . Status = = EStatus : : Ok ;
} ) ;
2022-04-05 16:43:41 -04:00
BlockingOwner . Wait ( ) ;
bHasValue = bSucceeded ;
}
2022-05-02 11:52:58 -04:00
if ( bHasValue )
2022-02-02 08:18:44 -05:00
{
2022-05-02 11:52:58 -04:00
if ( IsValueDataReady ( Value , Request . Policy ) )
2022-02-24 10:24:10 -05:00
{
2022-05-02 11:52:58 -04:00
ValueReader . SetSource ( Value . GetData ( ) ) ;
2022-02-02 08:18:44 -05:00
}
else
{
2022-05-02 11:52:58 -04:00
auto IdGetter = [ ] ( const FValue & Value )
{
return FString ( TEXT ( " Default " ) ) ;
} ;
FRequestOwner BlockingOwner ( EPriority : : Blocking ) ;
bool bSucceeded = false ;
FCompressedBuffer NewBuffer ;
FGetRecordOp : : GetDataBatch ( * this , BlockingOwner , Request . Name , Request . Key , : : MakeArrayView ( { Value } ) , IdGetter , [ & bSucceeded , & NewBuffer ] ( FGetRecordOp : : FGetCachedDataBatchResponse & & Response )
2022-06-29 21:31:09 -04:00
{
if ( Response . Status = = EStatus : : Ok )
2022-05-02 11:52:58 -04:00
{
2022-06-29 21:31:09 -04:00
bSucceeded = true ;
NewBuffer = MoveTemp ( Response . DataBuffer ) ;
}
} ) ;
2022-05-02 11:52:58 -04:00
BlockingOwner . Wait ( ) ;
if ( bSucceeded )
{
ValueBuffer = MoveTemp ( NewBuffer ) ;
ValueReader . SetSource ( ValueBuffer ) ;
}
else
{
ValueBuffer . Reset ( ) ;
ValueReader . ResetSource ( ) ;
}
2022-02-02 08:18:44 -05:00
}
}
2022-05-02 11:52:58 -04:00
else
{
ValueBuffer . Reset ( ) ;
ValueReader . ResetSource ( ) ;
}
2022-01-14 10:53:17 -05:00
}
}
if ( bHasValue )
{
const uint64 RawOffset = FMath : : Min ( Value . GetRawSize ( ) , Request . RawOffset ) ;
const uint64 RawSize = FMath : : Min ( Value . GetRawSize ( ) - RawOffset , Request . RawSize ) ;
UE_LOG ( LogDerivedDataCache , Verbose , TEXT ( " %s: Cache hit for %s from '%s' " ) ,
2022-05-31 22:01:53 -04:00
* Domain , * WriteToString < 96 > ( Request . Key , ' / ' , Request . Id ) , * Request . Name ) ;
2022-01-14 10:53:17 -05:00
COOK_STAT ( Timer . AddHit ( ! bExistsOnly ? RawSize : 0 ) ) ;
2022-02-14 14:43:39 -05:00
FSharedBuffer Buffer ;
if ( ! bExistsOnly )
2022-01-14 10:53:17 -05:00
{
2022-02-14 14:43:39 -05:00
Buffer = ValueReader . Decompress ( RawOffset , RawSize ) ;
2022-01-14 10:53:17 -05:00
}
2022-02-14 14:43:39 -05:00
const EStatus ChunkStatus = bExistsOnly | | Buffer . GetSize ( ) = = RawSize ? EStatus : : Ok : EStatus : : Error ;
OnComplete ( { Request . Name , Request . Key , Request . Id , Request . RawOffset ,
RawSize , Value . GetRawHash ( ) , MoveTemp ( Buffer ) , Request . UserData , ChunkStatus } ) ;
2022-01-14 10:53:17 -05:00
continue ;
}
2022-02-14 14:43:39 -05:00
OnComplete ( Request . MakeResponse ( EStatus : : Error ) ) ;
2021-04-28 16:22:18 -04:00
}
}
2022-05-31 22:01:53 -04:00
void FHttpCacheStoreParams : : Parse ( const TCHAR * NodeName , const TCHAR * Config )
{
FString ServerId ;
if ( FParse : : Value ( Config , TEXT ( " ServerID= " ) , ServerId ) )
{
FString ServerEntry ;
const TCHAR * ServerSection = TEXT ( " HordeStorageServers " ) ;
if ( GConfig - > GetString ( ServerSection , * ServerId , ServerEntry , GEngineIni ) )
{
Parse ( NodeName , * ServerEntry ) ;
}
else
{
UE_LOG ( LogDerivedDataCache , Warning , TEXT ( " %s: Using ServerID=%s which was not found in [%s] " ) , NodeName , * ServerId , ServerSection ) ;
}
}
FString OverrideName ;
// Host Params
FParse : : Value ( Config , TEXT ( " Host= " ) , Host ) ;
if ( FParse : : Value ( Config , TEXT ( " EnvHostOverride= " ) , OverrideName ) )
{
FString HostEnv = FPlatformMisc : : GetEnvironmentVariable ( * OverrideName ) ;
if ( ! HostEnv . IsEmpty ( ) )
{
Host = HostEnv ;
UE_LOG ( LogDerivedDataCache , Log , TEXT ( " %s: Found environment override for Host %s=%s " ) , NodeName , * OverrideName , * Host ) ;
}
}
if ( FParse : : Value ( Config , TEXT ( " CommandLineHostOverride= " ) , OverrideName ) )
{
if ( FParse : : Value ( FCommandLine : : Get ( ) , * ( OverrideName + TEXT ( " = " ) ) , Host ) )
{
UE_LOG ( LogDerivedDataCache , Log , TEXT ( " %s: Found command line override for Host %s=%s " ) , NodeName , * OverrideName , * Host ) ;
}
}
FParse : : Bool ( Config , TEXT ( " ResolveHostCanonicalName= " ) , bResolveHostCanonicalName ) ;
// Namespace Params
FParse : : Value ( Config , TEXT ( " Namespace= " ) , Namespace ) ;
FParse : : Value ( Config , TEXT ( " StructuredNamespace= " ) , StructuredNamespace ) ;
// OAuth Params
FParse : : Value ( Config , TEXT ( " OAuthProvider= " ) , OAuthProvider ) ;
if ( FParse : : Value ( Config , TEXT ( " CommandLineOAuthProviderOverride= " ) , OverrideName ) )
{
if ( FParse : : Value ( FCommandLine : : Get ( ) , * ( OverrideName + TEXT ( " = " ) ) , OAuthProvider ) )
{
UE_LOG ( LogDerivedDataCache , Log , TEXT ( " %s: Found command line override for OAuthProvider %s=%s " ) , NodeName , * OverrideName , * OAuthProvider ) ;
}
}
FParse : : Value ( Config , TEXT ( " OAuthClientId= " ) , OAuthClientId ) ;
FParse : : Value ( Config , TEXT ( " OAuthSecret= " ) , OAuthSecret ) ;
if ( FParse : : Value ( Config , TEXT ( " CommandLineOAuthSecretOverride= " ) , OverrideName ) )
{
if ( FParse : : Value ( FCommandLine : : Get ( ) , * ( OverrideName + TEXT ( " = " ) ) , OAuthSecret ) )
{
UE_LOG ( LogDerivedDataCache , Log , TEXT ( " %s: Found command line override for OAuthSecret %s=%s " ) , NodeName , * OverrideName , * OAuthSecret ) ;
}
}
FParse : : Value ( Config , TEXT ( " OAuthScope= " ) , OAuthScope ) ;
2022-07-06 05:59:22 -04:00
FParse : : Value ( Config , TEXT ( " OAuthProviderIdentifier= " ) , OAuthProviderIdentifier ) ;
if ( FParse : : Value ( Config , TEXT ( " OAuthAccessTokenEnvOverride= " ) , OverrideName ) )
{
FString AccessToken = FPlatformMisc : : GetEnvironmentVariable ( * OverrideName ) ;
if ( ! AccessToken . IsEmpty ( ) )
{
OAuthAccessToken = AccessToken ;
2022-07-12 15:36:38 -04:00
// We do not log the access token as it is sensitive information.
UE_LOG ( LogDerivedDataCache , Log , TEXT ( " %s: Found OAuth access token in %s. " ) , NodeName , * OverrideName ) ;
2022-07-06 05:59:22 -04:00
}
}
2022-05-31 22:01:53 -04:00
// Cache Params
FParse : : Bool ( Config , TEXT ( " ReadOnly= " ) , bReadOnly ) ;
}
2022-02-14 14:43:39 -05:00
} // UE::DerivedData
2021-04-28 16:22:18 -04:00
2022-01-11 11:57:38 -05:00
# endif // WITH_HTTP_DDC_BACKEND
2022-02-14 14:43:39 -05:00
namespace UE : : DerivedData
2022-01-11 11:57:38 -05:00
{
2022-05-31 22:01:53 -04:00
TTuple < ILegacyCacheStore * , ECacheStoreFlags > CreateHttpCacheStore ( const TCHAR * NodeName , const TCHAR * Config )
2022-01-11 11:57:38 -05:00
{
# if WITH_HTTP_DDC_BACKEND
2022-05-31 22:01:53 -04:00
FHttpCacheStoreParams Params ;
Params . Parse ( NodeName , Config ) ;
if ( Params . Host . IsEmpty ( ) )
2022-01-11 11:57:38 -05:00
{
2022-05-31 22:01:53 -04:00
UE_LOG ( LogDerivedDataCache , Error , TEXT ( " %s: Missing required parameter 'Host' " ) , NodeName ) ;
return MakeTuple ( nullptr , ECacheStoreFlags : : None ) ;
2022-01-11 11:57:38 -05:00
}
2022-05-31 22:01:53 -04:00
if ( Params . Host = = TEXT ( " None " ) )
{
UE_LOG ( LogDerivedDataCache , Log , TEXT ( " %s: Disabled because Host is set to 'None' " ) , NodeName ) ;
return MakeTuple ( nullptr , ECacheStoreFlags : : None ) ;
}
if ( Params . Namespace . IsEmpty ( ) )
{
Params . Namespace = FApp : : GetProjectName ( ) ;
UE_LOG ( LogDerivedDataCache , Warning , TEXT ( " %s: Missing required parameter 'Namespace', falling back to '%s' " ) , NodeName , * Params . Namespace ) ;
}
if ( Params . StructuredNamespace . IsEmpty ( ) )
{
Params . StructuredNamespace = Params . Namespace ;
UE_LOG ( LogDerivedDataCache , Warning , TEXT ( " %s: Missing required parameter 'StructuredNamespace', falling back to '%s' " ) , NodeName , * Params . StructuredNamespace ) ;
}
if ( Params . OAuthProvider . IsEmpty ( ) )
{
UE_LOG ( LogDerivedDataCache , Error , TEXT ( " %s: Missing required parameter 'OAuthProvider' " ) , NodeName ) ;
return MakeTuple ( nullptr , ECacheStoreFlags : : None ) ;
}
// No need for OAuth client id and secret if using a local provider.
if ( ! Params . OAuthProvider . StartsWith ( TEXT ( " http://localhost " ) ) )
{
if ( Params . OAuthClientId . IsEmpty ( ) )
{
UE_LOG ( LogDerivedDataCache , Error , TEXT ( " %s: Missing required parameter 'OAuthClientId' " ) , NodeName ) ;
return MakeTuple ( nullptr , ECacheStoreFlags : : None ) ;
}
if ( Params . OAuthSecret . IsEmpty ( ) )
{
UE_LOG ( LogDerivedDataCache , Error , TEXT ( " %s: Missing required parameter 'OAuthSecret' " ) , NodeName ) ;
return MakeTuple ( nullptr , ECacheStoreFlags : : None ) ;
}
}
if ( Params . OAuthScope . IsEmpty ( ) )
{
Params . OAuthScope = TEXTVIEW ( " cache_access " ) ;
}
TUniquePtr < FHttpCacheStore > Backend = MakeUnique < FHttpCacheStore > ( Params ) ;
if ( ! Backend - > IsUsable ( ) )
{
UE_LOG ( LogDerivedDataCache , Warning , TEXT ( " %s: Failed to contact the service (%s), will not use it. " ) , NodeName , * Params . Host ) ;
Backend . Reset ( ) ;
}
return MakeTuple ( Backend . Release ( ) , ECacheStoreFlags : : Remote | ECacheStoreFlags : : Query | ( Params . bReadOnly ? ECacheStoreFlags : : None : ECacheStoreFlags : : Store ) ) ;
2022-01-11 11:57:38 -05:00
# else
2022-05-31 22:01:53 -04:00
UE_LOG ( LogDerivedDataCache , Warning , TEXT ( " %s: HTTP cache is not yet supported in the current build configuration. " ) , NodeName ) ;
return MakeTuple ( nullptr , ECacheStoreFlags : : None ) ;
2022-01-11 11:57:38 -05:00
# endif
}
2022-05-31 22:01:53 -04:00
ILegacyCacheStore * GetAnyHttpCacheStore (
2022-01-11 11:57:38 -05:00
FString & OutDomain ,
FString & OutOAuthProvider ,
FString & OutOAuthClientId ,
FString & OutOAuthSecret ,
2022-03-23 18:05:53 -04:00
FString & OutOAuthScope ,
2022-07-06 05:59:22 -04:00
FString & OAuthProviderIdentifier ,
FString & OAuthAccessToken ,
2022-01-11 11:57:38 -05:00
FString & OutNamespace ,
FString & OutStructuredNamespace )
{
# if WITH_HTTP_DDC_BACKEND
2022-02-14 14:43:39 -05:00
if ( FHttpCacheStore * HttpBackend = FHttpCacheStore : : GetAny ( ) )
2022-01-14 10:53:17 -05:00
{
OutDomain = HttpBackend - > GetDomain ( ) ;
OutOAuthProvider = HttpBackend - > GetOAuthProvider ( ) ;
OutOAuthClientId = HttpBackend - > GetOAuthClientId ( ) ;
OutOAuthSecret = HttpBackend - > GetOAuthSecret ( ) ;
2022-03-23 18:05:53 -04:00
OutOAuthScope = HttpBackend - > GetOAuthScope ( ) ;
2022-07-06 05:59:22 -04:00
OAuthProviderIdentifier = HttpBackend - > GetOAuthProviderIdentifier ( ) ;
OAuthAccessToken = HttpBackend - > GetOAuthAccessToken ( ) ;
2022-01-14 10:53:17 -05:00
OutNamespace = HttpBackend - > GetNamespace ( ) ;
OutStructuredNamespace = HttpBackend - > GetStructuredNamespace ( ) ;
return HttpBackend ;
}
return nullptr ;
2022-01-11 11:57:38 -05:00
# else
return nullptr ;
# endif
}
2022-02-14 14:43:39 -05:00
} // UE::DerivedData