Audacity 3.2.0
Classes | Public Member Functions | Static Public Member Functions | Private Types | Private Member Functions | Private Attributes | List of all members
audacity::cloud::audiocom::sync::RemoteProjectSnapshot Class Referencefinal

#include <RemoteProjectSnapshot.h>

Inheritance diagram for audacity::cloud::audiocom::sync::RemoteProjectSnapshot:
[legend]
Collaboration diagram for audacity::cloud::audiocom::sync::RemoteProjectSnapshot:
[legend]

Classes

struct  Tag
 

Public Member Functions

 RemoteProjectSnapshot (Tag, ProjectInfo projectInfo, SnapshotInfo snapshotInfo, std::string path, RemoteProjectSnapshotStateCallback callback, bool downloadDetached)
 
 ~RemoteProjectSnapshot ()
 
 RemoteProjectSnapshot (const RemoteProjectSnapshot &)=delete
 
RemoteProjectSnapshotoperator= (const RemoteProjectSnapshot &)=delete
 
 RemoteProjectSnapshot (RemoteProjectSnapshot &&)=delete
 
RemoteProjectSnapshotoperator= (RemoteProjectSnapshot &&)=delete
 
void Cancel ()
 
TransferStats GetTransferStats () const
 
std::string_view GetProjectId () const
 

Static Public Member Functions

static std::shared_ptr< RemoteProjectSnapshotSync (ProjectInfo projectInfo, SnapshotInfo snapshotInfo, std::string path, RemoteProjectSnapshotStateCallback callback, bool downloadDetached)
 

Private Types

enum class  State { Downloading , Cancelled , Failed , Succeeded }
 
using SuccessHandler = std::function< void(audacity::network_manager::ResponsePtr)>
 
using Clock = std::chrono::steady_clock
 
using TimePoint = Clock::time_point
 

Private Member Functions

std::string AttachOriginalDB ()
 
void SetupBlocksCopy (const std::string &dbName, std::unordered_set< std::string > blocks)
 
std::unordered_set< std::string > CalculateKnownBlocks (const std::string &attachedDbName) const
 
void DoCancel ()
 
void DownloadBlob (std::string url, SuccessHandler onSuccess, int retries=3)
 
void OnProjectBlobDownloaded (audacity::network_manager::ResponsePtr response)
 
void OnBlockDownloaded (std::string blockHash, audacity::network_manager::ResponsePtr response)
 
void OnFailure (ResponseResult result)
 
void RemoveResponse (audacity::network_manager::IResponse *response)
 
void MarkProjectInDB (bool successfulDownload)
 
void ReportProgress ()
 
bool InProgress () const
 
void RequestsThread ()
 
void SetState (State state)
 
void CleanupOrphanBlocks ()
 

Private Attributes

const std::string mSnapshotDBName
 
const ProjectInfo mProjectInfo
 
const SnapshotInfo mSnapshotInfo
 
const std::string mPath
 
RemoteProjectSnapshotStateCallback mCallback
 
std::vector< std::string > mAttachedDBNames
 
std::atomic< StatemState { State::Downloading }
 
TimePoint mStartTime { Clock::now() }
 
TimePoint mEndTime
 
std::thread mRequestsThread
 
std::mutex mRequestsMutex
 
std::condition_variable mRequestsCV
 
std::vector< std::pair< std::string, SuccessHandler > > mRequests
 
int mRequestsInProgress { 0 }
 
size_t mNextRequestIndex { 0 }
 
std::mutex mResponsesMutex
 
std::vector< std::shared_ptr< audacity::network_manager::IResponse > > mResponses
 
std::condition_variable mResponsesEmptyCV
 
std::atomic< int64_t > mDownloadedBlocks { 0 }
 
std::atomic< int64_t > mCopiedBlocks { 0 }
 
std::atomic< int64_t > mDownloadedBytes { 0 }
 
int64_t mMissingBlocks { 0 }
 
std::optional< std::future< bool > > mCopyBlocksFuture
 
std::atomic< bool > mProjectDownloaded { false }
 
bool mNothingToDo { false }
 
const bool mDownloadDetached { false }
 

Detailed Description

Definition at line 51 of file RemoteProjectSnapshot.h.

Member Typedef Documentation

◆ Clock

using audacity::cloud::audiocom::sync::RemoteProjectSnapshot::Clock = std::chrono::steady_clock
private

Definition at line 134 of file RemoteProjectSnapshot.h.

◆ SuccessHandler

Definition at line 89 of file RemoteProjectSnapshot.h.

◆ TimePoint

Definition at line 135 of file RemoteProjectSnapshot.h.

Member Enumeration Documentation

◆ State

Enumerator
Downloading 
Cancelled 
Failed 
Succeeded 

Definition at line 81 of file RemoteProjectSnapshot.h.

Constructor & Destructor Documentation

◆ RemoteProjectSnapshot() [1/3]

audacity::cloud::audiocom::sync::RemoteProjectSnapshot::RemoteProjectSnapshot ( Tag  ,
ProjectInfo  projectInfo,
SnapshotInfo  snapshotInfo,
std::string  path,
RemoteProjectSnapshotStateCallback  callback,
bool  downloadDetached 
)

Definition at line 60 of file RemoteProjectSnapshot.cpp.

63 : mSnapshotDBName { std::string("s_") + projectInfo.Id }
64 , mProjectInfo { std::move(projectInfo) }
65 , mSnapshotInfo { std::move(snapshotInfo) }
66 , mPath { std::move(path) }
67 , mCallback { std::move(callback) }
68 , mDownloadDetached { downloadDetached }
69{
71 // RemoteProjectSnapshot always receives a path to the database
72 // that has AudacityProject schema installed, even if it's a detached
73 // or was deleted from the disk before
74 auto attachStmt = db->CreateStatement("ATTACH DATABASE ? AS ?");
75 auto result = attachStmt->Prepare(mPath, mSnapshotDBName).Run();
76
77 if (!result.IsOk())
78 return;
79
81
82 auto blocksSource = mSnapshotDBName;
83
85 {
86 if (auto name = AttachOriginalDB(); !name.empty())
87 blocksSource = name;
88 }
89
90 // This would return and empty set when the project
91 // is detached
92 auto knownBlocks = CalculateKnownBlocks(blocksSource);
93
95 {
96 // We can assume, that if the known blocks are present,
97 // they come from the "original" database
98 SetupBlocksCopy(blocksSource, knownBlocks);
99 }
100 else if (knownBlocks.size() == mSnapshotInfo.Blocks.size())
101 {
102 auto syncInfo =
104
105 if (
106 syncInfo && syncInfo->SnapshotId == mSnapshotInfo.Id &&
107 syncInfo->SyncStatus == DBProjectData::SyncStatusSynced)
108 {
109 mCallback({ {}, 0, 0, true });
110 mNothingToDo = true;
111 return;
112 }
113 }
114
117
118 MarkProjectInDB(false);
119
121 mSnapshotInfo.Blocks.size() :
122 mSnapshotInfo.Blocks.size() - knownBlocks.size();
123
124 mRequests.reserve(1 + mMissingBlocks);
125
126 mRequests.push_back(std::make_pair(
128 [this](auto response) { OnProjectBlobDownloaded(response); }));
129
130 for (auto& block : mSnapshotInfo.Blocks)
131 {
132 if (knownBlocks.find(ToUpper(block.Hash)) != knownBlocks.end())
133 continue;
134
135 mRequests.push_back(std::make_pair(
136 block.Url, [this, hash = ToUpper(block.Hash)](auto response)
137 { OnBlockDownloaded(std::move(hash), response); }));
138 }
139
141 std::thread { &RemoteProjectSnapshot::RequestsThread, this };
142}
std::string ToUpper(const std::string &str)
Definition: StringUtils.cpp:46
wxString name
Definition: TagsEditor.cpp:166
std::optional< DBProjectData > GetProjectData(std::string_view projectId) const
std::unordered_set< std::string > CalculateKnownBlocks(const std::string &attachedDbName) const
std::vector< std::pair< std::string, SuccessHandler > > mRequests
void SetupBlocksCopy(const std::string &dbName, std::unordered_set< std::string > blocks)
Result< Statement > CreateStatement(std::string_view sql) const
Prepares the given SQL statement for execution.
Definition: Connection.cpp:253
std::vector< SnapshotBlockInfo > Blocks
Definition: CloudSyncDTO.h:89

References AttachOriginalDB(), audacity::cloud::audiocom::sync::SnapshotInfo::Blocks, CalculateKnownBlocks(), CleanupOrphanBlocks(), audacity::sqlite::Connection::CreateStatement(), audacity::cloud::audiocom::sync::SnapshotInfo::FileUrl, audacity::cloud::audiocom::sync::CloudProjectsDatabase::Get(), audacity::cloud::audiocom::sync::CloudProjectsDatabase::GetConnection(), audacity::cloud::audiocom::sync::CloudProjectsDatabase::GetProjectData(), audacity::cloud::audiocom::sync::SnapshotInfo::Id, audacity::cloud::audiocom::sync::ProjectInfo::Id, MarkProjectInDB(), mAttachedDBNames, mCallback, mDownloadDetached, mMissingBlocks, mNothingToDo, mPath, mProjectInfo, mRequests, mRequestsThread, mSnapshotDBName, mSnapshotInfo, name, RequestsThread(), SetupBlocksCopy(), audacity::cloud::audiocom::sync::DBProjectData::SyncStatusSynced, and ToUpper().

Here is the call graph for this function:

◆ ~RemoteProjectSnapshot()

audacity::cloud::audiocom::sync::RemoteProjectSnapshot::~RemoteProjectSnapshot ( )

Definition at line 144 of file RemoteProjectSnapshot.cpp.

145{
146 DoCancel();
147
148 if (mRequestsThread.joinable())
149 mRequestsThread.join();
150
151 if (mCopyBlocksFuture.has_value())
152 mCopyBlocksFuture->wait();
153
154 {
155 auto lock = std::unique_lock { mResponsesMutex };
156 mResponsesEmptyCV.wait(lock, [this] { return mResponses.empty(); });
157 }
158
160
161 for (const auto& dbName : ListAttachedDatabases())
162 {
163 auto detachStmt = db->CreateStatement("DETACH DATABASE ?");
164 detachStmt->Prepare(dbName).Run();
165 }
166}
std::vector< std::shared_ptr< audacity::network_manager::IResponse > > mResponses

References audacity::sqlite::Connection::CreateStatement(), DoCancel(), audacity::cloud::audiocom::sync::CloudProjectsDatabase::Get(), audacity::cloud::audiocom::sync::CloudProjectsDatabase::GetConnection(), audacity::cloud::audiocom::sync::anonymous_namespace{RemoteProjectSnapshot.cpp}::ListAttachedDatabases(), mCopyBlocksFuture, mRequestsThread, mResponses, mResponsesEmptyCV, and mResponsesMutex.

Here is the call graph for this function:

◆ RemoteProjectSnapshot() [2/3]

audacity::cloud::audiocom::sync::RemoteProjectSnapshot::RemoteProjectSnapshot ( const RemoteProjectSnapshot )
delete

◆ RemoteProjectSnapshot() [3/3]

audacity::cloud::audiocom::sync::RemoteProjectSnapshot::RemoteProjectSnapshot ( RemoteProjectSnapshot &&  )
delete

Member Function Documentation

◆ AttachOriginalDB()

std::string audacity::cloud::audiocom::sync::RemoteProjectSnapshot::AttachOriginalDB ( )
private

Definition at line 226 of file RemoteProjectSnapshot.cpp.

227{
228 const std::string dbName = "o_" + mProjectInfo.Id;
229
230 const auto projectData =
232
233 if (!projectData)
234 return {};
235
237 // RemoteProjectSnapshot always receives a path to the database
238 // that has AudacityProject schema installed, even if it's a detached
239 // or was deleted from the disk before
240 auto attachStmt = db->CreateStatement("ATTACH DATABASE ? AS ?");
241 auto result = attachStmt->Prepare(projectData->LocalPath, dbName).Run();
242
243 if (!result.IsOk())
244 return {};
245
246 mAttachedDBNames.push_back(dbName);
247
248 return dbName;
249}

References audacity::sqlite::Connection::CreateStatement(), audacity::cloud::audiocom::sync::CloudProjectsDatabase::Get(), audacity::cloud::audiocom::sync::CloudProjectsDatabase::GetConnection(), audacity::cloud::audiocom::sync::CloudProjectsDatabase::GetProjectData(), audacity::cloud::audiocom::sync::ProjectInfo::Id, mAttachedDBNames, and mProjectInfo.

Referenced by RemoteProjectSnapshot().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ CalculateKnownBlocks()

std::unordered_set< std::string > audacity::cloud::audiocom::sync::RemoteProjectSnapshot::CalculateKnownBlocks ( const std::string &  attachedDbName) const
private

Definition at line 315 of file RemoteProjectSnapshot.cpp.

317{
318 std::unordered_set<std::string> remoteBlocks;
319
320 for (const auto& block : mSnapshotInfo.Blocks)
321 remoteBlocks.insert(ToUpper(block.Hash));
322
324
325 auto fn = db->CreateScalarFunction(
326 "inRemoteBlocks", [&remoteBlocks](const std::string& hash)
327 { return remoteBlocks.find(hash) != remoteBlocks.end(); });
328
329 auto statement = db->CreateStatement(
330 "SELECT hash FROM block_hashes WHERE project_id = ? AND inRemoteBlocks(hash) AND block_id IN (SELECT blockid FROM " +
331 attachedDbName + ".sampleblocks)");
332
333 if (!statement)
334 return {};
335
336 auto result = statement->Prepare(mProjectInfo.Id).Run();
337
338 std::unordered_set<std::string> knownBlocks;
339
340 for (auto row : result)
341 {
342 std::string hash;
343
344 if (!row.Get(0, hash))
345 continue;
346
347 knownBlocks.insert(hash);
348 }
349
350 return knownBlocks;
351}
static const auto fn

References audacity::cloud::audiocom::sync::SnapshotInfo::Blocks, fn, audacity::cloud::audiocom::sync::CloudProjectsDatabase::Get(), audacity::cloud::audiocom::sync::CloudProjectsDatabase::GetConnection(), audacity::cloud::audiocom::sync::ProjectInfo::Id, mProjectInfo, mSnapshotInfo, and ToUpper().

Referenced by RemoteProjectSnapshot().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Cancel()

void audacity::cloud::audiocom::sync::RemoteProjectSnapshot::Cancel ( )

Definition at line 196 of file RemoteProjectSnapshot.cpp.

References audacity::cloud::audiocom::Cancelled, DoCancel(), mCallback, mCopiedBlocks, mDownloadedBlocks, mMissingBlocks, and mProjectDownloaded.

Here is the call graph for this function:

◆ CleanupOrphanBlocks()

void audacity::cloud::audiocom::sync::RemoteProjectSnapshot::CleanupOrphanBlocks ( )
private

Definition at line 768 of file RemoteProjectSnapshot.cpp.

769{
771
772 auto transaction = db->BeginTransaction("d_" + mProjectInfo.Id);
773
774 std::unordered_set<std::string> snaphotBlockHashes;
775
776 for (const auto& block : mSnapshotInfo.Blocks)
777 snaphotBlockHashes.insert(ToUpper(block.Hash));
778
779 auto inSnaphotFunction = db->CreateScalarFunction(
780 "inSnapshot", [&snaphotBlockHashes](const std::string& hash)
781 { return snaphotBlockHashes.find(hash) != snaphotBlockHashes.end(); });
782
783 // Delete blocks not in the snapshot
784 auto deleteBlocksStatement = db->CreateStatement(
785 "DELETE FROM " + mSnapshotDBName +
786 ".sampleblocks WHERE blockid NOT IN (SELECT block_id FROM block_hashes WHERE project_id = ? AND inSnapshot(hash))");
787
788 if (!deleteBlocksStatement)
789 return;
790
791 auto result = deleteBlocksStatement->Prepare(mProjectInfo.Id).Run();
792
793 if (!result.IsOk())
794 return;
795
796 auto deleteHashesStatement = db->CreateStatement(
797 "DELETE FROM block_hashes WHERE project_id = ? AND NOT inSnapshot(hash)");
798
799 if (!deleteHashesStatement)
800 return;
801
802 result = deleteHashesStatement->Prepare(mProjectInfo.Id).Run();
803
804 if (!result.IsOk())
805 return;
806
807 transaction.Commit();
808}
Transaction BeginTransaction(std::string name)
Starts a new transaction.
Definition: Connection.cpp:200

References BasicUI::Get(), and ToUpper().

Referenced by RemoteProjectSnapshot().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ DoCancel()

void audacity::cloud::audiocom::sync::RemoteProjectSnapshot::DoCancel ( )
private

Definition at line 353 of file RemoteProjectSnapshot.cpp.

354{
355 if (mState.load(std::memory_order_acquire) != State::Downloading)
356 return;
357
359
360 mRequestsCV.notify_one();
361
362 {
363 auto responsesLock = std::lock_guard { mResponsesMutex };
364 for (auto& response : mResponses)
365 response->abort();
366 }
367}

References Cancelled, Downloading, mRequestsCV, mResponses, mResponsesMutex, mState, and SetState().

Referenced by Cancel(), and ~RemoteProjectSnapshot().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ DownloadBlob()

void audacity::cloud::audiocom::sync::RemoteProjectSnapshot::DownloadBlob ( std::string  url,
SuccessHandler  onSuccess,
int  retries = 3 
)
private

Definition at line 369 of file RemoteProjectSnapshot.cpp.

371{
372 using namespace audacity::network_manager;
373
374 auto request = Request(url);
375
376 auto response = NetworkManager::GetInstance().doGet(request);
377
378 {
379 auto responsesLock = std::lock_guard { mResponsesMutex };
380 mResponses.push_back(response);
381 }
382
383 response->setRequestFinishedCallback(
384 [this, self = weak_from_this(), onSuccess = std::move(onSuccess), retries, response](auto)
385 {
386 auto strong = self.lock();
387 if(!strong)
388 return;
389
390 mDownloadedBytes.fetch_add(
391 response->getBytesAvailable(), std::memory_order_acq_rel);
392
393 RemoveResponse(response.get());
394
395 auto responseResult = GetResponseResult(*response, false);
396
397 if (responseResult.Code == SyncResultCode::Cancelled)
398 return;
399
400 if (
401 responseResult.Code != SyncResultCode::Success &&
402 responseResult.Code != SyncResultCode::ConnectionFailed)
403 {
404 OnFailure(std::move(responseResult));
405 return;
406 }
407
408 if (responseResult.Code == SyncResultCode::ConnectionFailed)
409 {
410 if (retries <= 0)
411 {
412 OnFailure(std::move(responseResult));
413 return;
414 }
415
417 response->getRequest().getURL(), std::move(onSuccess),
418 retries - 1);
419
420 return;
421 }
422
423 onSuccess(response);
424 });
425}
void DownloadBlob(std::string url, SuccessHandler onSuccess, int retries=3)
void RemoveResponse(audacity::network_manager::IResponse *response)
ResponsePtr doGet(const Request &request)
ResponseResult GetResponseResult(IResponse &response, bool readBody)

References audacity::cloud::audiocom::Cancelled, audacity::cloud::audiocom::ConnectionFailed, audacity::network_manager::NetworkManager::doGet(), DownloadBlob(), audacity::network_manager::NetworkManager::GetInstance(), audacity::cloud::audiocom::GetResponseResult(), mDownloadedBytes, mResponses, mResponsesMutex, RemoveResponse(), and audacity::cloud::audiocom::Success.

Referenced by DownloadBlob().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ GetProjectId()

std::string_view audacity::cloud::audiocom::sync::RemoteProjectSnapshot::GetProjectId ( ) const

Definition at line 221 of file RemoteProjectSnapshot.cpp.

222{
223 return mProjectInfo.Id;
224}

References audacity::cloud::audiocom::sync::ProjectInfo::Id, and mProjectInfo.

◆ GetTransferStats()

TransferStats audacity::cloud::audiocom::sync::RemoteProjectSnapshot::GetTransferStats ( ) const

Definition at line 206 of file RemoteProjectSnapshot.cpp.

207{
208 const auto duration =
209 mState.load(std::memory_order_acquire) == State::Downloading ?
210 Clock::now() - mStartTime :
212
213 return TransferStats {}
214 .SetBlocksTransferred(mDownloadedBlocks.load())
215 .SetBytesTransferred(mDownloadedBytes.load())
216 .SetProjectFilesTransferred(mProjectDownloaded.load() ? 1 : 0)
217 .SetTransferDuration(
218 std::chrono::duration_cast<TransferStats::Duration>(duration));
219}

References Downloading, mDownloadedBlocks, mDownloadedBytes, mEndTime, mProjectDownloaded, mStartTime, mState, audacity::cloud::audiocom::TransferStats::SetBlocksTransferred(), audacity::cloud::audiocom::TransferStats::SetBytesTransferred(), audacity::cloud::audiocom::TransferStats::SetProjectFilesTransferred(), and audacity::cloud::audiocom::TransferStats::SetTransferDuration().

Here is the call graph for this function:

◆ InProgress()

bool audacity::cloud::audiocom::sync::RemoteProjectSnapshot::InProgress ( ) const
private

Definition at line 717 of file RemoteProjectSnapshot.cpp.

718{
719 return mState.load(std::memory_order_acquire) == State::Downloading;
720}

References mState.

Referenced by SetupBlocksCopy().

Here is the caller graph for this function:

◆ MarkProjectInDB()

void audacity::cloud::audiocom::sync::RemoteProjectSnapshot::MarkProjectInDB ( bool  successfulDownload)
private

Definition at line 661 of file RemoteProjectSnapshot.cpp.

662{
664 return;
665
666 auto& db = CloudProjectsDatabase::Get();
667 auto currentData = db.GetProjectData(mProjectInfo.Id);
668
669 auto data = currentData ? *currentData : DBProjectData {};
670
671 data.ProjectId = mProjectInfo.Id;
672 data.SnapshotId = mSnapshotInfo.Id;
673 data.SyncStatus = successfulDownload ? DBProjectData::SyncStatusSynced :
675 data.LastRead = wxDateTime::Now().GetTicks();
676 data.LocalPath = mPath;
677
678 if (data.SavesCount == 0)
679 data.SavesCount = 1;
680
681 // For the downloaded projects - we don't need to show the dialog
682 data.FirstSyncDialogShown = true;
683
684 db.UpdateProjectData(data);
685
686 if (successfulDownload)
687 db.SetProjectUserSlug(mProjectInfo.Id, mProjectInfo.Username);
688}

References BasicUI::Get(), and audacity::cloud::audiocom::sync::DBProjectData::ProjectId.

Referenced by RemoteProjectSnapshot().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ OnBlockDownloaded()

void audacity::cloud::audiocom::sync::RemoteProjectSnapshot::OnBlockDownloaded ( std::string  blockHash,
audacity::network_manager::ResponsePtr  response 
)
private

Definition at line 540 of file RemoteProjectSnapshot.cpp.

542{
543 const auto compressedData = ReadResponseData(*response);
544
545 const auto blockData =
546 DecompressBlock(compressedData.data(), compressedData.size());
547
548 if (!blockData)
549 {
550 OnFailure(
552 audacity::ToUTF8(XO("Failed to decompress the Cloud project block")
553 .Translation()) });
554 return;
555 }
556
558 auto transaction = db->BeginTransaction("b_" + blockHash);
559
560 auto hashesStatement = db->CreateStatement(
561 "INSERT INTO block_hashes (project_id, block_id, hash) VALUES (?1, ?2, ?3) "
562 "ON CONFLICT(project_id, block_id) DO UPDATE SET hash = ?3");
563
564 auto result =
565 hashesStatement->Prepare(mProjectInfo.Id, blockData->BlockId, blockHash)
566 .Run();
567
568 if (!result.IsOk())
569 {
570 OnFailure(
573 result.GetErrors().front().GetErrorString().Translation()) });
574 return;
575 }
576
577 auto blockStatement = db->CreateStatement(
578 "INSERT INTO " + mSnapshotDBName +
579 ".sampleblocks (blockid, sampleformat, summin, summax, sumrms, summary256, summary64k, samples) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8) "
580 "ON CONFLICT(blockid) DO UPDATE SET sampleformat = ?2, summin = ?3, summax = ?4, sumrms = ?5, summary256 = ?6, summary64k = ?7, samples = ?8");
581
582 if (!blockStatement)
583 {
584 OnFailure(
587 blockStatement.GetError().GetErrorString().Translation()) });
588 return;
589 }
590
591 auto& preparedStatement = blockStatement->Prepare();
592
593 preparedStatement.Bind(1, blockData->BlockId);
594 preparedStatement.Bind(2, static_cast<int64_t>(blockData->Format));
595 preparedStatement.Bind(3, blockData->BlockMinMaxRMS.Min);
596 preparedStatement.Bind(4, blockData->BlockMinMaxRMS.Max);
597 preparedStatement.Bind(5, blockData->BlockMinMaxRMS.RMS);
598 preparedStatement.Bind(
599 6, blockData->Summary256.data(),
600 blockData->Summary256.size() * sizeof(MinMaxRMS), false);
601 preparedStatement.Bind(
602 7, blockData->Summary64k.data(),
603 blockData->Summary64k.size() * sizeof(MinMaxRMS), false);
604 preparedStatement.Bind(
605 8, blockData->Data.data(), blockData->Data.size(), false);
606
607 result = preparedStatement.Run();
608
609 if (!result.IsOk())
610 {
611 OnFailure(
614 result.GetErrors().front().GetErrorString().Translation()) });
615 return;
616 }
617
618 if (auto error = transaction.Commit(); error.IsError())
619 {
621 audacity::ToUTF8(error.GetErrorString().Translation()) });
622 return;
623 }
624
625 mDownloadedBlocks.fetch_add(1, std::memory_order_acq_rel);
626
628}
XO("Cut/Copy/Paste")
std::vector< uint8_t > ReadResponseData(audacity::network_manager::IResponse &response)
std::optional< DecompressedBlock > DecompressBlock(const void *data, const std::size_t size)
std::string ToUTF8(const std::wstring &wstr)

References audacity::cloud::audiocom::sync::DecompressBlock(), BasicUI::Get(), audacity::cloud::audiocom::InternalClientError, audacity::cloud::audiocom::sync::anonymous_namespace{RemoteProjectSnapshot.cpp}::ReadResponseData(), audacity::ToUTF8(), and XO().

Here is the call graph for this function:

◆ OnFailure()

void audacity::cloud::audiocom::sync::RemoteProjectSnapshot::OnFailure ( ResponseResult  result)
private

Definition at line 630 of file RemoteProjectSnapshot.cpp.

631{
633 mCallback({ result,
634 mDownloadedBlocks.load(std::memory_order_acquire) +
635 mCopiedBlocks.load(std::memory_order_acquire),
637 mProjectDownloaded.load(std::memory_order_acquire) });
638}

Referenced by SetupBlocksCopy().

Here is the caller graph for this function:

◆ OnProjectBlobDownloaded()

void audacity::cloud::audiocom::sync::RemoteProjectSnapshot::OnProjectBlobDownloaded ( audacity::network_manager::ResponsePtr  response)
private

Definition at line 445 of file RemoteProjectSnapshot.cpp.

447{
448 const std::vector<uint8_t> data = ReadResponseData(*response);
449 uint64_t dictSize = 0;
450
451 if (data.size() < sizeof(uint64_t))
452 {
454 return;
455 }
456
457 std::memcpy(&dictSize, data.data(), sizeof(uint64_t));
458
459 if (!IsLittleEndian())
460 dictSize = SwapIntBytes(dictSize);
461
462 if (data.size() < sizeof(uint64_t) + dictSize)
463 {
465 return;
466 }
467
469 auto transaction = db->BeginTransaction("p_" + mProjectInfo.Id);
470
471 auto updateProjectStatement = db->CreateStatement(
472 "INSERT INTO " + mSnapshotDBName +
473 ".project (id, dict, doc) VALUES (1, ?1, ?2) "
474 "ON CONFLICT(id) DO UPDATE SET dict = ?1, doc = ?2");
475
476 if (!updateProjectStatement)
477 {
479 audacity::ToUTF8(updateProjectStatement.GetError()
480 .GetErrorString()
481 .Translation()) });
482 return;
483 }
484
485 auto& preparedUpdateProjectStatement = updateProjectStatement->Prepare();
486
487 preparedUpdateProjectStatement.Bind(
488 1, data.data() + sizeof(uint64_t), dictSize, false);
489
490 preparedUpdateProjectStatement.Bind(
491 2, data.data() + sizeof(uint64_t) + dictSize,
492 data.size() - sizeof(uint64_t) - dictSize, false);
493
494 auto result = preparedUpdateProjectStatement.Run();
495
496 if (!result.IsOk())
497 {
498 OnFailure(
501 result.GetErrors().front().GetErrorString().Translation()) });
502
503 return;
504 }
505
506 auto deleteAutosaveStatement = db->CreateStatement(
507 "DELETE FROM " + mSnapshotDBName + ".autosave WHERE id = 1");
508
509 if (!deleteAutosaveStatement)
510 {
512 audacity::ToUTF8(deleteAutosaveStatement.GetError()
513 .GetErrorString()
514 .Translation()) });
515 return;
516 }
517
518 result = deleteAutosaveStatement->Prepare().Run();
519
520 if (!result.IsOk())
521 {
522 OnFailure(
525 result.GetErrors().front().GetErrorString().Translation()) });
526 return;
527 }
528
529 if (auto error = transaction.Commit(); error.IsError())
530 {
532 audacity::ToUTF8(error.GetErrorString().Translation()) });
533 return;
534 }
535
536 mProjectDownloaded.store(true, std::memory_order_release);
538}
constexpr IntType SwapIntBytes(IntType value) noexcept
Swap bytes in an integer.
Definition: MemoryX.h:377
bool IsLittleEndian() noexcept
Check that machine is little-endian.
Definition: MemoryX.h:368

References BasicUI::Get(), audacity::cloud::audiocom::InternalClientError, IsLittleEndian(), audacity::cloud::audiocom::sync::anonymous_namespace{RemoteProjectSnapshot.cpp}::ReadResponseData(), SwapIntBytes(), audacity::ToUTF8(), and audacity::cloud::audiocom::UnexpectedResponse.

Here is the call graph for this function:

◆ operator=() [1/2]

RemoteProjectSnapshot & audacity::cloud::audiocom::sync::RemoteProjectSnapshot::operator= ( const RemoteProjectSnapshot )
delete

◆ operator=() [2/2]

RemoteProjectSnapshot & audacity::cloud::audiocom::sync::RemoteProjectSnapshot::operator= ( RemoteProjectSnapshot &&  )
delete

◆ RemoveResponse()

void audacity::cloud::audiocom::sync::RemoteProjectSnapshot::RemoveResponse ( audacity::network_manager::IResponse response)
private

Definition at line 640 of file RemoteProjectSnapshot.cpp.

642{
643 {
644 auto lock = std::lock_guard { mResponsesMutex };
645 mResponses.erase(
646 std::remove_if(
647 mResponses.begin(), mResponses.end(),
648 [response](auto& r) { return r.get() == response; }),
649 mResponses.end());
650
651 if (mResponses.empty())
652 mResponsesEmptyCV.notify_all();
653 }
654 {
655 auto lock = std::lock_guard { mRequestsMutex };
657 mRequestsCV.notify_one();
658 }
659}

Referenced by DownloadBlob().

Here is the caller graph for this function:

◆ ReportProgress()

void audacity::cloud::audiocom::sync::RemoteProjectSnapshot::ReportProgress ( )
private

Definition at line 690 of file RemoteProjectSnapshot.cpp.

691{
692 if (mState.load(std::memory_order_acquire) != State::Downloading)
693 return;
694
695 const auto projectDownloaded =
696 mProjectDownloaded.load(std::memory_order_acquire);
697 const auto blocksDownloaded =
698 mDownloadedBlocks.load(std::memory_order_acquire);
699
700 const auto blockCopied = mCopiedBlocks.load(std::memory_order_acquire);
701
702 const auto processedBlocks = blocksDownloaded + blockCopied;
703
704 const auto completed =
705 processedBlocks == mMissingBlocks && projectDownloaded;
706
707 if (completed)
708 {
711 MarkProjectInDB(true);
712 }
713
714 mCallback({ {}, processedBlocks, mMissingBlocks, projectDownloaded });
715}

References mState.

Referenced by SetupBlocksCopy().

Here is the caller graph for this function:

◆ RequestsThread()

void audacity::cloud::audiocom::sync::RemoteProjectSnapshot::RequestsThread ( )
private

Definition at line 722 of file RemoteProjectSnapshot.cpp.

723{
724 constexpr auto MAX_CONCURRENT_REQUESTS = 6;
725
726 while (InProgress())
727 {
728 std::pair<std::string, SuccessHandler> request;
729
730 {
731 auto lock = std::unique_lock { mRequestsMutex };
732
733 if (mRequestsInProgress >= MAX_CONCURRENT_REQUESTS)
734 {
735 mRequestsCV.wait(
736 lock,
737 [this, MAX_CONCURRENT_REQUESTS] {
738 return mRequestsInProgress < MAX_CONCURRENT_REQUESTS ||
739 !InProgress();
740 });
741 }
742
743 if (!InProgress())
744 return;
745
746 if (mNextRequestIndex >= mRequests.size())
747 return;
748
749 request = mRequests[mNextRequestIndex++];
751 }
752
753 DownloadBlob(std::move(request.first), std::move(request.second), 3);
754
755 // TODO: Random sleep to avoid overloading the server
756 std::this_thread::sleep_for(std::chrono::milliseconds(50));
757 }
758}

Referenced by RemoteProjectSnapshot().

Here is the caller graph for this function:

◆ SetState()

void audacity::cloud::audiocom::sync::RemoteProjectSnapshot::SetState ( State  state)
private

Definition at line 760 of file RemoteProjectSnapshot.cpp.

761{
762 if (state != State::Downloading)
763 mEndTime = Clock::now();
764
765 mState.exchange(state);
766}

References mState.

Referenced by DoCancel().

Here is the caller graph for this function:

◆ SetupBlocksCopy()

void audacity::cloud::audiocom::sync::RemoteProjectSnapshot::SetupBlocksCopy ( const std::string &  dbName,
std::unordered_set< std::string >  blocks 
)
private

Definition at line 251 of file RemoteProjectSnapshot.cpp.

253{
254 // Still, better be safe than sorry
255 if (dbName == mSnapshotDBName)
256 return;
257
258 if (blocks.empty())
259 return;
260
261 mCopyBlocksFuture = std::async(
262 std::launch::async,
263 [this, dbName = dbName, blocks = std::move(blocks)]()
264 {
265 const auto queryString =
266 "INSERT INTO " + mSnapshotDBName +
267 ".sampleblocks "
268 "SELECT * FROM " +
269 dbName +
270 ".sampleblocks WHERE blockid IN (SELECT block_id FROM block_hashes WHERE hash = ?)";
271
272 // Only lock DB for one block a time so the download thread can
273 // continue to work
274 for (const auto& block : blocks)
275 {
276 if (!InProgress())
277 return false;
278
280
281 auto copyBlocksStatement = db->CreateStatement(queryString);
282
283 if (!copyBlocksStatement)
284 {
286 audacity::ToUTF8(copyBlocksStatement.GetError()
287 .GetErrorString()
288 .Translation()) });
289
290 return false;
291 }
292
293 auto result = copyBlocksStatement->Prepare(block).Run();
294
295 if (!result.IsOk())
296 {
298 audacity::ToUTF8(result.GetErrors()
299 .front()
300 .GetErrorString()
301 .Translation()) });
302 return false;
303 }
304
305 const auto rowsUpdated = result.GetModifiedRowsCount();
306 mCopiedBlocks.fetch_add(rowsUpdated, std::memory_order_acq_rel);
307
309 }
310
311 return true;
312 });
313}

References audacity::sqlite::Connection::CreateStatement(), audacity::cloud::audiocom::sync::CloudProjectsDatabase::Get(), audacity::cloud::audiocom::sync::CloudProjectsDatabase::GetConnection(), InProgress(), audacity::cloud::audiocom::InternalClientError, mCopiedBlocks, mCopyBlocksFuture, mSnapshotDBName, OnFailure(), ReportProgress(), and audacity::ToUTF8().

Referenced by RemoteProjectSnapshot().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Sync()

std::shared_ptr< RemoteProjectSnapshot > audacity::cloud::audiocom::sync::RemoteProjectSnapshot::Sync ( ProjectInfo  projectInfo,
SnapshotInfo  snapshotInfo,
std::string  path,
RemoteProjectSnapshotStateCallback  callback,
bool  downloadDetached 
)
static

Definition at line 168 of file RemoteProjectSnapshot.cpp.

171{
172 auto snapshot = std::make_shared<RemoteProjectSnapshot>(
173 Tag {}, std::move(projectInfo), std::move(snapshotInfo), std::move(path),
174 std::move(callback), downloadDetached);
175
176 if (snapshot->mAttachedDBNames.empty())
177 {
178 snapshot->mCallback(
181 XO("Failed to attach to the Cloud project database")
182 .Translation()) },
183 0,
184 0,
185 false });
186
187 return {};
188 }
189
190 if (snapshot->mNothingToDo)
191 return {};
192
193 return snapshot;
194}

References audacity::cloud::audiocom::InternalClientError, audacity::ToUTF8(), and XO().

Here is the call graph for this function:

Member Data Documentation

◆ mAttachedDBNames

std::vector<std::string> audacity::cloud::audiocom::sync::RemoteProjectSnapshot::mAttachedDBNames
private

Definition at line 130 of file RemoteProjectSnapshot.h.

Referenced by AttachOriginalDB(), and RemoteProjectSnapshot().

◆ mCallback

RemoteProjectSnapshotStateCallback audacity::cloud::audiocom::sync::RemoteProjectSnapshot::mCallback
private

Definition at line 128 of file RemoteProjectSnapshot.h.

Referenced by Cancel(), and RemoteProjectSnapshot().

◆ mCopiedBlocks

std::atomic<int64_t> audacity::cloud::audiocom::sync::RemoteProjectSnapshot::mCopiedBlocks { 0 }
private

Definition at line 155 of file RemoteProjectSnapshot.h.

Referenced by Cancel(), and SetupBlocksCopy().

◆ mCopyBlocksFuture

std::optional<std::future<bool> > audacity::cloud::audiocom::sync::RemoteProjectSnapshot::mCopyBlocksFuture
private

Definition at line 160 of file RemoteProjectSnapshot.h.

Referenced by SetupBlocksCopy(), and ~RemoteProjectSnapshot().

◆ mDownloadDetached

const bool audacity::cloud::audiocom::sync::RemoteProjectSnapshot::mDownloadDetached { false }
private

Definition at line 165 of file RemoteProjectSnapshot.h.

Referenced by RemoteProjectSnapshot().

◆ mDownloadedBlocks

std::atomic<int64_t> audacity::cloud::audiocom::sync::RemoteProjectSnapshot::mDownloadedBlocks { 0 }
private

Definition at line 154 of file RemoteProjectSnapshot.h.

Referenced by Cancel(), and GetTransferStats().

◆ mDownloadedBytes

std::atomic<int64_t> audacity::cloud::audiocom::sync::RemoteProjectSnapshot::mDownloadedBytes { 0 }
private

Definition at line 156 of file RemoteProjectSnapshot.h.

Referenced by DownloadBlob(), and GetTransferStats().

◆ mEndTime

TimePoint audacity::cloud::audiocom::sync::RemoteProjectSnapshot::mEndTime
private

Definition at line 138 of file RemoteProjectSnapshot.h.

Referenced by GetTransferStats().

◆ mMissingBlocks

int64_t audacity::cloud::audiocom::sync::RemoteProjectSnapshot::mMissingBlocks { 0 }
private

Definition at line 158 of file RemoteProjectSnapshot.h.

Referenced by Cancel(), and RemoteProjectSnapshot().

◆ mNextRequestIndex

size_t audacity::cloud::audiocom::sync::RemoteProjectSnapshot::mNextRequestIndex { 0 }
private

Definition at line 147 of file RemoteProjectSnapshot.h.

◆ mNothingToDo

bool audacity::cloud::audiocom::sync::RemoteProjectSnapshot::mNothingToDo { false }
private

Definition at line 164 of file RemoteProjectSnapshot.h.

Referenced by RemoteProjectSnapshot().

◆ mPath

const std::string audacity::cloud::audiocom::sync::RemoteProjectSnapshot::mPath
private

Definition at line 127 of file RemoteProjectSnapshot.h.

Referenced by RemoteProjectSnapshot().

◆ mProjectDownloaded

std::atomic<bool> audacity::cloud::audiocom::sync::RemoteProjectSnapshot::mProjectDownloaded { false }
private

Definition at line 162 of file RemoteProjectSnapshot.h.

Referenced by Cancel(), and GetTransferStats().

◆ mProjectInfo

const ProjectInfo audacity::cloud::audiocom::sync::RemoteProjectSnapshot::mProjectInfo
private

◆ mRequests

std::vector<std::pair<std::string, SuccessHandler> > audacity::cloud::audiocom::sync::RemoteProjectSnapshot::mRequests
private

Definition at line 144 of file RemoteProjectSnapshot.h.

Referenced by RemoteProjectSnapshot().

◆ mRequestsCV

std::condition_variable audacity::cloud::audiocom::sync::RemoteProjectSnapshot::mRequestsCV
private

Definition at line 142 of file RemoteProjectSnapshot.h.

Referenced by DoCancel().

◆ mRequestsInProgress

int audacity::cloud::audiocom::sync::RemoteProjectSnapshot::mRequestsInProgress { 0 }
private

Definition at line 146 of file RemoteProjectSnapshot.h.

◆ mRequestsMutex

std::mutex audacity::cloud::audiocom::sync::RemoteProjectSnapshot::mRequestsMutex
private

Definition at line 141 of file RemoteProjectSnapshot.h.

◆ mRequestsThread

std::thread audacity::cloud::audiocom::sync::RemoteProjectSnapshot::mRequestsThread
private

Definition at line 140 of file RemoteProjectSnapshot.h.

Referenced by RemoteProjectSnapshot(), and ~RemoteProjectSnapshot().

◆ mResponses

std::vector<std::shared_ptr<audacity::network_manager::IResponse> > audacity::cloud::audiocom::sync::RemoteProjectSnapshot::mResponses
private

Definition at line 151 of file RemoteProjectSnapshot.h.

Referenced by DoCancel(), DownloadBlob(), and ~RemoteProjectSnapshot().

◆ mResponsesEmptyCV

std::condition_variable audacity::cloud::audiocom::sync::RemoteProjectSnapshot::mResponsesEmptyCV
private

Definition at line 152 of file RemoteProjectSnapshot.h.

Referenced by ~RemoteProjectSnapshot().

◆ mResponsesMutex

std::mutex audacity::cloud::audiocom::sync::RemoteProjectSnapshot::mResponsesMutex
private

Definition at line 149 of file RemoteProjectSnapshot.h.

Referenced by DoCancel(), DownloadBlob(), and ~RemoteProjectSnapshot().

◆ mSnapshotDBName

const std::string audacity::cloud::audiocom::sync::RemoteProjectSnapshot::mSnapshotDBName
private

Definition at line 124 of file RemoteProjectSnapshot.h.

Referenced by RemoteProjectSnapshot(), and SetupBlocksCopy().

◆ mSnapshotInfo

const SnapshotInfo audacity::cloud::audiocom::sync::RemoteProjectSnapshot::mSnapshotInfo
private

Definition at line 126 of file RemoteProjectSnapshot.h.

Referenced by CalculateKnownBlocks(), and RemoteProjectSnapshot().

◆ mStartTime

TimePoint audacity::cloud::audiocom::sync::RemoteProjectSnapshot::mStartTime { Clock::now() }
private

Definition at line 137 of file RemoteProjectSnapshot.h.

Referenced by GetTransferStats().

◆ mState

std::atomic<State> audacity::cloud::audiocom::sync::RemoteProjectSnapshot::mState { State::Downloading }
private

Definition at line 132 of file RemoteProjectSnapshot.h.

Referenced by DoCancel(), and GetTransferStats().


The documentation for this class was generated from the following files: