Home.md
... ...
@@ -59,6 +59,7 @@ SAP is at the center of today’s technology revolution, developing innovations
59 59
* Amazon
60 60
* [[Amazon EC2|wiki/info/landscape/amazon-ec2]]
61 61
* [[Upgrading ARCHIVE server|wiki/info/landscape/archive-server-upgrade]]
62
+ * [[Upgrading MongoDB Nodes|wiki/info/landscape/mongo-cluster-upgrade]]
62 63
* [[EC2 Backup Strategy|wiki/info/landscape/amazon-ec2-backup-strategy]]
63 64
* [[Creating an EC2 image from scratch|wiki/info/landscape/creating-ec2-image-from-scratch]]
64 65
* [[Upgrading an EC2 image|wiki/info/landscape/upgrading-ec2-image]]
java/com.sap.sailing.grib/src/com/sap/sailing/grib/impl/GribWindFieldFactoryImpl.java
... ...
@@ -19,6 +19,7 @@ import java.util.logging.Logger;
19 19
20 20
import com.sap.sailing.grib.GribWindField;
21 21
import com.sap.sailing.grib.GribWindFieldFactory;
22
+import com.sap.sse.common.Util;
22 23
import com.sap.sse.common.util.MappingIterable;
23 24
import com.sap.sse.util.LoggerAppender;
24 25
... ...
@@ -188,6 +189,9 @@ public class GribWindFieldFactoryImpl implements GribWindFieldFactory {
188 189
* are no longer needed.
189 190
*/
190 191
private File copyStreamToFile(InputStream s, String filename) throws IOException {
192
+ if (Util.hasLength(filename) && (filename.contains("..") || filename.contains("/") || filename.contains("\\"))) {
193
+ throw new IllegalArgumentException("File extension must not contain '..' or a file separator like '/'.");
194
+ }
191 195
Path tempDir = Files.createTempDirectory("gribcache");
192 196
Path filePath = tempDir.resolve(filename);
193 197
Files.copy(s, filePath);
java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/client/LandscapeManagementPanel.java
... ...
@@ -1618,7 +1618,7 @@ public class LandscapeManagementPanel extends SimplePanel {
1618 1618
new AsyncCallback<Void>() {
1619 1619
@Override
1620 1620
public void onSuccess(Void result) {
1621
- Notification.notify(stringMessages.unlockedSuccessfully(), NotificationType.SUCCESS);
1621
+ Notification.notify(stringMessages.success(), NotificationType.SUCCESS);
1622 1622
proxiesTableBusy.setBusy(false);
1623 1623
refreshProxiesTable();
1624 1624
}
java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/client/i18n/StringMessages.java
... ...
@@ -173,7 +173,7 @@ com.sap.sse.gwt.adminconsole.StringMessages {
173 173
String successfullyRotatedHttpdLogsOnInstance(String instance);
174 174
String invalidOperationForThisProxy();
175 175
String pleaseProvideNonEmptyNameAndAZ();
176
- String unlockedSuccessfully();
176
+ String success();
177 177
String availabilityZone();
178 178
String runOnExisting();
179 179
String publicIp();
java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/client/i18n/StringMessages.properties
... ...
@@ -162,7 +162,7 @@ rotateHttpdLogs=Rotate httpd logs
162 162
successfullyRotatedHttpdLogsOnInstance=Successfully rotated the Apache Httpd logs on instance: {0}
163 163
invalidOperationForThisProxy=You can''t perform this operation on this instance
164 164
pleaseProvideNonEmptyNameAndAZ=Please provide a non-empty name (UTF-8) and an AZ.
165
-success=success
165
+success=Success
166 166
availabilityZone=Availability Zone
167 167
runOnExisting=Run on an existing, running instance
168 168
publicIp=Public IP address
java/com.sap.sse.filestorage/src/com/sap/sse/filestorage/impl/LocalFileStorageServiceImpl.java
... ...
@@ -17,6 +17,7 @@ import org.apache.shiro.authz.UnauthorizedException;
17 17
import org.osgi.framework.BundleContext;
18 18
19 19
import com.sap.sailing.domain.common.security.SecuredDomainType;
20
+import com.sap.sse.common.Util;
20 21
import com.sap.sse.common.Util.Pair;
21 22
import com.sap.sse.filestorage.FileStorageService;
22 23
import com.sap.sse.filestorage.FileStorageServiceProperty;
... ...
@@ -41,8 +42,6 @@ import com.sap.sse.security.shared.TypeRelativeObjectIdentifier;
41 42
* @author Jan Broß
42 43
*
43 44
*/
44
-
45
-
46 45
public class LocalFileStorageServiceImpl extends BaseFileStorageServiceImpl implements FileStorageService {
47 46
private static final long serialVersionUID = -8661781258137340835L;
48 47
private static final String testFile = "Bundesliga2014_Regatta6_eventteaser.jpg";
... ...
@@ -62,6 +61,9 @@ public class LocalFileStorageServiceImpl extends BaseFileStorageServiceImpl impl
62 61
@Override
63 62
public URI storeFile(InputStream is, String fileExtension, long lengthInBytes)
64 63
throws IOException, UnauthorizedException {
64
+ if (Util.hasLength(fileExtension) && (fileExtension.contains("..") || fileExtension.contains("/") || fileExtension.contains("\\"))) {
65
+ throw new IllegalArgumentException("File extension must not contain '..' or a file separator like '/'.");
66
+ }
65 67
String fileName = getKey(fileExtension);
66 68
String pathToFile = localPath.getValue() + "/" + fileName;
67 69
return getSecurityService().setOwnershipCheckPermissionForObjectCreationAndRevertOnError(SecuredDomainType.FILE_STORAGE,
java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/impl/AwsLandscapeImpl.java
... ...
@@ -1029,7 +1029,6 @@ public class AwsLandscapeImpl<ShardingKey> implements AwsLandscape<ShardingKey>
1029 1029
}
1030 1030
final Ec2Client ec2Client = getEc2Client(getRegion(az.getRegion()));
1031 1031
final Builder runInstancesRequestBuilder = RunInstancesRequest.builder()
1032
- .additionalInfo("Test " + getClass().getName())
1033 1032
.imageId(fromImage.getId().toString())
1034 1033
.minCount(numberOfHostsToLaunch)
1035 1034
.maxCount(numberOfHostsToLaunch)
java/com.sap.sse.test/src/com/sap/sse/test/RegexTest.java
... ...
@@ -5,6 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
5 5
import static org.junit.jupiter.api.Assertions.assertNull;
6 6
import static org.junit.jupiter.api.Assertions.assertTrue;
7 7
8
+import java.util.Arrays;
8 9
import java.util.logging.Logger;
9 10
import java.util.regex.Matcher;
10 11
import java.util.regex.Pattern;
... ...
@@ -94,4 +95,11 @@ public class RegexTest {
94 95
final Matcher m1 = nationalityPattern.matcher(sb.toString());
95 96
assertFalse(m1.matches());
96 97
}
98
+
99
+ @Test
100
+ public void testExpeditionHeaderSplitting() {
101
+ final String line = "a , b ,, c";
102
+ final String[] splitResult = line.split("\\s*,\\s*");
103
+ assertEquals(Arrays.asList("a", "b", "", "c"), Arrays.asList(splitResult));
104
+ }
97 105
}
wiki/info/landscape/mongo-cluster-upgrade.md
... ...
@@ -0,0 +1,91 @@
1
+# MongoDB Cluster Upgrades
2
+
3
+In our production environment on AWS, we currently (2026-02-10) run three MongoDB replica sets:
4
+
5
+- ``live``: holds all databases for live operations and consists of three nodes: two i3.large instances with fast NVMe storage used for the ``/var/lib/mongo`` partition, and a hidden instance with an EBS volume that is backed up on a daily basis
6
+- ``archive``: holds the ``winddb`` database used for the ARCHIVE server
7
+- ``slow``: used for backing up databases when removing them from the ``live`` replica set, e.g., when shutting down an application replica set after an event
8
+
9
+The ``archive`` and ``slow`` replica sets usually have only a single instance running on ``dbserver.internal.sapsailing.com``, and this is also where the hidden replica of the ``live`` replica set runs. The other two ``live`` nodes have internal DNS names set for them: ``mongo[01].internal.sapsailing.com``.
10
+
11
+Upgrades may affect the packages installed on the nodes, or may affect the major version of MongoDB being run. Both upgrade procedures are described in the following two sections.
12
+
13
+## Upgrade Using Package Manager
14
+
15
+With Amazon Linux 2023, ``dnf`` is the package manager used. When logging on to an instance, a message like
16
+
17
+```
18
+A newer release of "Amazon Linux" is available.
19
+ Version 2023.10.20260202:
20
+Run "/usr/bin/dnf check-release-update" for full release and version update info
21
+```
22
+
23
+may be shown. In this case, run
24
+
25
+```
26
+dnf --releasever=latest upgrade
27
+```
28
+
29
+and watch closely what the package manager suggests. As soon as you see a kernel update about to install, displayed in red color (if your terminal supports colored output), a reboot will be required after completing the installation. This can also be checked using the following command:
30
+
31
+```
32
+needs-restarting -r
33
+```
34
+
35
+It will output a message like
36
+
37
+```
38
+No core libraries or services have been updated since boot-up.
39
+Reboot should not be necessary.
40
+```
41
+
42
+and exits with code ``0`` if no reboot is required; otherwise, it will exit with ``1`` and display a corresponding message.
43
+
44
+To avoid interrupting user-facing services, rebooting the MongoDB nodes shall follow a certain procedure:
45
+
46
+- Ensure that no ARCHIVE candidate is currently launching; such a candidate would read from the ``archive`` replica set, so that rebooting the ``dbserver.internal.sapsailing.com`` node would interrupt this loading process. If an ARCHIVE candidate is launching, wait for the launch to finish.
47
+- Ensure that no application replica set is currently being shut down with backing up its database. This backup would fail if the ``dbserver.internal.sapsailing.com`` node were restarted as it hosts the ``slow`` replica set used for the backup.
48
+- ssh into ``ec2-user@dbserver.internal.sapsailing.com``
49
+- There, run ``sudo dnf --releasever=latest upgrade`` and confirm with "yes"
50
+- Assuming an update was installed that now requires a reboot, run ``sudo reboot``
51
+- Wait until the instance is back up and running, you can ssh into it again, and ``pgrep mongod`` shows the three process IDs of the three running ``mongod`` processes
52
+- ssh into ``ec2-user@mongo0.internal.sapsailing.com``
53
+- run ``mongosh`` to see if ``mongo0`` is currently primary or secondary in the ``live`` replica set
54
+- if you see "secondary", you're all set; if you see "primary", enter ``rs.stepDown()`` and see how the prompt changes from "primary" to "secondary"
55
+- use ``quit()`` to exit the ``mongosh`` shell
56
+- run ``sudo dnf --releasever=latest upgrade`` and confirm with "yes"
57
+- if a reboot is required, run ``sudo reboot``
58
+- wait for the instance and its ``mongod`` process to become available again; you may probe, e.g., by ssh-ing into the instance and checking with ``mongosh``
59
+- repeat the process described for ``mongo0`` for ``mongo1.internal.sapsailing.com``
60
+
61
+Hint: You can choose the order between ``mongo0`` and ``mongo1`` as you wish. If you start with the "secondary" instance, you will save one ``rs.stepDown()`` command.
62
+
63
+## MongoDB Major Version Upgrade
64
+
65
+Upgrading a MongoDB replica set that has more than one node can work without client noticing any interruption of service. This is in particular important for our ``live`` replica set used by all running application replica sets other than ``ARCHIVE``. For the single-node replica sets ``archive`` and ``slow`` it again comes down to timing an upgrade such that no ``ARCHIVE`` candidate launch is ongoing, and that no application replica set is currently being shut down with its database getting backed up to the ``slow`` replica set.
66
+
67
+The [MongoDB online documentation](https://www.mongodb.com/docs/manual/release-notes/8.0-upgrade-replica-set/#std-label-8.0-upgrade-replica-set) contains a useful description of the steps necessary. The key to understanding those steps is that MongoDB replica sets can distinguish between the actual version of ``mongod`` that is running, and the "protocol version" the nodes use to talk to each other in a replica set. Newer ``mongod`` versions can always still work with the "protocol version" of the previous major release. For example, ``mongod`` in version 8 can still work with protocol version "7.0".
68
+
69
+Therefore, upgrading a replica set with multiple nodes will work along these steps:
70
+
71
+- Ensure all ``mongod`` processes in the replica set run the same (old) version
72
+- Ensure all ``mongod`` processes use the protocol version that matches their own ``mongod`` version
73
+- Upgrade the binaries and restart the ``mongod`` processes with the new version for all nodes, properly having the primary step down before restarting its process
74
+- Set the new protocol version for the replica set
75
+
76
+The sequence in which to work with the different nodes and processes resembles that for reboots after upgrades with the package manager. Here are the steps in detail:
77
+
78
+- Ensure that no ARCHIVE candidate is currently launching; such a candidate would read from the ``archive`` replica set, so that rebooting the ``dbserver.internal.sapsailing.com`` node would interrupt this loading process. If an ARCHIVE candidate is launching, wait for the launch to finish.
79
+- Ensure that no application replica set is currently being shut down with backing up its database. This backup would fail if the ``dbserver.internal.sapsailing.com`` node were restarted as it hosts the ``slow`` replica set used for the backup.
80
+- ssh into ``ec2-user@dbserver.internal.sapsailing.com``
81
+- Ensure all ``mongod`` processes on the host run the same (old) version, using ``mongosh`` for all three replica sets (``live``, ``archive``, ``slow``)
82
+- In ``mongosh``, display the protocol version using ``db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )``. Should you find a deviation, set the protocol version using ``db.adminCommand( { setFeatureCompatibilityVersion: "7.0" , confirm: true } )`` (of course with the "7.0" replaced by whichever protocol version you have to set this to).
83
+- In ``/etc/yum.repos.d/`` find the ``mongodb-org.{major.minor}.repo`` file that controls where the MongoDB packages are currently obtained from. Rename the current ``mongodb-org.{major.minor}.repo`` by appending, e.g., ``.bak`` to its name and create a new ``.repo`` file for the MongoDB version you'd like to upgrade to. Then run ``dnf --releasever=latest upgrade``. This should automatically restart the ``mongod`` processes now upgraded to the new release.
84
+- ssh into ``ec2-user@mongo0.internal.sapsailing.com``
85
+- check ``mongod`` and protocol version using ``mongosh``; adjust protocol version if necessary (see above)
86
+- if on the "primary", use ``rs.stepDown()`` to make it a "secondary"
87
+- run the binaries upgrade as explained for ``dbserver.internal.sapsailing.com`` above, adjusting the ``.repo`` file under ``/etc/yum.repos.d``, followed by ``dnf --releasever=latest upgrade``
88
+- repeat the last four steps for ``mongo1.internal.sapsailing.com``
89
+- use ``mongosh`` to connect to the primaries of all three replica sets (``live``, ``archive``, ``slow``) and on each one issue the command ``db.adminCommand( { setFeatureCompatibilityVersion: "8.0", confirm: true }`` with the "8.0" replaced by the protocol version you want to upgrade to, so usually the major/minor version of the binaries to which you have upgraded.
90
+
91
+Done :-)
... ...
\ No newline at end of file