@@ -215,21 +215,25 @@ static void throwCollectionNotFound(char const* name) {
215
215
216
216
// / @brief Insert an error reported instead of the new document
217
217
static void createBabiesError (
218
- VPackBuilder& builder,
218
+ VPackBuilder* builder,
219
219
std::unordered_map<ErrorCode, size_t >& countErrorCodes,
220
220
Result const & error) {
221
- builder.openObject ();
222
- builder.add (StaticStrings::Error, VPackValue (true ));
223
- builder.add (StaticStrings::ErrorNum, VPackValue (error.errorNumber ()));
224
- builder.add (StaticStrings::ErrorMessage, VPackValue (error.errorMessage ()));
225
- builder.close ();
226
-
227
- auto it = countErrorCodes.find (error.errorNumber ());
228
- if (it == countErrorCodes.end ()) {
229
- countErrorCodes.emplace (error.errorNumber (), 1 );
230
- } else {
231
- it->second ++;
221
+ // on followers, builder will be a nullptr, so we can spare building
222
+ // the error result details in the response body, which the leader
223
+ // will ignore anyway.
224
+ if (builder != nullptr ) {
225
+ // only build error detail results if we got a builder passed here.
226
+ builder->openObject (false );
227
+ builder->add (StaticStrings::Error, VPackValue (true ));
228
+ builder->add (StaticStrings::ErrorNum, VPackValue (error.errorNumber ()));
229
+ builder->add (StaticStrings::ErrorMessage, VPackValue (error.errorMessage ()));
230
+ builder->close ();
232
231
}
232
+
233
+ // always (also on followers) increase error counter for the
234
+ // error code we got.
235
+ auto & value = countErrorCodes[error.errorNumber ()];
236
+ ++value;
233
237
}
234
238
235
239
static OperationResult emptyResult (OperationOptions const & options) {
@@ -907,7 +911,7 @@ Future<OperationResult> transaction::Methods::documentLocal(
907
911
for (VPackSlice s : VPackArrayIterator (value)) {
908
912
res = workForOneDocument (s, true );
909
913
if (res.fail ()) {
910
- createBabiesError (resultBuilder, countErrorCodes, res);
914
+ createBabiesError (& resultBuilder, countErrorCodes, res);
911
915
}
912
916
}
913
917
res.reset (); // With babies the reporting is handled somewhere else.
@@ -1045,7 +1049,11 @@ Future<OperationResult> transaction::Methods::insertLocal(
1045
1049
return OperationResult (TRI_ERROR_CLUSTER_SHARD_LEADER_RESIGNED,
1046
1050
options);
1047
1051
}
1052
+
1048
1053
bool sendRefusal = (options.isSynchronousReplicationFrom != theLeader);
1054
+ TRI_IF_FAILURE (" synchronousReplication::neverRefuseOnFollower" ) {
1055
+ sendRefusal = false ;
1056
+ }
1049
1057
TRI_IF_FAILURE (" synchronousReplication::refuseOnFollower" ) {
1050
1058
sendRefusal = true ;
1051
1059
}
@@ -1061,6 +1069,12 @@ Future<OperationResult> transaction::Methods::insertLocal(
1061
1069
::buildRefusalResult (*collection, " insert" , options, theLeader),
1062
1070
options);
1063
1071
}
1072
+
1073
+ // we are a valid follower. we do not need to send a proper result with
1074
+ // _key, _id, _rev back to the leader, because it will ignore all these
1075
+ // data anyway. it is sufficient to send headers and the proper error
1076
+ // codes back.
1077
+ options.silent = true ;
1064
1078
}
1065
1079
} // isDBServer - early block
1066
1080
@@ -1248,13 +1262,19 @@ Future<OperationResult> transaction::Methods::insertLocal(
1248
1262
VPackSlice s = it.value ();
1249
1263
TRI_IF_FAILURE (" insertLocal::fakeResult1" ) {
1250
1264
res.reset (TRI_ERROR_DEBUG);
1251
- createBabiesError (resultBuilder, errorCounter, res);
1265
+ createBabiesError (replicationType == ReplicationType::FOLLOWER
1266
+ ? nullptr
1267
+ : &resultBuilder,
1268
+ errorCounter, res);
1252
1269
it.next ();
1253
1270
continue ;
1254
1271
}
1255
1272
res = workForOneDocument (s, true , excludeFromReplication);
1256
1273
if (res.fail ()) {
1257
- createBabiesError (resultBuilder, errorCounter, res);
1274
+ createBabiesError (replicationType == ReplicationType::FOLLOWER
1275
+ ? nullptr
1276
+ : &resultBuilder,
1277
+ errorCounter, res);
1258
1278
} else if (excludeFromReplication) {
1259
1279
excludePositions.insert (it.index ());
1260
1280
}
@@ -1269,8 +1289,21 @@ Future<OperationResult> transaction::Methods::insertLocal(
1269
1289
if (res.ok () && excludeFromReplication) {
1270
1290
excludePositions.insert (0 );
1271
1291
}
1292
+
1293
+ // on a follower, our result should always be an empty object
1294
+ if (replicationType == ReplicationType::FOLLOWER) {
1295
+ TRI_ASSERT (resultBuilder.slice ().isNone ());
1296
+ // add an empty object here so that when sending things back in JSON
1297
+ // format, there is no "non-representable type 'none'" issue.
1298
+ resultBuilder.add (VPackSlice::emptyObjectSlice ());
1299
+ }
1272
1300
}
1273
1301
1302
+ // on a follower, our result should always be an empty array or object
1303
+ TRI_ASSERT (replicationType != ReplicationType::FOLLOWER ||
1304
+ (value.isArray () && resultBuilder.slice ().isEmptyArray ()) ||
1305
+ (value.isObject () && resultBuilder.slice ().isEmptyObject ()));
1306
+
1274
1307
TRI_ASSERT (res.ok () || !value.isArray ());
1275
1308
1276
1309
TRI_IF_FAILURE (" insertLocal::fakeResult2" ) { res.reset (TRI_ERROR_DEBUG); }
@@ -1438,6 +1471,9 @@ Future<OperationResult> transaction::Methods::modifyLocal(
1438
1471
options);
1439
1472
}
1440
1473
bool sendRefusal = (options.isSynchronousReplicationFrom != theLeader);
1474
+ TRI_IF_FAILURE (" synchronousReplication::neverRefuseOnFollower" ) {
1475
+ sendRefusal = false ;
1476
+ }
1441
1477
TRI_IF_FAILURE (" synchronousReplication::refuseOnFollower" ) {
1442
1478
sendRefusal = true ;
1443
1479
}
@@ -1457,6 +1493,12 @@ Future<OperationResult> transaction::Methods::modifyLocal(
1457
1493
options, theLeader),
1458
1494
options);
1459
1495
}
1496
+
1497
+ // we are a valid follower. we do not need to send a proper result with
1498
+ // _key, _id, _rev back to the leader, because it will ignore all these
1499
+ // data anyway. it is sufficient to send headers and the proper error
1500
+ // codes back.
1501
+ options.silent = true ;
1460
1502
}
1461
1503
} // isDBServer - early block
1462
1504
@@ -1548,7 +1590,10 @@ Future<OperationResult> transaction::Methods::modifyLocal(
1548
1590
bool excludeFromReplication = false ;
1549
1591
res = workForOneDocument (it.value (), true , excludeFromReplication);
1550
1592
if (res.fail ()) {
1551
- createBabiesError (resultBuilder, errorCounter, res);
1593
+ createBabiesError (replicationType == ReplicationType::FOLLOWER
1594
+ ? nullptr
1595
+ : &resultBuilder,
1596
+ errorCounter, res);
1552
1597
} else if (excludeFromReplication) {
1553
1598
excludePositions.insert (it.index ());
1554
1599
}
@@ -1562,8 +1607,21 @@ Future<OperationResult> transaction::Methods::modifyLocal(
1562
1607
if (res.ok () && excludeFromReplication) {
1563
1608
excludePositions.insert (0 );
1564
1609
}
1610
+
1611
+ // on a follower, our result should always be an empty object
1612
+ if (replicationType == ReplicationType::FOLLOWER) {
1613
+ TRI_ASSERT (resultBuilder.slice ().isNone ());
1614
+ // add an empty object here so that when sending things back in JSON
1615
+ // format, there is no "non-representable type 'none'" issue.
1616
+ resultBuilder.add (VPackSlice::emptyObjectSlice ());
1617
+ }
1565
1618
}
1566
1619
1620
+ // on a follower, our result should always be an empty array or object
1621
+ TRI_ASSERT (replicationType != ReplicationType::FOLLOWER ||
1622
+ (newValue.isArray () && resultBuilder.slice ().isEmptyArray ()) ||
1623
+ (newValue.isObject () && resultBuilder.slice ().isEmptyObject ()));
1624
+
1567
1625
TRI_ASSERT (!newValue.isArray () || options.silent ||
1568
1626
resultBuilder.slice ().length () == newValue.length ());
1569
1627
@@ -1707,6 +1765,9 @@ Future<OperationResult> transaction::Methods::removeLocal(
1707
1765
options);
1708
1766
}
1709
1767
bool sendRefusal = (options.isSynchronousReplicationFrom != theLeader);
1768
+ TRI_IF_FAILURE (" synchronousReplication::neverRefuseOnFollower" ) {
1769
+ sendRefusal = false ;
1770
+ }
1710
1771
TRI_IF_FAILURE (" synchronousReplication::refuseOnFollower" ) {
1711
1772
sendRefusal = true ;
1712
1773
}
@@ -1722,6 +1783,12 @@ Future<OperationResult> transaction::Methods::removeLocal(
1722
1783
::buildRefusalResult (*collection, " remove" , options, theLeader),
1723
1784
options);
1724
1785
}
1786
+
1787
+ // we are a valid follower. we do not need to send a proper result with
1788
+ // _key, _id, _rev back to the leader, because it will ignore all these
1789
+ // data anyway. it is sufficient to send headers and the proper error
1790
+ // codes back.
1791
+ options.silent = true ;
1725
1792
}
1726
1793
} // isDBServer - early block
1727
1794
@@ -1792,16 +1859,32 @@ Future<OperationResult> transaction::Methods::removeLocal(
1792
1859
for (VPackSlice s : VPackArrayIterator (value)) {
1793
1860
res = workForOneDocument (s, true );
1794
1861
if (res.fail ()) {
1795
- createBabiesError (resultBuilder, errorCounter, res);
1862
+ createBabiesError (replicationType == ReplicationType::FOLLOWER
1863
+ ? nullptr
1864
+ : &resultBuilder,
1865
+ errorCounter, res);
1796
1866
}
1797
1867
}
1798
1868
1799
1869
// it is ok to clobber res here!
1800
1870
res.reset ();
1801
1871
} else {
1802
1872
res = workForOneDocument (value, false );
1873
+
1874
+ // on a follower, our result should always be an empty object
1875
+ if (replicationType == ReplicationType::FOLLOWER) {
1876
+ TRI_ASSERT (resultBuilder.slice ().isNone ());
1877
+ // add an empty object here so that when sending things back in JSON
1878
+ // format, there is no "non-representable type 'none'" issue.
1879
+ resultBuilder.add (VPackSlice::emptyObjectSlice ());
1880
+ }
1803
1881
}
1804
1882
1883
+ // on a follower, our result should always be an empty array or object
1884
+ TRI_ASSERT (replicationType != ReplicationType::FOLLOWER ||
1885
+ (value.isArray () && resultBuilder.slice ().isEmptyArray ()) ||
1886
+ (value.isObject () && resultBuilder.slice ().isEmptyObject ()));
1887
+
1805
1888
TRI_ASSERT (!value.isArray () || options.silent ||
1806
1889
resultBuilder.slice ().length () == value.length ());
1807
1890
@@ -1969,6 +2052,9 @@ Future<OperationResult> transaction::Methods::truncateLocal(
1969
2052
OperationResult (TRI_ERROR_CLUSTER_SHARD_LEADER_RESIGNED, options));
1970
2053
}
1971
2054
bool sendRefusal = (options.isSynchronousReplicationFrom != theLeader);
2055
+ TRI_IF_FAILURE (" synchronousReplication::neverRefuseOnFollower" ) {
2056
+ sendRefusal = false ;
2057
+ }
1972
2058
TRI_IF_FAILURE (" synchronousReplication::refuseOnFollower" ) {
1973
2059
sendRefusal = true ;
1974
2060
}
0 commit comments