Line data Source code
1 : use std::fs::{File, OpenOptions};
2 : use std::io;
3 : use std::io::prelude::*;
4 : use std::path::Path;
5 :
6 : use anyhow::Result;
7 : use compute_api::spec::{ComputeMode, ComputeSpec, GenericOption};
8 :
9 : use crate::pg_helpers::{GenericOptionExt, PgOptionsSerialize, escape_conf_value};
10 :
11 : /// Check that `line` is inside a text file and put it there if it is not.
12 : /// Create file if it doesn't exist.
13 3 : pub fn line_in_file(path: &Path, line: &str) -> Result<bool> {
14 3 : let mut file = OpenOptions::new()
15 3 : .read(true)
16 3 : .write(true)
17 3 : .create(true)
18 3 : .append(false)
19 3 : .truncate(false)
20 3 : .open(path)?;
21 3 : let buf = io::BufReader::new(&file);
22 3 : let mut count: usize = 0;
23 :
24 5 : for l in buf.lines() {
25 5 : if l? == line {
26 1 : return Ok(false);
27 4 : }
28 4 : count = 1;
29 : }
30 :
31 2 : write!(file, "{}{}", "\n".repeat(count), line)?;
32 2 : Ok(true)
33 3 : }
34 :
35 : /// Create or completely rewrite configuration file specified by `path`
36 0 : pub fn write_postgres_conf(
37 0 : path: &Path,
38 0 : spec: &ComputeSpec,
39 0 : extension_server_port: u16,
40 0 : ) -> Result<()> {
41 : // File::create() destroys the file content if it exists.
42 0 : let mut file = File::create(path)?;
43 :
44 : // Write the postgresql.conf content from the spec file as is.
45 0 : if let Some(conf) = &spec.cluster.postgresql_conf {
46 0 : writeln!(file, "{}", conf)?;
47 0 : }
48 :
49 : // Add options for connecting to storage
50 0 : writeln!(file, "# Neon storage settings")?;
51 0 : if let Some(s) = &spec.pageserver_connstring {
52 0 : writeln!(file, "neon.pageserver_connstring={}", escape_conf_value(s))?;
53 0 : }
54 0 : if let Some(stripe_size) = spec.shard_stripe_size {
55 0 : writeln!(file, "neon.stripe_size={stripe_size}")?;
56 0 : }
57 0 : if !spec.safekeeper_connstrings.is_empty() {
58 0 : writeln!(
59 0 : file,
60 0 : "neon.safekeepers={}",
61 0 : escape_conf_value(&spec.safekeeper_connstrings.join(","))
62 0 : )?;
63 0 : }
64 0 : if let Some(s) = &spec.tenant_id {
65 0 : writeln!(file, "neon.tenant_id={}", escape_conf_value(&s.to_string()))?;
66 0 : }
67 0 : if let Some(s) = &spec.timeline_id {
68 0 : writeln!(
69 0 : file,
70 0 : "neon.timeline_id={}",
71 0 : escape_conf_value(&s.to_string())
72 0 : )?;
73 0 : }
74 :
75 : // Locales
76 0 : if cfg!(target_os = "macos") {
77 0 : writeln!(file, "lc_messages='C'")?;
78 0 : writeln!(file, "lc_monetary='C'")?;
79 0 : writeln!(file, "lc_time='C'")?;
80 0 : writeln!(file, "lc_numeric='C'")?;
81 : } else {
82 0 : writeln!(file, "lc_messages='C.UTF-8'")?;
83 0 : writeln!(file, "lc_monetary='C.UTF-8'")?;
84 0 : writeln!(file, "lc_time='C.UTF-8'")?;
85 0 : writeln!(file, "lc_numeric='C.UTF-8'")?;
86 : }
87 :
88 0 : match spec.mode {
89 0 : ComputeMode::Primary => {}
90 0 : ComputeMode::Static(lsn) => {
91 0 : // hot_standby is 'on' by default, but let's be explicit
92 0 : writeln!(file, "hot_standby=on")?;
93 0 : writeln!(file, "recovery_target_lsn='{lsn}'")?;
94 : }
95 : ComputeMode::Replica => {
96 : // hot_standby is 'on' by default, but let's be explicit
97 0 : writeln!(file, "hot_standby=on")?;
98 : }
99 : }
100 :
101 0 : if cfg!(target_os = "linux") {
102 : // Check /proc/sys/vm/overcommit_memory -- if it equals 2 (i.e. linux memory overcommit is
103 : // disabled), then the control plane has enabled swap and we should set
104 : // dynamic_shared_memory_type = 'mmap'.
105 : //
106 : // This is (maybe?) temporary - for more, see https://github.com/neondatabase/cloud/issues/12047.
107 0 : let overcommit_memory_contents = std::fs::read_to_string("/proc/sys/vm/overcommit_memory")
108 0 : // ignore any errors - they may be expected to occur under certain situations (e.g. when
109 0 : // not running in Linux).
110 0 : .unwrap_or_else(|_| String::new());
111 0 : if overcommit_memory_contents.trim() == "2" {
112 0 : let opt = GenericOption {
113 0 : name: "dynamic_shared_memory_type".to_owned(),
114 0 : value: Some("mmap".to_owned()),
115 0 : vartype: "enum".to_owned(),
116 0 : };
117 0 :
118 0 : writeln!(file, "{}", opt.to_pg_setting())?;
119 0 : }
120 0 : }
121 :
122 : // If there are any extra options in the 'settings' field, append those
123 0 : if spec.cluster.settings.is_some() {
124 0 : writeln!(file, "# Managed by compute_ctl: begin")?;
125 0 : write!(file, "{}", spec.cluster.settings.as_pg_settings())?;
126 0 : writeln!(file, "# Managed by compute_ctl: end")?;
127 0 : }
128 :
129 0 : writeln!(file, "neon.extension_server_port={}", extension_server_port)?;
130 :
131 0 : if spec.drop_subscriptions_before_start {
132 0 : writeln!(file, "neon.disable_logical_replication_subscribers=true")?;
133 : } else {
134 : // be explicit about the default value
135 0 : writeln!(file, "neon.disable_logical_replication_subscribers=false")?;
136 : }
137 :
138 : // This is essential to keep this line at the end of the file,
139 : // because it is intended to override any settings above.
140 0 : writeln!(file, "include_if_exists = 'compute_ctl_temp_override.conf'")?;
141 :
142 0 : Ok(())
143 0 : }
144 :
145 0 : pub fn with_compute_ctl_tmp_override<F>(pgdata_path: &Path, options: &str, exec: F) -> Result<()>
146 0 : where
147 0 : F: FnOnce() -> Result<()>,
148 0 : {
149 0 : let path = pgdata_path.join("compute_ctl_temp_override.conf");
150 0 : let mut file = File::create(path)?;
151 0 : write!(file, "{}", options)?;
152 :
153 0 : let res = exec();
154 0 :
155 0 : file.set_len(0)?;
156 :
157 0 : res
158 0 : }
|