Skip to content

Commit 00cc54f

Browse files
committed
Introduce a separate serve protocol
To start, it is just a clone of the common protocol. But now that we have the separate protocol implementations, we can add versioning information without the versions of one protocol leaking into another. Using the infrastructure from the previous commit, we don't have to duplicate code for shared behavior. Motivation: No more perverse incentives. [0] did some awkward things because the serializers did not store the version. I don't want anyone making changes to be pushed towards keeping the serialization logic with the core data types just because it's easier or the alternative is tedious. [0]: fe1f34f
1 parent c7f1d86 commit 00cc54f

File tree

15 files changed

+344
-39
lines changed

15 files changed

+344
-39
lines changed

src/libstore/legacy-ssh-store.cc

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@
33
#include "pool.hh"
44
#include "remote-store.hh"
55
#include "serve-protocol.hh"
6+
#include "serve-protocol-impl.hh"
67
#include "build-result.hh"
78
#include "store-api.hh"
89
#include "path-with-outputs.hh"
9-
#include "common-protocol.hh"
10-
#include "common-protocol-impl.hh"
1110
#include "ssh.hh"
1211
#include "derivations.hh"
1312
#include "callback.hh"
@@ -50,37 +49,31 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor
5049
bool good = true;
5150

5251
/**
53-
* Coercion to `CommonProto::ReadConn`. This makes it easy to use the
54-
* factored out common protocol serialisers with a
52+
* Coercion to `ServeProto::ReadConn`. This makes it easy to use the
53+
* factored out serve protocol searlizers with a
5554
* `LegacySSHStore::Connection`.
5655
*
57-
* The common protocol connection types are unidirectional, unlike
56+
* The serve protocol connection types are unidirectional, unlike
5857
* this type.
59-
*
60-
* @todo Use server protocol serializers, not common protocol
61-
* serializers, once we have made that distiction.
6258
*/
63-
operator CommonProto::ReadConn ()
59+
operator ServeProto::ReadConn ()
6460
{
65-
return CommonProto::ReadConn {
61+
return ServeProto::ReadConn {
6662
.from = from,
6763
};
6864
}
6965

7066
/*
71-
* Coercion to `CommonProto::WriteConn`. This makes it easy to use the
72-
* factored out common protocol searlizers with a
67+
* Coercion to `ServeProto::WriteConn`. This makes it easy to use the
68+
* factored out serve protocol searlizers with a
7369
* `LegacySSHStore::Connection`.
7470
*
75-
* The common protocol connection types are unidirectional, unlike
71+
* The serve protocol connection types are unidirectional, unlike
7672
* this type.
77-
*
78-
* @todo Use server protocol serializers, not common protocol
79-
* serializers, once we have made that distiction.
8073
*/
81-
operator CommonProto::WriteConn ()
74+
operator ServeProto::WriteConn ()
8275
{
83-
return CommonProto::WriteConn {
76+
return ServeProto::WriteConn {
8477
.to = to,
8578
};
8679
}
@@ -183,7 +176,7 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor
183176
auto deriver = readString(conn->from);
184177
if (deriver != "")
185178
info->deriver = parseStorePath(deriver);
186-
info->references = CommonProto::Serialise<StorePathSet>::read(*this, *conn);
179+
info->references = ServeProto::Serialise<StorePathSet>::read(*this, *conn);
187180
readLongLong(conn->from); // download size
188181
info->narSize = readLongLong(conn->from);
189182

@@ -217,7 +210,7 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor
217210
<< printStorePath(info.path)
218211
<< (info.deriver ? printStorePath(*info.deriver) : "")
219212
<< info.narHash.to_string(Base16, false);
220-
CommonProto::write(*this, *conn, info.references);
213+
ServeProto::write(*this, *conn, info.references);
221214
conn->to
222215
<< info.registrationTime
223216
<< info.narSize
@@ -246,7 +239,7 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor
246239
conn->to
247240
<< exportMagic
248241
<< printStorePath(info.path);
249-
CommonProto::write(*this, *conn, info.references);
242+
ServeProto::write(*this, *conn, info.references);
250243
conn->to
251244
<< (info.deriver ? printStorePath(*info.deriver) : "")
252245
<< 0
@@ -331,7 +324,7 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor
331324
if (GET_PROTOCOL_MINOR(conn->remoteVersion) >= 3)
332325
conn->from >> status.timesBuilt >> status.isNonDeterministic >> status.startTime >> status.stopTime;
333326
if (GET_PROTOCOL_MINOR(conn->remoteVersion) >= 6) {
334-
auto builtOutputs = CommonProto::Serialise<DrvOutputs>::read(*this, *conn);
327+
auto builtOutputs = ServeProto::Serialise<DrvOutputs>::read(*this, *conn);
335328
for (auto && [output, realisation] : builtOutputs)
336329
status.builtOutputs.insert_or_assign(
337330
std::move(output.outputName),
@@ -409,10 +402,10 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor
409402
conn->to
410403
<< ServeProto::Command::QueryClosure
411404
<< includeOutputs;
412-
CommonProto::write(*this, *conn, paths);
405+
ServeProto::write(*this, *conn, paths);
413406
conn->to.flush();
414407

415-
for (auto & i : CommonProto::Serialise<StorePathSet>::read(*this, *conn))
408+
for (auto & i : ServeProto::Serialise<StorePathSet>::read(*this, *conn))
416409
out.insert(i);
417410
}
418411

@@ -425,10 +418,10 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor
425418
<< ServeProto::Command::QueryValidPaths
426419
<< false // lock
427420
<< maybeSubstitute;
428-
CommonProto::write(*this, *conn, paths);
421+
ServeProto::write(*this, *conn, paths);
429422
conn->to.flush();
430423

431-
return CommonProto::Serialise<StorePathSet>::read(*this, *conn);
424+
return ServeProto::Serialise<StorePathSet>::read(*this, *conn);
432425
}
433426

434427
void connect() override
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#pragma once
2+
/**
3+
* @file
4+
*
5+
* Template implementations (as opposed to mere declarations).
6+
*
7+
* This file is an exmample of the "impl.hh" pattern. See the
8+
* contributing guide.
9+
*/
10+
11+
#include "serve-protocol.hh"
12+
#include "length-prefixed-protocol-helper.hh"
13+
14+
namespace nix {
15+
16+
/* protocol-agnostic templates */
17+
18+
#define SERVE_USE_LENGTH_PREFIX_SERIALISER(TEMPLATE, T) \
19+
TEMPLATE T ServeProto::Serialise< T >::read(const Store & store, ServeProto::ReadConn conn) \
20+
{ \
21+
return LengthPrefixedProtoHelper<ServeProto, T >::read(store, conn); \
22+
} \
23+
TEMPLATE void ServeProto::Serialise< T >::write(const Store & store, ServeProto::WriteConn conn, const T & t) \
24+
{ \
25+
LengthPrefixedProtoHelper<ServeProto, T >::write(store, conn, t); \
26+
}
27+
28+
SERVE_USE_LENGTH_PREFIX_SERIALISER(template<typename T>, std::vector<T>)
29+
SERVE_USE_LENGTH_PREFIX_SERIALISER(template<typename T>, std::set<T>)
30+
SERVE_USE_LENGTH_PREFIX_SERIALISER(template<typename... Ts>, std::tuple<Ts...>)
31+
32+
#define COMMA_ ,
33+
SERVE_USE_LENGTH_PREFIX_SERIALISER(
34+
template<typename K COMMA_ typename V>,
35+
std::map<K COMMA_ V>)
36+
#undef COMMA_
37+
38+
/**
39+
* Use `CommonProto` where possible.
40+
*/
41+
template<typename T>
42+
struct ServeProto::Serialise
43+
{
44+
static T read(const Store & store, ServeProto::ReadConn conn)
45+
{
46+
return CommonProto::Serialise<T>::read(store,
47+
CommonProto::ReadConn { .from = conn.from });
48+
}
49+
static void write(const Store & store, ServeProto::WriteConn conn, const T & t)
50+
{
51+
CommonProto::Serialise<T>::write(store,
52+
CommonProto::WriteConn { .to = conn.to },
53+
t);
54+
}
55+
};
56+
57+
/* protocol-specific templates */
58+
59+
}

src/libstore/serve-protocol.cc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#include "serialise.hh"
2+
#include "util.hh"
3+
#include "path-with-outputs.hh"
4+
#include "store-api.hh"
5+
#include "serve-protocol.hh"
6+
#include "serve-protocol-impl.hh"
7+
#include "archive.hh"
8+
9+
#include <nlohmann/json.hpp>
10+
11+
namespace nix {
12+
13+
/* protocol-specific definitions */
14+
15+
}

src/libstore/serve-protocol.hh

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#pragma once
22
///@file
33

4+
#include "common-protocol.hh"
5+
46
namespace nix {
57

68
#define SERVE_MAGIC_1 0x390c9deb
@@ -10,6 +12,11 @@ namespace nix {
1012
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
1113
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)
1214

15+
16+
class Store;
17+
struct Source;
18+
19+
1320
/**
1421
* The "serve protocol", used by ssh:// stores.
1522
*
@@ -22,6 +29,57 @@ struct ServeProto
2229
* Enumeration of all the request types for the protocol.
2330
*/
2431
enum struct Command : uint64_t;
32+
33+
/**
34+
* A unidirectional read connection, to be used by the read half of the
35+
* canonical serializers below.
36+
*
37+
* This currently is just a `Source &`, but more fields will be added
38+
* later.
39+
*/
40+
struct ReadConn {
41+
Source & from;
42+
};
43+
44+
/**
45+
* A unidirectional write connection, to be used by the write half of the
46+
* canonical serializers below.
47+
*
48+
* This currently is just a `Sink &`, but more fields will be added
49+
* later.
50+
*/
51+
struct WriteConn {
52+
Sink & to;
53+
};
54+
55+
/**
56+
* Data type for canonical pairs of serialisers for the serve protocol.
57+
*
58+
* See https://en.cppreference.com/w/cpp/language/adl for the broader
59+
* concept of what is going on here.
60+
*/
61+
template<typename T>
62+
struct Serialise;
63+
// This is the definition of `Serialise` we *want* to put here, but
64+
// do not do so.
65+
//
66+
// See `worker-protocol.hh` for a longer explanation.
67+
#if 0
68+
{
69+
static T read(const Store & store, ReadConn conn);
70+
static void write(const Store & store, WriteConn conn, const T & t);
71+
};
72+
#endif
73+
74+
/**
75+
* Wrapper function around `ServeProto::Serialise<T>::write` that allows us to
76+
* infer the type instead of having to write it down explicitly.
77+
*/
78+
template<typename T>
79+
static void write(const Store & store, WriteConn conn, const T & t)
80+
{
81+
ServeProto::Serialise<T>::write(store, conn, t);
82+
}
2583
};
2684

2785
enum struct ServeProto::Command : uint64_t
@@ -58,4 +116,33 @@ inline std::ostream & operator << (std::ostream & s, ServeProto::Command op)
58116
return s << (uint64_t) op;
59117
}
60118

119+
/**
120+
* Declare a canonical serialiser pair for the worker protocol.
121+
*
122+
* We specialise the struct merely to indicate that we are implementing
123+
* the function for the given type.
124+
*
125+
* Some sort of `template<...>` must be used with the caller for this to
126+
* be legal specialization syntax. See below for what that looks like in
127+
* practice.
128+
*/
129+
#define DECLARE_SERVE_SERIALISER(T) \
130+
struct ServeProto::Serialise< T > \
131+
{ \
132+
static T read(const Store & store, ServeProto::ReadConn conn); \
133+
static void write(const Store & store, ServeProto::WriteConn conn, const T & t); \
134+
};
135+
136+
template<typename T>
137+
DECLARE_SERVE_SERIALISER(std::vector<T>);
138+
template<typename T>
139+
DECLARE_SERVE_SERIALISER(std::set<T>);
140+
template<typename... Ts>
141+
DECLARE_SERVE_SERIALISER(std::tuple<Ts...>);
142+
143+
#define COMMA_ ,
144+
template<typename K, typename V>
145+
DECLARE_SERVE_SERIALISER(std::map<K COMMA_ V>);
146+
#undef COMMA_
147+
61148
}

0 commit comments

Comments
 (0)