Rename asset_stream_manager to cdc_stream (#27)

Fixes #13
This commit is contained in:
Lutz Justen
2022-12-01 16:14:56 +01:00
committed by GitHub
parent f0539226a2
commit 01a60e2490
58 changed files with 197 additions and 213 deletions

267
cdc_stream/multi_session.h Normal file
View File

@@ -0,0 +1,267 @@
/*
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CDC_STREAM_MULTI_SESSION_H_
#define CDC_STREAM_MULTI_SESSION_H_
#include <memory>
#include <string>
#include <thread>
#include <unordered_map>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "cdc_stream/asset_stream_server.h"
#include "cdc_stream/metrics_recorder.h"
#include "cdc_stream/session_config.h"
#include "common/stopwatch.h"
#include "data_store/data_store_writer.h"
#include "manifest/file_chunk_map.h"
#include "manifest/manifest_updater.h"
namespace cdc_ft {
class ProcessFactory;
class Session;
struct SessionTarget;
using ManifestUpdatedCb = std::function<void()>;
// Updates the manifest and runs a file watcher in a background thread.
class MultiSessionRunner {
public:
// |src_dir| is the source directory on the workstation to stream.
// |data_store| can be passed for tests to override the default store used.
// |process_factory| abstracts process creation.
// |enable_stats| shows whether statistics should be derived.
// |wait_duration| is the waiting time for changes in the streamed directory.
// |num_updater_threads| is the thread count for the manifest updater.
// |manifest_updated_cb| is the callback executed when a new manifest is set.
MultiSessionRunner(
std::string src_dir, DataStoreWriter* data_store,
ProcessFactory* process_factory, bool enable_stats,
absl::Duration wait_duration, uint32_t num_updater_threads,
MultiSessionMetricsRecorder const* metrics_recorder,
ManifestUpdatedCb manifest_updated_cb = ManifestUpdatedCb());
~MultiSessionRunner() = default;
// Starts |server_| of |type| on |port|.
absl::Status Initialize(
int port, AssetStreamServerType type,
ContentSentHandler content_sent = ContentSentHandler());
// Stops updating the manifest and |server_|.
absl::Status Shutdown() ABSL_LOCKS_EXCLUDED(mutex_);
// Waits until a manifest is ready and the session for |instance_id| has
// acknowledged the reception of the currently set manifest id. |fuse_timeout|
// is the timeout for waiting for the FUSE manifest ack. The time required to
// generate the manifest is not part of this timeout as this could take a
// longer time for a directory with many files.
absl::Status WaitForManifestAck(const std::string& instance_id,
absl::Duration fuse_timeout);
absl::Status Status() ABSL_LOCKS_EXCLUDED(mutex_);
// Returns the current manifest id used by |server_|.
ContentIdProto ManifestId() const;
private:
// Updates manifest if the content of the watched directory changes and
// distributes it to subscribed gamelets.
void Run();
// Record MultiSessionStart event.
void RecordMultiSessionStart(const ManifestUpdater& manifest_updater);
// Record ManifestUpdate event.
void RecordManifestUpdate(const ManifestUpdater& manifest_updater,
absl::Duration duration,
metrics::UpdateTrigger trigger,
absl::Status status);
void SetStatus(absl::Status status) ABSL_LOCKS_EXCLUDED(mutex_);
// Files changed callback called from FileWatcherWin.
void OnFilesChanged() ABSL_LOCKS_EXCLUDED(mutex_);
// Directory recreated callback called from FileWatcherWin.
void OnDirRecreated() ABSL_LOCKS_EXCLUDED(mutex_);
// Called during manifest update when the intermediate manifest or the final
// manifest is available. Pushes the manifest to connected FUSEs.
void SetManifest(const ContentIdProto& manifest_id);
const std::string src_dir_;
DataStoreWriter* const data_store_;
ProcessFactory* const process_factory_;
FileChunkMap file_chunks_;
const absl::Duration wait_duration_;
const uint32_t num_updater_threads_;
const ManifestUpdatedCb manifest_updated_cb_;
std::unique_ptr<AssetStreamServer> server_;
std::unique_ptr<ManifestUpdater> manifest_updater_;
// Modifications (shutdown, file changes).
absl::Mutex mutex_;
bool shutdown_ ABSL_GUARDED_BY(mutex_) = false;
bool files_changed_ ABSL_GUARDED_BY(mutex_) = false;
bool dir_recreated_ ABSL_GUARDED_BY(mutex_) = false;
bool manifest_set_ ABSL_GUARDED_BY(mutex_) = false;
Stopwatch files_changed_timer_ ABSL_GUARDED_BY(mutex_);
absl::Status status_ ABSL_GUARDED_BY(mutex_);
// Background thread that watches files and updates the manifest.
std::unique_ptr<std::thread> thread_;
MultiSessionMetricsRecorder const* metrics_recorder_;
};
// Manages an asset streaming session from a fixed directory on the workstation
// to an arbitrary number of gamelets.
class MultiSession {
public:
// Maximum length of cache path. We must be able to write content hashes into
// this path:
// <cache path>\01234567890123456789<null terminator> = 260 characters.
static constexpr size_t kDefaultMaxCachePathLen =
260 - 1 - ContentId::kHashSize * 2 - 1;
// Length of the hash appended to the cache directory, exposed for testing.
static constexpr size_t kDirHashLen = 8;
// |src_dir| is the source directory on the workstation to stream.
// |cfg| contains generic configuration parameters for each session.
// |process_factory| abstracts process creation.
// |data_store| can be passed for tests to override the default store used.
// By default, the class uses a DiskDataStore that writes to
// %APPDATA%\GGP\asset_streaming|<dir_derived_from_src_dir> on Windows.
MultiSession(std::string src_dir, SessionConfig cfg,
ProcessFactory* process_factory,
MultiSessionMetricsRecorder const* metrics_recorder,
std::unique_ptr<DataStoreWriter> data_store = nullptr);
~MultiSession();
// Initializes the data store if not overridden in the constructor and starts
// a background thread for updating the manifest and watching file changes.
// Does not wait for the initial manifest update to finish. Use IsRunning()
// to determine whether it is finished.
// Not thread-safe.
absl::Status Initialize();
// Stops all sessions and shuts down the server.
// Not thread-safe.
absl::Status Shutdown() ABSL_LOCKS_EXCLUDED(shutdownMu_);
// Returns the |src_dir| streaming directory passed to the constructor.
const std::string& src_dir() const { return src_dir_; }
// Returns the status of the background thread.
// Not thread-safe.
absl::Status Status();
// Starts a new streaming session to the instance described by |target| and
// waits until the FUSE has received the initial manifest id.
// Returns an error if a session for that instance already exists.
// |instance_id| is a unique id for the remote instance and mount directory,
// e.g. user@host:mount_dir.
// |target| identifies the remote target and how to connect to it.
// |project_id| is the project that owns the instance. Stadia only.
// |organization_id| is organization that contains the instance. Stadia only.
// Thread-safe.
absl::Status StartSession(const std::string& instance_id,
const SessionTarget& target,
const std::string& project_id,
const std::string& organization_id)
ABSL_LOCKS_EXCLUDED(sessions_mutex_);
// Stops the session for the given |instance_id|.
// Returns a NotFound error if a session for that instance does not exists.
// Thread-safe.
absl::Status StopSession(const std::string& instance_id)
ABSL_LOCKS_EXCLUDED(sessions_mutex_);
// Returns true if there is an existing session for |instance_id|.
bool HasSession(const std::string& instance_id)
ABSL_LOCKS_EXCLUDED(sessions_mutex_);
// Returns true if the FUSE process is up and running for an existing session
// with ID |instance_id|.
bool IsSessionHealthy(const std::string& instance_id)
ABSL_LOCKS_EXCLUDED(sessions_mutex_);
// Returns true if the MultiSession does not have any active sessions.
bool Empty() ABSL_LOCKS_EXCLUDED(sessions_mutex_);
// Returns the number of avtive sessions.
uint32_t GetSessionCount() ABSL_LOCKS_EXCLUDED(sessions_mutex_);
// For a given source directory |dir|, e.g. "C:\path\to\game", returns a
// sanitized version of |dir| plus a hash of |dir|, e.g.
// "c__path_to_game_abcdef01".
static std::string GetCacheDir(std::string dir);
// Returns the directory where manifest chunks are cached, e.g.
// "%APPDATA%\GGP\asset_streaming\c__path_to_game_abcdef01" for
// "C:\path\to\game".
// The returned path is shortened to |max_len| by removing UTF8 code points
// from the beginning of the actual cache directory (c__path...) if necessary.
static absl::StatusOr<std::string> GetCachePath(
const std::string& src_dir, size_t max_len = kDefaultMaxCachePathLen);
// Record an event associated with the multi-session.
void RecordMultiSessionEvent(metrics::DeveloperLogEvent event,
metrics::EventType code);
// Record an event for a session associated with the |instance_id|.
void RecordSessionEvent(metrics::DeveloperLogEvent event,
metrics::EventType code,
const std::string& instance_id);
private:
std::string src_dir_;
SessionConfig cfg_;
ProcessFactory* const process_factory_;
std::unique_ptr<DataStoreWriter> data_store_;
std::thread heartbeat_watcher_;
absl::Mutex shutdownMu_;
bool shutdown_ ABSL_GUARDED_BY(shutdownMu_) = false;
// Background thread for watching file changes and updating the manifest.
std::unique_ptr<MultiSessionRunner> runner_;
// Local forwarding port for the asset stream service.
int local_asset_stream_port_ = 0;
// Maps instance id to sessions.
std::unordered_map<std::string, std::unique_ptr<Session>> sessions_
ABSL_GUARDED_BY(sessions_mutex_);
absl::Mutex sessions_mutex_;
MultiSessionMetricsRecorder const* metrics_recorder_;
Session* FindSession(const std::string& instance_id)
ABSL_LOCKS_EXCLUDED(sessions_mutex_);
void OnContentSent(size_t byte_count, size_t chunck_count,
std::string instance_id);
void StartHeartBeatCheck();
};
} // namespace cdc_ft
#endif // CDC_STREAM_MULTI_SESSION_H_