GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/publish/repository_transaction.cc
Date: 2025-07-13 02:35:07
Exec Total Coverage
Lines: 0 80 0.0%
Branches: 0 182 0.0%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5
6 #include <string>
7
8 #include "backoff.h"
9 #include "catalog_mgr_ro.h"
10 #include "catalog_mgr_rw.h"
11 #include "directory_entry.h"
12 #include "manifest.h"
13 #include "publish/except.h"
14 #include "publish/repository.h"
15 #include "publish/repository_util.h"
16 #include "publish/settings.h"
17 #include "util/exception.h"
18 #include "util/logging.h"
19 #include "util/pointer.h"
20 #include "util/posix.h"
21
22 namespace publish {
23
24
25 void Publisher::TransactionRetry() {
26 bool waiting_on_lease = false;
27 if (managed_node_.IsValid()) {
28 const int rvi = managed_node_->Check(false /* is_quiet */);
29 if (rvi != 0)
30 throw EPublish("cannot establish writable mountpoint");
31 }
32
33 BackoffThrottle throttle(500, 5000, 10000);
34 // Negative timeouts (i.e.: no retry) will result in a deadline that has
35 // already passed and thus has the correct effect
36 uint64_t deadline = platform_monotonic_time()
37 + settings_.transaction().GetTimeoutS();
38 if (settings_.transaction().GetTimeoutS() == 0)
39 deadline = uint64_t(-1);
40
41 while (true) {
42 try {
43 TransactionImpl(waiting_on_lease);
44 break;
45 } catch (const publish::EPublish &e) {
46 if (e.failure() != EPublish::kFailTransactionState) {
47 session_->Drop();
48 in_transaction_.Clear();
49 }
50
51 if ((e.failure() == EPublish::kFailTransactionState)
52 || (e.failure() == EPublish::kFailLeaseBusy)) {
53 if (platform_monotonic_time() > deadline)
54 throw;
55
56 waiting_on_lease = true;
57 LogCvmfs(kLogCvmfs, kLogStdout, "repository busy, retrying");
58 throttle.Throttle();
59 continue;
60 }
61
62 throw;
63 } // try-catch
64 } // while (true)
65
66 if (managed_node_.IsValid())
67 managed_node_->Open();
68 }
69
70
71 void Publisher::TransactionImpl(bool waiting_on_lease) {
72 if (in_transaction_.IsSet()) {
73 throw EPublish("another transaction is already open",
74 EPublish::kFailTransactionState);
75 }
76
77 InitSpoolArea();
78
79 // On error, Transaction() will release the transaction lock and drop
80 // the session
81 session_->Acquire();
82
83 // We might have a valid lease for a non-existing path. Nevertheless, we run
84 // run into problems when merging catalogs later, so for the time being we
85 // disallow transactions on non-existing paths.
86 if (!settings_.transaction().lease_path().empty()) {
87 const std::string path = GetParentPath(
88 "/" + settings_.transaction().lease_path());
89 catalog::SimpleCatalogManager *catalog_mgr = GetSimpleCatalogManager();
90 catalog::DirectoryEntry dirent;
91 const bool retval = catalog_mgr->LookupPath(path, catalog::kLookupDefault,
92 &dirent);
93 if (!retval) {
94 throw EPublish("cannot open transaction on non-existing path " + path,
95 EPublish::kFailLeaseNoEntry);
96 }
97 if (!dirent.IsDirectory()) {
98 throw EPublish(
99 "cannot open transaction on " + path + ", which is not a directory",
100 EPublish::kFailLeaseNoDir);
101 }
102 }
103
104 const UniquePtr<CheckoutMarker> marker(CheckoutMarker::CreateFrom(
105 settings_.transaction().spool_area().checkout_marker()));
106 // TODO(jblomer): take root hash from r/o mountpoint?
107
108
109 if (settings_.storage().type() == upload::SpoolerDefinition::Gateway
110 && waiting_on_lease) {
111 DownloadRootObjects(settings_.url(), settings_.fqrn(),
112 settings_.transaction().spool_area().tmp_dir());
113 const int rvi = managed_node_->Check(true /* is_quiet */);
114 if (rvi != 0)
115 throw EPublish("cannot establish writable mountpoint");
116 }
117
118 in_transaction_.Set();
119 ConstructSpoolers();
120 if (marker.IsValid())
121 settings_.GetTransaction()->SetBaseHash(marker->hash());
122 else
123 settings_.GetTransaction()->SetBaseHash(manifest_->catalog_hash());
124
125 if (settings_.transaction().HasTemplate()) {
126 LogCvmfs(kLogCvmfs, llvl_ | kLogStdout | kLogNoLinebreak,
127 "CernVM-FS: cloning template %s --> %s ... ",
128 settings_.transaction().template_from().c_str(),
129 settings_.transaction().template_to().c_str());
130 ConstructSyncManagers();
131
132 try {
133 catalog_mgr_->CloneTree(settings_.transaction().template_from(),
134 settings_.transaction().template_to());
135 } catch (const ECvmfsException &e) {
136 const std::string panic_msg = e.what();
137 in_transaction_.Clear();
138 // TODO(aandvalenzuela): release session token (gateway publishing)
139 throw publish::EPublish("cannot clone directory tree. " + panic_msg,
140 publish::EPublish::kFailInput);
141 }
142
143 Sync();
144 SendTalkCommand(
145 settings_.transaction().spool_area().readonly_talk_socket(),
146 "chroot " + settings_.transaction().base_hash().ToString() + "\n");
147 LogCvmfs(kLogCvmfs, llvl_ | kLogStdout, "[done]");
148 // TODO(jblomer): fix-me
149 // PushReflog();
150 }
151
152 LogCvmfs(kLogCvmfs, llvl_ | kLogDebug | kLogSyslog, "(%s) opened transaction",
153 settings_.fqrn().c_str());
154 }
155
156 } // namespace publish
157