├── .gitignore ├── README.markdown ├── postgres ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── doc │ └── intro.md ├── project.clj ├── resources │ ├── pg_hba.conf │ └── postgresql.conf └── src │ └── jepsen │ └── postgres │ ├── cli.clj │ ├── client.clj │ ├── db.clj │ └── workload │ ├── append.clj │ └── ledger.clj └── rds ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── doc └── intro.md ├── project.clj └── src └── jepsen └── postgres └── rds.clj /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | profiles.clj 5 | pom.xml 6 | pom.xml.asc 7 | *.jar 8 | *.class 9 | /.lein-* 10 | /.nrepl-port 11 | /.prepl-port 12 | /store 13 | *.swp 14 | .hgignore 15 | .hg/ 16 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # Jepsen Postgres tests 2 | 3 | This repo includes tests for several related systems that expose a Postgres 4 | API. The core tests for single-node Postgres are in `postgres/`. Tests for AWS 5 | RDS Postgres clusters are in `rds/`. 6 | 7 | We package these separately because some Postgres-based tests pull in 8 | conflicting dependencies. Stolon, for instance, uses jetcd, which uses a bunch 9 | of Jetty deps. RDS needs the AWS API, which also uses Jetty, but incompatible 10 | versions. 11 | 12 | This also makes it possible to pull in the Postgres tests as a library when 13 | testing other Postgres-based systems, like Cockroach. 14 | -------------------------------------------------------------------------------- /postgres/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | /store 5 | profiles.clj 6 | pom.xml 7 | pom.xml.asc 8 | *.jar 9 | *.class 10 | /.lein-* 11 | /.nrepl-port 12 | .hgignore 13 | .hg/ 14 | -------------------------------------------------------------------------------- /postgres/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/). 3 | 4 | ## [Unreleased] 5 | ### Changed 6 | - Add a new arity to `make-widget-async` to provide a different widget shape. 7 | 8 | ## [0.1.1] - 2020-05-29 9 | ### Changed 10 | - Documentation on how to make the widgets. 11 | 12 | ### Removed 13 | - `make-widget-sync` - we're all async, all the time. 14 | 15 | ### Fixed 16 | - Fixed widget maker to keep working when daylight savings switches over. 17 | 18 | ## 0.1.0 - 2020-05-29 19 | ### Added 20 | - Files from the new template. 21 | - Widget maker public API - `make-widget-sync`. 22 | 23 | [Unreleased]: https://github.com/your-name/jepsen.stolon/compare/0.1.1...HEAD 24 | [0.1.1]: https://github.com/your-name/jepsen.stolon/compare/0.1.0...0.1.1 25 | -------------------------------------------------------------------------------- /postgres/LICENSE: -------------------------------------------------------------------------------- 1 | Eclipse Public License - v 2.0 2 | 3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE 4 | PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION 5 | OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 6 | 7 | 1. DEFINITIONS 8 | 9 | "Contribution" means: 10 | 11 | a) in the case of the initial Contributor, the initial content 12 | Distributed under this Agreement, and 13 | 14 | b) in the case of each subsequent Contributor: 15 | i) changes to the Program, and 16 | ii) additions to the Program; 17 | where such changes and/or additions to the Program originate from 18 | and are Distributed by that particular Contributor. A Contribution 19 | "originates" from a Contributor if it was added to the Program by 20 | such Contributor itself or anyone acting on such Contributor's behalf. 21 | Contributions do not include changes or additions to the Program that 22 | are not Modified Works. 23 | 24 | "Contributor" means any person or entity that Distributes the Program. 25 | 26 | "Licensed Patents" mean patent claims licensable by a Contributor which 27 | are necessarily infringed by the use or sale of its Contribution alone 28 | or when combined with the Program. 29 | 30 | "Program" means the Contributions Distributed in accordance with this 31 | Agreement. 32 | 33 | "Recipient" means anyone who receives the Program under this Agreement 34 | or any Secondary License (as applicable), including Contributors. 35 | 36 | "Derivative Works" shall mean any work, whether in Source Code or other 37 | form, that is based on (or derived from) the Program and for which the 38 | editorial revisions, annotations, elaborations, or other modifications 39 | represent, as a whole, an original work of authorship. 40 | 41 | "Modified Works" shall mean any work in Source Code or other form that 42 | results from an addition to, deletion from, or modification of the 43 | contents of the Program, including, for purposes of clarity any new file 44 | in Source Code form that contains any contents of the Program. Modified 45 | Works shall not include works that contain only declarations, 46 | interfaces, types, classes, structures, or files of the Program solely 47 | in each case in order to link to, bind by name, or subclass the Program 48 | or Modified Works thereof. 49 | 50 | "Distribute" means the acts of a) distributing or b) making available 51 | in any manner that enables the transfer of a copy. 52 | 53 | "Source Code" means the form of a Program preferred for making 54 | modifications, including but not limited to software source code, 55 | documentation source, and configuration files. 56 | 57 | "Secondary License" means either the GNU General Public License, 58 | Version 2.0, or any later versions of that license, including any 59 | exceptions or additional permissions as identified by the initial 60 | Contributor. 61 | 62 | 2. GRANT OF RIGHTS 63 | 64 | a) Subject to the terms of this Agreement, each Contributor hereby 65 | grants Recipient a non-exclusive, worldwide, royalty-free copyright 66 | license to reproduce, prepare Derivative Works of, publicly display, 67 | publicly perform, Distribute and sublicense the Contribution of such 68 | Contributor, if any, and such Derivative Works. 69 | 70 | b) Subject to the terms of this Agreement, each Contributor hereby 71 | grants Recipient a non-exclusive, worldwide, royalty-free patent 72 | license under Licensed Patents to make, use, sell, offer to sell, 73 | import and otherwise transfer the Contribution of such Contributor, 74 | if any, in Source Code or other form. This patent license shall 75 | apply to the combination of the Contribution and the Program if, at 76 | the time the Contribution is added by the Contributor, such addition 77 | of the Contribution causes such combination to be covered by the 78 | Licensed Patents. The patent license shall not apply to any other 79 | combinations which include the Contribution. No hardware per se is 80 | licensed hereunder. 81 | 82 | c) Recipient understands that although each Contributor grants the 83 | licenses to its Contributions set forth herein, no assurances are 84 | provided by any Contributor that the Program does not infringe the 85 | patent or other intellectual property rights of any other entity. 86 | Each Contributor disclaims any liability to Recipient for claims 87 | brought by any other entity based on infringement of intellectual 88 | property rights or otherwise. As a condition to exercising the 89 | rights and licenses granted hereunder, each Recipient hereby 90 | assumes sole responsibility to secure any other intellectual 91 | property rights needed, if any. For example, if a third party 92 | patent license is required to allow Recipient to Distribute the 93 | Program, it is Recipient's responsibility to acquire that license 94 | before distributing the Program. 95 | 96 | d) Each Contributor represents that to its knowledge it has 97 | sufficient copyright rights in its Contribution, if any, to grant 98 | the copyright license set forth in this Agreement. 99 | 100 | e) Notwithstanding the terms of any Secondary License, no 101 | Contributor makes additional grants to any Recipient (other than 102 | those set forth in this Agreement) as a result of such Recipient's 103 | receipt of the Program under the terms of a Secondary License 104 | (if permitted under the terms of Section 3). 105 | 106 | 3. REQUIREMENTS 107 | 108 | 3.1 If a Contributor Distributes the Program in any form, then: 109 | 110 | a) the Program must also be made available as Source Code, in 111 | accordance with section 3.2, and the Contributor must accompany 112 | the Program with a statement that the Source Code for the Program 113 | is available under this Agreement, and informs Recipients how to 114 | obtain it in a reasonable manner on or through a medium customarily 115 | used for software exchange; and 116 | 117 | b) the Contributor may Distribute the Program under a license 118 | different than this Agreement, provided that such license: 119 | i) effectively disclaims on behalf of all other Contributors all 120 | warranties and conditions, express and implied, including 121 | warranties or conditions of title and non-infringement, and 122 | implied warranties or conditions of merchantability and fitness 123 | for a particular purpose; 124 | 125 | ii) effectively excludes on behalf of all other Contributors all 126 | liability for damages, including direct, indirect, special, 127 | incidental and consequential damages, such as lost profits; 128 | 129 | iii) does not attempt to limit or alter the recipients' rights 130 | in the Source Code under section 3.2; and 131 | 132 | iv) requires any subsequent distribution of the Program by any 133 | party to be under a license that satisfies the requirements 134 | of this section 3. 135 | 136 | 3.2 When the Program is Distributed as Source Code: 137 | 138 | a) it must be made available under this Agreement, or if the 139 | Program (i) is combined with other material in a separate file or 140 | files made available under a Secondary License, and (ii) the initial 141 | Contributor attached to the Source Code the notice described in 142 | Exhibit A of this Agreement, then the Program may be made available 143 | under the terms of such Secondary Licenses, and 144 | 145 | b) a copy of this Agreement must be included with each copy of 146 | the Program. 147 | 148 | 3.3 Contributors may not remove or alter any copyright, patent, 149 | trademark, attribution notices, disclaimers of warranty, or limitations 150 | of liability ("notices") contained within the Program from any copy of 151 | the Program which they Distribute, provided that Contributors may add 152 | their own appropriate notices. 153 | 154 | 4. COMMERCIAL DISTRIBUTION 155 | 156 | Commercial distributors of software may accept certain responsibilities 157 | with respect to end users, business partners and the like. While this 158 | license is intended to facilitate the commercial use of the Program, 159 | the Contributor who includes the Program in a commercial product 160 | offering should do so in a manner which does not create potential 161 | liability for other Contributors. Therefore, if a Contributor includes 162 | the Program in a commercial product offering, such Contributor 163 | ("Commercial Contributor") hereby agrees to defend and indemnify every 164 | other Contributor ("Indemnified Contributor") against any losses, 165 | damages and costs (collectively "Losses") arising from claims, lawsuits 166 | and other legal actions brought by a third party against the Indemnified 167 | Contributor to the extent caused by the acts or omissions of such 168 | Commercial Contributor in connection with its distribution of the Program 169 | in a commercial product offering. The obligations in this section do not 170 | apply to any claims or Losses relating to any actual or alleged 171 | intellectual property infringement. In order to qualify, an Indemnified 172 | Contributor must: a) promptly notify the Commercial Contributor in 173 | writing of such claim, and b) allow the Commercial Contributor to control, 174 | and cooperate with the Commercial Contributor in, the defense and any 175 | related settlement negotiations. The Indemnified Contributor may 176 | participate in any such claim at its own expense. 177 | 178 | For example, a Contributor might include the Program in a commercial 179 | product offering, Product X. That Contributor is then a Commercial 180 | Contributor. If that Commercial Contributor then makes performance 181 | claims, or offers warranties related to Product X, those performance 182 | claims and warranties are such Commercial Contributor's responsibility 183 | alone. Under this section, the Commercial Contributor would have to 184 | defend claims against the other Contributors related to those performance 185 | claims and warranties, and if a court requires any other Contributor to 186 | pay any damages as a result, the Commercial Contributor must pay 187 | those damages. 188 | 189 | 5. NO WARRANTY 190 | 191 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT 192 | PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS" 193 | BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR 194 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF 195 | TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR 196 | PURPOSE. Each Recipient is solely responsible for determining the 197 | appropriateness of using and distributing the Program and assumes all 198 | risks associated with its exercise of rights under this Agreement, 199 | including but not limited to the risks and costs of program errors, 200 | compliance with applicable laws, damage to or loss of data, programs 201 | or equipment, and unavailability or interruption of operations. 202 | 203 | 6. DISCLAIMER OF LIABILITY 204 | 205 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT 206 | PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS 207 | SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 208 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST 209 | PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 210 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 211 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 212 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE 213 | POSSIBILITY OF SUCH DAMAGES. 214 | 215 | 7. GENERAL 216 | 217 | If any provision of this Agreement is invalid or unenforceable under 218 | applicable law, it shall not affect the validity or enforceability of 219 | the remainder of the terms of this Agreement, and without further 220 | action by the parties hereto, such provision shall be reformed to the 221 | minimum extent necessary to make such provision valid and enforceable. 222 | 223 | If Recipient institutes patent litigation against any entity 224 | (including a cross-claim or counterclaim in a lawsuit) alleging that the 225 | Program itself (excluding combinations of the Program with other software 226 | or hardware) infringes such Recipient's patent(s), then such Recipient's 227 | rights granted under Section 2(b) shall terminate as of the date such 228 | litigation is filed. 229 | 230 | All Recipient's rights under this Agreement shall terminate if it 231 | fails to comply with any of the material terms or conditions of this 232 | Agreement and does not cure such failure in a reasonable period of 233 | time after becoming aware of such noncompliance. If all Recipient's 234 | rights under this Agreement terminate, Recipient agrees to cease use 235 | and distribution of the Program as soon as reasonably practicable. 236 | However, Recipient's obligations under this Agreement and any licenses 237 | granted by Recipient relating to the Program shall continue and survive. 238 | 239 | Everyone is permitted to copy and distribute copies of this Agreement, 240 | but in order to avoid inconsistency the Agreement is copyrighted and 241 | may only be modified in the following manner. The Agreement Steward 242 | reserves the right to publish new versions (including revisions) of 243 | this Agreement from time to time. No one other than the Agreement 244 | Steward has the right to modify this Agreement. The Eclipse Foundation 245 | is the initial Agreement Steward. The Eclipse Foundation may assign the 246 | responsibility to serve as the Agreement Steward to a suitable separate 247 | entity. Each new version of the Agreement will be given a distinguishing 248 | version number. The Program (including Contributions) may always be 249 | Distributed subject to the version of the Agreement under which it was 250 | received. In addition, after a new version of the Agreement is published, 251 | Contributor may elect to Distribute the Program (including its 252 | Contributions) under the new version. 253 | 254 | Except as expressly stated in Sections 2(a) and 2(b) above, Recipient 255 | receives no rights or licenses to the intellectual property of any 256 | Contributor under this Agreement, whether expressly, by implication, 257 | estoppel or otherwise. All rights in the Program not expressly granted 258 | under this Agreement are reserved. Nothing in this Agreement is intended 259 | to be enforceable by any entity that is not a Contributor or Recipient. 260 | No third-party beneficiary rights are created under this Agreement. 261 | 262 | Exhibit A - Form of Secondary Licenses Notice 263 | 264 | "This Source Code may also be made available under the following 265 | Secondary Licenses when the conditions for such availability set forth 266 | in the Eclipse Public License, v. 2.0 are satisfied: {name license(s), 267 | version(s), and exceptions or additional permissions here}." 268 | 269 | Simply including a copy of this Agreement, including this Exhibit A 270 | is not sufficient to license the Source Code under Secondary Licenses. 271 | 272 | If it is not possible or desirable to put the notice in a particular 273 | file, then You may include the notice in a location (such as a LICENSE 274 | file in a relevant directory) where a recipient would be likely to 275 | look for such a notice. 276 | 277 | You may add additional accurate notices of copyright ownership. 278 | -------------------------------------------------------------------------------- /postgres/README.md: -------------------------------------------------------------------------------- 1 | # jepsen.postgres 2 | 3 | Jepsen tests for the Postgres database system. 4 | 5 | ## Usage 6 | 7 | To check Postgres `SERIALIZABLE`, try 8 | 9 | ``` 10 | lein run test-all --node n1 -w append --concurrency 50 --isolation serializable --nemesis none --time-limit 120 -r 200 --test-count 2 --max-writes-per-key 1 11 | ``` 12 | 13 | If you have a postgres process on localhost, with a postgres user (and 14 | database) named `jepsen`, and password `pw`, try: 15 | 16 | ``` 17 | lein run test-all -w append --max-writes-per-key 4 --concurrency 50 -r 500 --isolation serializable --time-limit 60 --nemesis none --existing-postgres --node localhost --no-ssh --postgres-user jepsen --postgres-password pw 18 | ``` 19 | 20 | ## License 21 | 22 | Copyright © 2020, 2024 Jepsen, LLC 23 | 24 | This program and the accompanying materials are made available under the 25 | terms of the Eclipse Public License 2.0 which is available at 26 | http://www.eclipse.org/legal/epl-2.0. 27 | 28 | This Source Code may also be made available under the following Secondary 29 | Licenses when the conditions for such availability set forth in the Eclipse 30 | Public License, v. 2.0 are satisfied: GNU General Public License as published by 31 | the Free Software Foundation, either version 2 of the License, or (at your 32 | option) any later version, with the GNU Classpath Exception which is available 33 | at https://www.gnu.org/software/classpath/license.html. 34 | -------------------------------------------------------------------------------- /postgres/doc/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction to jepsen.stolon 2 | 3 | TODO: write [great documentation](http://jacobian.org/writing/what-to-write/) 4 | -------------------------------------------------------------------------------- /postgres/project.clj: -------------------------------------------------------------------------------- 1 | (defproject io.jepsen/postgres "0.1.2-SNAPSHOT" 2 | :description "Jepsen tests for PostgreSQL." 3 | :url "https://github.com/jepsen-io/postgres" 4 | :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0" 5 | :url "https://www.eclipse.org/legal/epl-2.0/"} 6 | :dependencies [[org.clojure/clojure "1.12.0"] 7 | [jepsen "0.3.8"] 8 | [seancorfield/next.jdbc "1.2.659"] 9 | [org.postgresql/postgresql "42.7.5"] 10 | [cheshire "5.13.0"] 11 | [clj-wallhack "1.0.1"]] 12 | :main jepsen.postgres.cli 13 | :jvm-opts ["-Djava.awt.headless=true" 14 | "-server"] 15 | :repl-options {:init-ns jepsen.postgres}) 16 | -------------------------------------------------------------------------------- /postgres/resources/pg_hba.conf: -------------------------------------------------------------------------------- 1 | local all postgres peer 2 | local all all peer 3 | host all all 0.0.0.0/0 trust 4 | -------------------------------------------------------------------------------- /postgres/resources/postgresql.conf: -------------------------------------------------------------------------------- 1 | # ----------------------------- 2 | # PostgreSQL configuration file 3 | # ----------------------------- 4 | # 5 | # This file consists of lines of the form: 6 | # 7 | # name = value 8 | # 9 | # (The "=" is optional.) Whitespace may be used. Comments are introduced with 10 | # "#" anywhere on a line. The complete list of parameter names and allowed 11 | # values can be found in the PostgreSQL documentation. 12 | # 13 | # The commented-out settings shown in this file represent the default values. 14 | # Re-commenting a setting is NOT sufficient to revert it to the default value; 15 | # you need to reload the server. 16 | # 17 | # This file is read on server startup and when the server receives a SIGHUP 18 | # signal. If you edit the file on a running system, you have to SIGHUP the 19 | # server for the changes to take effect, run "pg_ctl reload", or execute 20 | # "SELECT pg_reload_conf()". Some parameters, which are marked below, 21 | # require a server shutdown and restart to take effect. 22 | # 23 | # Any parameter can also be given as a command-line option to the server, e.g., 24 | # "postgres -c log_connections=on". Some parameters can be changed at run time 25 | # with the "SET" SQL command. 26 | # 27 | # Memory units: kB = kilobytes Time units: ms = milliseconds 28 | # MB = megabytes s = seconds 29 | # GB = gigabytes min = minutes 30 | # TB = terabytes h = hours 31 | # d = days 32 | 33 | 34 | #------------------------------------------------------------------------------ 35 | # FILE LOCATIONS 36 | #------------------------------------------------------------------------------ 37 | 38 | # The default values of these variables are driven from the -D command-line 39 | # option or PGDATA environment variable, represented here as ConfigDir. 40 | 41 | data_directory = '/var/lib/postgresql/12/main' # use data in another directory 42 | # (change requires restart) 43 | hba_file = '/etc/postgresql/12/main/pg_hba.conf' # host-based authentication file 44 | # (change requires restart) 45 | ident_file = '/etc/postgresql/12/main/pg_ident.conf' # ident configuration file 46 | # (change requires restart) 47 | 48 | # If external_pid_file is not explicitly set, no extra PID file is written. 49 | external_pid_file = '/var/run/postgresql/12-main.pid' # write an extra PID file 50 | # (change requires restart) 51 | 52 | 53 | #------------------------------------------------------------------------------ 54 | # CONNECTIONS AND AUTHENTICATION 55 | #------------------------------------------------------------------------------ 56 | 57 | # - Connection Settings - 58 | 59 | #listen_addresses = 'localhost' # what IP address(es) to listen on; 60 | # comma-separated list of addresses; 61 | # defaults to 'localhost'; use '*' for all 62 | # (change requires restart) 63 | listen_addresses = '*' 64 | port = 5432 # (change requires restart) 65 | max_connections = 100 # (change requires restart) 66 | #superuser_reserved_connections = 3 # (change requires restart) 67 | unix_socket_directories = '/var/run/postgresql' # comma-separated list of directories 68 | # (change requires restart) 69 | #unix_socket_group = '' # (change requires restart) 70 | #unix_socket_permissions = 0777 # begin with 0 to use octal notation 71 | # (change requires restart) 72 | #bonjour = off # advertise server via Bonjour 73 | # (change requires restart) 74 | #bonjour_name = '' # defaults to the computer name 75 | # (change requires restart) 76 | 77 | # - TCP settings - 78 | # see "man 7 tcp" for details 79 | 80 | #tcp_keepalives_idle = 0 # TCP_KEEPIDLE, in seconds; 81 | # 0 selects the system default 82 | #tcp_keepalives_interval = 0 # TCP_KEEPINTVL, in seconds; 83 | # 0 selects the system default 84 | #tcp_keepalives_count = 0 # TCP_KEEPCNT; 85 | # 0 selects the system default 86 | #tcp_user_timeout = 0 # TCP_USER_TIMEOUT, in milliseconds; 87 | # 0 selects the system default 88 | 89 | # - Authentication - 90 | 91 | #authentication_timeout = 1min # 1s-600s 92 | #password_encryption = md5 # md5 or scram-sha-256 93 | #db_user_namespace = off 94 | 95 | # GSSAPI using Kerberos 96 | #krb_server_keyfile = '' 97 | #krb_caseins_users = off 98 | 99 | # - SSL - 100 | 101 | #ssl = on 102 | ssl = off 103 | #ssl_ca_file = '' 104 | ssl_cert_file = '/etc/ssl/certs/ssl-cert-snakeoil.pem' 105 | #ssl_crl_file = '' 106 | ssl_key_file = '/etc/ssl/private/ssl-cert-snakeoil.key' 107 | #ssl_ciphers = 'HIGH:MEDIUM:+3DES:!aNULL' # allowed SSL ciphers 108 | #ssl_prefer_server_ciphers = on 109 | #ssl_ecdh_curve = 'prime256v1' 110 | #ssl_min_protocol_version = 'TLSv1' 111 | #ssl_max_protocol_version = '' 112 | #ssl_dh_params_file = '' 113 | #ssl_passphrase_command = '' 114 | #ssl_passphrase_command_supports_reload = off 115 | 116 | 117 | #------------------------------------------------------------------------------ 118 | # RESOURCE USAGE (except WAL) 119 | #------------------------------------------------------------------------------ 120 | 121 | # - Memory - 122 | 123 | shared_buffers = 128MB # min 128kB 124 | # (change requires restart) 125 | #huge_pages = try # on, off, or try 126 | # (change requires restart) 127 | #temp_buffers = 8MB # min 800kB 128 | #max_prepared_transactions = 0 # zero disables the feature 129 | # (change requires restart) 130 | # Caution: it is not advisable to set max_prepared_transactions nonzero unless 131 | # you actively intend to use prepared transactions. 132 | #work_mem = 4MB # min 64kB 133 | #maintenance_work_mem = 64MB # min 1MB 134 | #autovacuum_work_mem = -1 # min 1MB, or -1 to use maintenance_work_mem 135 | #max_stack_depth = 2MB # min 100kB 136 | #shared_memory_type = mmap # the default is the first option 137 | # supported by the operating system: 138 | # mmap 139 | # sysv 140 | # windows 141 | # (change requires restart) 142 | dynamic_shared_memory_type = posix # the default is the first option 143 | # supported by the operating system: 144 | # posix 145 | # sysv 146 | # windows 147 | # mmap 148 | # (change requires restart) 149 | 150 | # - Disk - 151 | 152 | #temp_file_limit = -1 # limits per-process temp file space 153 | # in kB, or -1 for no limit 154 | 155 | # - Kernel Resources - 156 | 157 | #max_files_per_process = 1000 # min 25 158 | # (change requires restart) 159 | 160 | # - Cost-Based Vacuum Delay - 161 | 162 | #vacuum_cost_delay = 0 # 0-100 milliseconds (0 disables) 163 | #vacuum_cost_page_hit = 1 # 0-10000 credits 164 | #vacuum_cost_page_miss = 10 # 0-10000 credits 165 | #vacuum_cost_page_dirty = 20 # 0-10000 credits 166 | #vacuum_cost_limit = 200 # 1-10000 credits 167 | 168 | # - Background Writer - 169 | 170 | #bgwriter_delay = 200ms # 10-10000ms between rounds 171 | #bgwriter_lru_maxpages = 100 # max buffers written/round, 0 disables 172 | #bgwriter_lru_multiplier = 2.0 # 0-10.0 multiplier on buffers scanned/round 173 | #bgwriter_flush_after = 512kB # measured in pages, 0 disables 174 | 175 | # - Asynchronous Behavior - 176 | 177 | #effective_io_concurrency = 1 # 1-1000; 0 disables prefetching 178 | #max_worker_processes = 8 # (change requires restart) 179 | #max_parallel_maintenance_workers = 2 # taken from max_parallel_workers 180 | #max_parallel_workers_per_gather = 2 # taken from max_parallel_workers 181 | #parallel_leader_participation = on 182 | #max_parallel_workers = 8 # maximum number of max_worker_processes that 183 | # can be used in parallel operations 184 | #old_snapshot_threshold = -1 # 1min-60d; -1 disables; 0 is immediate 185 | # (change requires restart) 186 | #backend_flush_after = 0 # measured in pages, 0 disables 187 | 188 | 189 | #------------------------------------------------------------------------------ 190 | # WRITE-AHEAD LOG 191 | #------------------------------------------------------------------------------ 192 | 193 | # - Settings - 194 | 195 | #wal_level = replica # minimal, replica, or logical 196 | # (change requires restart) 197 | #fsync = on # flush data to disk for crash safety 198 | # (turning this off can cause 199 | # unrecoverable data corruption) 200 | #synchronous_commit = on # synchronization level; 201 | # off, local, remote_write, remote_apply, or on 202 | #wal_sync_method = fsync # the default is the first option 203 | # supported by the operating system: 204 | # open_datasync 205 | # fdatasync (default on Linux) 206 | # fsync 207 | # fsync_writethrough 208 | # open_sync 209 | #full_page_writes = on # recover from partial page writes 210 | #wal_compression = off # enable compression of full-page writes 211 | #wal_log_hints = off # also do full page writes of non-critical updates 212 | # (change requires restart) 213 | #wal_init_zero = on # zero-fill new WAL files 214 | #wal_recycle = on # recycle WAL files 215 | #wal_buffers = -1 # min 32kB, -1 sets based on shared_buffers 216 | # (change requires restart) 217 | #wal_writer_delay = 200ms # 1-10000 milliseconds 218 | #wal_writer_flush_after = 1MB # measured in pages, 0 disables 219 | 220 | #commit_delay = 0 # range 0-100000, in microseconds 221 | #commit_siblings = 5 # range 1-1000 222 | 223 | # - Checkpoints - 224 | 225 | #checkpoint_timeout = 5min # range 30s-1d 226 | max_wal_size = 1GB 227 | min_wal_size = 80MB 228 | #checkpoint_completion_target = 0.5 # checkpoint target duration, 0.0 - 1.0 229 | #checkpoint_flush_after = 256kB # measured in pages, 0 disables 230 | #checkpoint_warning = 30s # 0 disables 231 | 232 | # - Archiving - 233 | 234 | #archive_mode = off # enables archiving; off, on, or always 235 | # (change requires restart) 236 | #archive_command = '' # command to use to archive a logfile segment 237 | # placeholders: %p = path of file to archive 238 | # %f = file name only 239 | # e.g. 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f' 240 | #archive_timeout = 0 # force a logfile segment switch after this 241 | # number of seconds; 0 disables 242 | 243 | # - Archive Recovery - 244 | 245 | # These are only used in recovery mode. 246 | 247 | #restore_command = '' # command to use to restore an archived logfile segment 248 | # placeholders: %p = path of file to restore 249 | # %f = file name only 250 | # e.g. 'cp /mnt/server/archivedir/%f %p' 251 | # (change requires restart) 252 | #archive_cleanup_command = '' # command to execute at every restartpoint 253 | #recovery_end_command = '' # command to execute at completion of recovery 254 | 255 | # - Recovery Target - 256 | 257 | # Set these only when performing a targeted recovery. 258 | 259 | #recovery_target = '' # 'immediate' to end recovery as soon as a 260 | # consistent state is reached 261 | # (change requires restart) 262 | #recovery_target_name = '' # the named restore point to which recovery will proceed 263 | # (change requires restart) 264 | #recovery_target_time = '' # the time stamp up to which recovery will proceed 265 | # (change requires restart) 266 | #recovery_target_xid = '' # the transaction ID up to which recovery will proceed 267 | # (change requires restart) 268 | #recovery_target_lsn = '' # the WAL LSN up to which recovery will proceed 269 | # (change requires restart) 270 | #recovery_target_inclusive = on # Specifies whether to stop: 271 | # just after the specified recovery target (on) 272 | # just before the recovery target (off) 273 | # (change requires restart) 274 | #recovery_target_timeline = 'latest' # 'current', 'latest', or timeline ID 275 | # (change requires restart) 276 | #recovery_target_action = 'pause' # 'pause', 'promote', 'shutdown' 277 | # (change requires restart) 278 | 279 | 280 | #------------------------------------------------------------------------------ 281 | # REPLICATION 282 | #------------------------------------------------------------------------------ 283 | 284 | # - Sending Servers - 285 | 286 | # Set these on the master and on any standby that will send replication data. 287 | 288 | #max_wal_senders = 10 # max number of walsender processes 289 | # (change requires restart) 290 | #wal_keep_segments = 0 # in logfile segments; 0 disables 291 | #wal_sender_timeout = 60s # in milliseconds; 0 disables 292 | 293 | #max_replication_slots = 10 # max number of replication slots 294 | # (change requires restart) 295 | #track_commit_timestamp = off # collect timestamp of transaction commit 296 | # (change requires restart) 297 | 298 | # - Master Server - 299 | 300 | # These settings are ignored on a standby server. 301 | 302 | #synchronous_standby_names = '' # standby servers that provide sync rep 303 | # method to choose sync standbys, number of sync standbys, 304 | # and comma-separated list of application_name 305 | # from standby(s); '*' = all 306 | #vacuum_defer_cleanup_age = 0 # number of xacts by which cleanup is delayed 307 | 308 | # - Standby Servers - 309 | 310 | # These settings are ignored on a master server. 311 | 312 | #primary_conninfo = '' # connection string to sending server 313 | # (change requires restart) 314 | #primary_slot_name = '' # replication slot on sending server 315 | # (change requires restart) 316 | #promote_trigger_file = '' # file name whose presence ends recovery 317 | #hot_standby = on # "off" disallows queries during recovery 318 | # (change requires restart) 319 | #max_standby_archive_delay = 30s # max delay before canceling queries 320 | # when reading WAL from archive; 321 | # -1 allows indefinite delay 322 | #max_standby_streaming_delay = 30s # max delay before canceling queries 323 | # when reading streaming WAL; 324 | # -1 allows indefinite delay 325 | #wal_receiver_status_interval = 10s # send replies at least this often 326 | # 0 disables 327 | #hot_standby_feedback = off # send info from standby to prevent 328 | # query conflicts 329 | #wal_receiver_timeout = 60s # time that receiver waits for 330 | # communication from master 331 | # in milliseconds; 0 disables 332 | #wal_retrieve_retry_interval = 5s # time to wait before retrying to 333 | # retrieve WAL after a failed attempt 334 | #recovery_min_apply_delay = 0 # minimum delay for applying changes during recovery 335 | 336 | # - Subscribers - 337 | 338 | # These settings are ignored on a publisher. 339 | 340 | #max_logical_replication_workers = 4 # taken from max_worker_processes 341 | # (change requires restart) 342 | #max_sync_workers_per_subscription = 2 # taken from max_logical_replication_workers 343 | 344 | 345 | #------------------------------------------------------------------------------ 346 | # QUERY TUNING 347 | #------------------------------------------------------------------------------ 348 | 349 | # - Planner Method Configuration - 350 | 351 | #enable_bitmapscan = on 352 | #enable_hashagg = on 353 | #enable_hashjoin = on 354 | #enable_indexscan = on 355 | #enable_indexonlyscan = on 356 | #enable_material = on 357 | #enable_mergejoin = on 358 | #enable_nestloop = on 359 | #enable_parallel_append = on 360 | #enable_seqscan = on 361 | #enable_sort = on 362 | #enable_tidscan = on 363 | #enable_partitionwise_join = off 364 | #enable_partitionwise_aggregate = off 365 | #enable_parallel_hash = on 366 | #enable_partition_pruning = on 367 | 368 | # - Planner Cost Constants - 369 | 370 | #seq_page_cost = 1.0 # measured on an arbitrary scale 371 | #random_page_cost = 4.0 # same scale as above 372 | #cpu_tuple_cost = 0.01 # same scale as above 373 | #cpu_index_tuple_cost = 0.005 # same scale as above 374 | #cpu_operator_cost = 0.0025 # same scale as above 375 | #parallel_tuple_cost = 0.1 # same scale as above 376 | #parallel_setup_cost = 1000.0 # same scale as above 377 | 378 | #jit_above_cost = 100000 # perform JIT compilation if available 379 | # and query more expensive than this; 380 | # -1 disables 381 | #jit_inline_above_cost = 500000 # inline small functions if query is 382 | # more expensive than this; -1 disables 383 | #jit_optimize_above_cost = 500000 # use expensive JIT optimizations if 384 | # query is more expensive than this; 385 | # -1 disables 386 | 387 | #min_parallel_table_scan_size = 8MB 388 | #min_parallel_index_scan_size = 512kB 389 | #effective_cache_size = 4GB 390 | 391 | # - Genetic Query Optimizer - 392 | 393 | #geqo = on 394 | #geqo_threshold = 12 395 | #geqo_effort = 5 # range 1-10 396 | #geqo_pool_size = 0 # selects default based on effort 397 | #geqo_generations = 0 # selects default based on effort 398 | #geqo_selection_bias = 2.0 # range 1.5-2.0 399 | #geqo_seed = 0.0 # range 0.0-1.0 400 | 401 | # - Other Planner Options - 402 | 403 | #default_statistics_target = 100 # range 1-10000 404 | #constraint_exclusion = partition # on, off, or partition 405 | #cursor_tuple_fraction = 0.1 # range 0.0-1.0 406 | #from_collapse_limit = 8 407 | #join_collapse_limit = 8 # 1 disables collapsing of explicit 408 | # JOIN clauses 409 | #force_parallel_mode = off 410 | #jit = on # allow JIT compilation 411 | #plan_cache_mode = auto # auto, force_generic_plan or 412 | # force_custom_plan 413 | 414 | 415 | #------------------------------------------------------------------------------ 416 | # REPORTING AND LOGGING 417 | #------------------------------------------------------------------------------ 418 | 419 | # - Where to Log - 420 | 421 | #log_destination = 'stderr' # Valid values are combinations of 422 | # stderr, csvlog, syslog, and eventlog, 423 | # depending on platform. csvlog 424 | # requires logging_collector to be on. 425 | 426 | # This is used when logging to stderr: 427 | #logging_collector = off # Enable capturing of stderr and csvlog 428 | # into log files. Required to be on for 429 | # csvlogs. 430 | # (change requires restart) 431 | 432 | # These are only used if logging_collector is on: 433 | #log_directory = 'log' # directory where log files are written, 434 | # can be absolute or relative to PGDATA 435 | #log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' # log file name pattern, 436 | # can include strftime() escapes 437 | #log_file_mode = 0600 # creation mode for log files, 438 | # begin with 0 to use octal notation 439 | #log_truncate_on_rotation = off # If on, an existing log file with the 440 | # same name as the new log file will be 441 | # truncated rather than appended to. 442 | # But such truncation only occurs on 443 | # time-driven rotation, not on restarts 444 | # or size-driven rotation. Default is 445 | # off, meaning append to existing files 446 | # in all cases. 447 | #log_rotation_age = 1d # Automatic rotation of logfiles will 448 | # happen after that time. 0 disables. 449 | #log_rotation_size = 10MB # Automatic rotation of logfiles will 450 | # happen after that much log output. 451 | # 0 disables. 452 | 453 | # These are relevant when logging to syslog: 454 | #syslog_facility = 'LOCAL0' 455 | #syslog_ident = 'postgres' 456 | #syslog_sequence_numbers = on 457 | #syslog_split_messages = on 458 | 459 | # This is only relevant when logging to eventlog (win32): 460 | # (change requires restart) 461 | #event_source = 'PostgreSQL' 462 | 463 | # - When to Log - 464 | 465 | #log_min_messages = warning # values in order of decreasing detail: 466 | # debug5 467 | # debug4 468 | # debug3 469 | # debug2 470 | # debug1 471 | # info 472 | # notice 473 | # warning 474 | # error 475 | # log 476 | # fatal 477 | # panic 478 | 479 | #log_min_error_statement = error # values in order of decreasing detail: 480 | # debug5 481 | # debug4 482 | # debug3 483 | # debug2 484 | # debug1 485 | # info 486 | # notice 487 | # warning 488 | # error 489 | # log 490 | # fatal 491 | # panic (effectively off) 492 | 493 | #log_min_duration_statement = -1 # -1 is disabled, 0 logs all statements 494 | # and their durations, > 0 logs only 495 | # statements running at least this number 496 | # of milliseconds 497 | 498 | #log_transaction_sample_rate = 0.0 # Fraction of transactions whose statements 499 | # are logged regardless of their duration. 1.0 logs all 500 | # statements from all transactions, 0.0 never logs. 501 | 502 | # - What to Log - 503 | 504 | #debug_print_parse = off 505 | #debug_print_rewritten = off 506 | #debug_print_plan = off 507 | #debug_pretty_print = on 508 | #log_checkpoints = off 509 | #log_connections = off 510 | #log_disconnections = off 511 | #log_duration = off 512 | #log_error_verbosity = default # terse, default, or verbose messages 513 | #log_hostname = off 514 | #log_line_prefix = '%m [%p] %q%u@%d ' # special values: 515 | log_line_prefix='%p %a %l ' 516 | # %a = application name 517 | # %u = user name 518 | # %d = database name 519 | # %r = remote host and port 520 | # %h = remote host 521 | # %p = process ID 522 | # %t = timestamp without milliseconds 523 | # %m = timestamp with milliseconds 524 | # %n = timestamp with milliseconds (as a Unix epoch) 525 | # %i = command tag 526 | # %e = SQL state 527 | # %c = session ID 528 | # %l = session line number 529 | # %s = session start timestamp 530 | # %v = virtual transaction ID 531 | # %x = transaction ID (0 if none) 532 | # %q = stop here in non-session 533 | # processes 534 | # %% = '%' 535 | # e.g. '<%u%%%d> ' 536 | #log_lock_waits = off # log lock waits >= deadlock_timeout 537 | #log_statement = 'none' # none, ddl, mod, all 538 | log_statement=all 539 | #log_replication_commands = off 540 | #log_temp_files = -1 # log temporary files equal or larger 541 | # than the specified size in kilobytes; 542 | # -1 disables, 0 logs all temp files 543 | log_timezone = 'America/Los_Angeles' 544 | 545 | #------------------------------------------------------------------------------ 546 | # PROCESS TITLE 547 | #------------------------------------------------------------------------------ 548 | 549 | cluster_name = '12/main' # added to process titles if nonempty 550 | # (change requires restart) 551 | #update_process_title = on 552 | 553 | 554 | #------------------------------------------------------------------------------ 555 | # STATISTICS 556 | #------------------------------------------------------------------------------ 557 | 558 | # - Query and Index Statistics Collector - 559 | 560 | #track_activities = on 561 | #track_counts = on 562 | #track_io_timing = off 563 | #track_functions = none # none, pl, all 564 | #track_activity_query_size = 1024 # (change requires restart) 565 | stats_temp_directory = '/var/run/postgresql/12-main.pg_stat_tmp' 566 | 567 | 568 | # - Monitoring - 569 | 570 | #log_parser_stats = off 571 | #log_planner_stats = off 572 | #log_executor_stats = off 573 | #log_statement_stats = off 574 | 575 | 576 | #------------------------------------------------------------------------------ 577 | # AUTOVACUUM 578 | #------------------------------------------------------------------------------ 579 | 580 | #autovacuum = on # Enable autovacuum subprocess? 'on' 581 | # requires track_counts to also be on. 582 | #log_autovacuum_min_duration = -1 # -1 disables, 0 logs all actions and 583 | # their durations, > 0 logs only 584 | # actions running at least this number 585 | # of milliseconds. 586 | #autovacuum_max_workers = 3 # max number of autovacuum subprocesses 587 | # (change requires restart) 588 | autovacuum_naptime = 5s # time between autovacuum runs 589 | #autovacuum_vacuum_threshold = 50 # min number of row updates before 590 | # vacuum 591 | #autovacuum_analyze_threshold = 50 # min number of row updates before 592 | # analyze 593 | #autovacuum_vacuum_scale_factor = 0.2 # fraction of table size before vacuum 594 | #autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze 595 | #autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum 596 | # (change requires restart) 597 | #autovacuum_multixact_freeze_max_age = 400000000 # maximum multixact age 598 | # before forced vacuum 599 | # (change requires restart) 600 | #autovacuum_vacuum_cost_delay = 2ms # default vacuum cost delay for 601 | # autovacuum, in milliseconds; 602 | # -1 means use vacuum_cost_delay 603 | #autovacuum_vacuum_cost_limit = -1 # default vacuum cost limit for 604 | # autovacuum, -1 means use 605 | # vacuum_cost_limit 606 | 607 | 608 | #------------------------------------------------------------------------------ 609 | # CLIENT CONNECTION DEFAULTS 610 | #------------------------------------------------------------------------------ 611 | 612 | # - Statement Behavior - 613 | 614 | #client_min_messages = notice # values in order of decreasing detail: 615 | # debug5 616 | # debug4 617 | # debug3 618 | # debug2 619 | # debug1 620 | # log 621 | # notice 622 | # warning 623 | # error 624 | #search_path = '"$user", public' # schema names 625 | #row_security = on 626 | #default_tablespace = '' # a tablespace name, '' uses the default 627 | #temp_tablespaces = '' # a list of tablespace names, '' uses 628 | # only default tablespace 629 | #default_table_access_method = 'heap' 630 | #check_function_bodies = on 631 | #default_transaction_isolation = 'read committed' 632 | #default_transaction_read_only = off 633 | #default_transaction_deferrable = off 634 | #session_replication_role = 'origin' 635 | #statement_timeout = 0 # in milliseconds, 0 is disabled 636 | #lock_timeout = 0 # in milliseconds, 0 is disabled 637 | #idle_in_transaction_session_timeout = 0 # in milliseconds, 0 is disabled 638 | #vacuum_freeze_min_age = 50000000 639 | #vacuum_freeze_table_age = 150000000 640 | #vacuum_multixact_freeze_min_age = 5000000 641 | #vacuum_multixact_freeze_table_age = 150000000 642 | #vacuum_cleanup_index_scale_factor = 0.1 # fraction of total number of tuples 643 | # before index cleanup, 0 always performs 644 | # index cleanup 645 | #bytea_output = 'hex' # hex, escape 646 | #xmlbinary = 'base64' 647 | #xmloption = 'content' 648 | #gin_fuzzy_search_limit = 0 649 | #gin_pending_list_limit = 4MB 650 | 651 | # - Locale and Formatting - 652 | 653 | datestyle = 'iso, mdy' 654 | #intervalstyle = 'postgres' 655 | timezone = 'America/Los_Angeles' 656 | #timezone_abbreviations = 'Default' # Select the set of available time zone 657 | # abbreviations. Currently, there are 658 | # Default 659 | # Australia (historical usage) 660 | # India 661 | # You can create your own file in 662 | # share/timezonesets/. 663 | #extra_float_digits = 1 # min -15, max 3; any value >0 actually 664 | # selects precise output mode 665 | #client_encoding = sql_ascii # actually, defaults to database 666 | # encoding 667 | 668 | # These settings are initialized by initdb, but they can be changed. 669 | lc_messages = 'en_US.UTF-8' # locale for system error message 670 | # strings 671 | lc_monetary = 'en_US.UTF-8' # locale for monetary formatting 672 | lc_numeric = 'en_US.UTF-8' # locale for number formatting 673 | lc_time = 'en_US.UTF-8' # locale for time formatting 674 | 675 | # default configuration for text search 676 | default_text_search_config = 'pg_catalog.english' 677 | 678 | # - Shared Library Preloading - 679 | 680 | #shared_preload_libraries = '' # (change requires restart) 681 | #local_preload_libraries = '' 682 | #session_preload_libraries = '' 683 | #jit_provider = 'llvmjit' # JIT library to use 684 | 685 | # - Other Defaults - 686 | 687 | #dynamic_library_path = '$libdir' 688 | 689 | 690 | #------------------------------------------------------------------------------ 691 | # LOCK MANAGEMENT 692 | #------------------------------------------------------------------------------ 693 | 694 | #deadlock_timeout = 1s 695 | #max_locks_per_transaction = 64 # min 10 696 | # (change requires restart) 697 | #max_pred_locks_per_transaction = 64 # min 10 698 | # (change requires restart) 699 | #max_pred_locks_per_relation = -2 # negative values mean 700 | # (max_pred_locks_per_transaction 701 | # / -max_pred_locks_per_relation) - 1 702 | #max_pred_locks_per_page = 2 # min 0 703 | 704 | 705 | #------------------------------------------------------------------------------ 706 | # VERSION AND PLATFORM COMPATIBILITY 707 | #------------------------------------------------------------------------------ 708 | 709 | # - Previous PostgreSQL Versions - 710 | 711 | #array_nulls = on 712 | #backslash_quote = safe_encoding # on, off, or safe_encoding 713 | #escape_string_warning = on 714 | #lo_compat_privileges = off 715 | #operator_precedence_warning = off 716 | #quote_all_identifiers = off 717 | #standard_conforming_strings = on 718 | #synchronize_seqscans = on 719 | 720 | # - Other Platforms and Clients - 721 | 722 | #transform_null_equals = off 723 | 724 | 725 | #------------------------------------------------------------------------------ 726 | # ERROR HANDLING 727 | #------------------------------------------------------------------------------ 728 | 729 | #exit_on_error = off # terminate session on any error? 730 | #restart_after_crash = on # reinitialize after backend crash? 731 | #data_sync_retry = off # retry or panic on failure to fsync 732 | # data? 733 | # (change requires restart) 734 | 735 | 736 | #------------------------------------------------------------------------------ 737 | # CONFIG FILE INCLUDES 738 | #------------------------------------------------------------------------------ 739 | 740 | # These options allow settings to be loaded from files other than the 741 | # default postgresql.conf. Note that these are directives, not variable 742 | # assignments, so they can usefully be given more than once. 743 | 744 | include_dir = 'conf.d' # include files ending in '.conf' from 745 | # a directory, e.g., 'conf.d' 746 | #include_if_exists = '...' # include file only if it exists 747 | #include = '...' # include file 748 | 749 | 750 | #------------------------------------------------------------------------------ 751 | # CUSTOMIZED OPTIONS 752 | #------------------------------------------------------------------------------ 753 | 754 | # Add settings for extensions here 755 | -------------------------------------------------------------------------------- /postgres/src/jepsen/postgres/cli.clj: -------------------------------------------------------------------------------- 1 | (ns jepsen.postgres.cli 2 | "Constructs tests, handles CLI arguments, etc." 3 | (:require [clojure.tools.logging :refer [info warn]] 4 | [clojure [pprint :refer [pprint]] 5 | [string :as str]] 6 | [clojure.java [io :as io]] 7 | [jepsen [cli :as cli] 8 | [checker :as checker] 9 | [db :as jdb] 10 | [generator :as gen] 11 | [nemesis :as nemesis] 12 | [os :as os] 13 | [tests :as tests] 14 | [util :as util]] 15 | [jepsen.nemesis.combined :as nc] 16 | [jepsen.os.debian :as debian] 17 | [jepsen.postgres [db :as db]] 18 | [jepsen.postgres.workload [append :as append] 19 | [ledger :as ledger]])) 20 | 21 | (def workloads 22 | {:append append/workload 23 | :ledger ledger/workload 24 | :none (fn [_] tests/noop-test)}) 25 | 26 | (def all-workloads 27 | "A collection of workloads we run by default." 28 | (remove #{:none} (keys workloads))) 29 | 30 | (def workloads-expected-to-pass 31 | "A collection of workload names which we expect should actually pass." 32 | (remove #{} all-workloads)) 33 | 34 | (def all-nemeses 35 | "Combinations of nemeses for tests" 36 | [[] 37 | [:pause :kill :partition :clock]]) 38 | 39 | (def special-nemeses 40 | "A map of special nemesis names to collections of faults" 41 | {:none [] 42 | :all [:pause :kill :partition :clock]}) 43 | 44 | (defn parse-nemesis-spec 45 | "Takes a comma-separated nemesis string and returns a collection of keyword 46 | faults." 47 | [spec] 48 | (->> (str/split spec #",") 49 | (map keyword) 50 | (mapcat #(get special-nemeses % [%])))) 51 | 52 | (def short-isolation 53 | {:strict-serializable "Strict-1SR" 54 | :serializable "S" 55 | :strong-snapshot-isolation "Strong-SI" 56 | :snapshot-isolation "SI" 57 | :repeatable-read "RR" 58 | :read-committed "RC" 59 | :read-uncommitted "RU"}) 60 | 61 | (defn postgres-test 62 | "Given an options map from the command line runner (e.g. :nodes, :ssh, 63 | :concurrency, ...), constructs a test map." 64 | [opts] 65 | (let [workload-name (:workload opts) 66 | workload ((workloads workload-name) opts) 67 | db (if (:existing-postgres opts) 68 | jdb/noop 69 | (db/db opts)) 70 | os (if (:existing-postgres opts) 71 | os/noop 72 | debian/os) 73 | nemesis (if (:existing-postgres opts) 74 | {:nemesis nemesis/noop} 75 | (nc/nemesis-package 76 | {:db db 77 | :nodes (:nodes opts) 78 | :faults (:nemesis opts) 79 | :partition {:targets [:primaries]} 80 | :pause {:targets [nil :one :primaries :majority :all]} 81 | :kill {:targets [nil :one :primaries :majority :all]} 82 | :interval (:nemesis-interval opts)}))] 83 | (merge tests/noop-test 84 | opts 85 | {:name (str "postgres " (name workload-name) 86 | " " (short-isolation (:isolation opts)) " (" 87 | (short-isolation (:expected-consistency-model opts)) ")" 88 | (when-let [ns (:nemesis opts)] 89 | (str " " (str/join "," (map name (:nemesis opts)))))) 90 | :os os 91 | :db db 92 | :checker (checker/compose 93 | {:perf (checker/perf 94 | {:nemeses (:perf nemesis)}) 95 | :clock (checker/clock-plot) 96 | :stats (checker/stats) 97 | :exceptions (checker/unhandled-exceptions) 98 | :workload (:checker workload)}) 99 | :client (:client workload) 100 | :nemesis (:nemesis nemesis) 101 | :generator (gen/phases 102 | (->> (:generator workload) 103 | (gen/stagger (/ (:rate opts))) 104 | (gen/nemesis (:generator nemesis)) 105 | (gen/time-limit (:time-limit opts))))} 106 | ; If we're using an existing postgres install, disable all SSH 107 | ; capabilities, including fault injection. 108 | (when (:existing-postgres opts) 109 | {:ssh {:dummy? true}})))) 110 | 111 | (def cli-opts 112 | "Additional CLI options" 113 | [["-i" "--isolation LEVEL" "What level of isolation we should set: serializable, repeatable-read, etc." 114 | :default :serializable 115 | :parse-fn keyword 116 | :validate [#{:read-uncommitted 117 | :read-committed 118 | :repeatable-read 119 | :serializable} 120 | "Should be one of read-uncommitted, read-committed, repeatable-read, or serializable"]] 121 | 122 | [nil "--existing-postgres" "If set, assumes nodes already have a running Postgres instance, skipping any OS and DB setup and teardown. Suitable for testing a local instance of Postgres (or some sort of pre-built cluster, like RDS) when you don't want to set up a whole-ass Jepsen environment." 123 | :default false] 124 | 125 | [nil "--expected-consistency-model MODEL" "What level of isolation do we *expect* to observe? Defaults to the same as --isolation." 126 | :default nil 127 | :parse-fn keyword] 128 | 129 | [nil "--key-count NUM" "Number of keys in active rotation." 130 | :default 10 131 | :parse-fn parse-long 132 | :validate [pos? "Must be a positive integer"]] 133 | 134 | [nil "--nemesis FAULTS" "A comma-separated list of nemesis faults to enable" 135 | :parse-fn parse-nemesis-spec 136 | :validate [(partial every? #{:pause :kill :partition :clock :member}) 137 | "Faults must be pause, kill, partition, clock, or member, or the special faults all or none."]] 138 | 139 | [nil "--max-txn-length NUM" "Maximum number of operations in a transaction." 140 | :default 4 141 | :parse-fn parse-long 142 | :validate [pos? "Must be a positive integer"]] 143 | 144 | [nil "--max-writes-per-key NUM" "Maximum number of writes to any given key." 145 | :default 256 146 | :parse-fn parse-long 147 | :validate [pos? "Must be a positive integer."]] 148 | 149 | [nil "--nemesis-interval SECS" "Roughly how long between nemesis operations." 150 | :default 5 151 | :parse-fn read-string 152 | :validate [pos? "Must be a positive number."]] 153 | 154 | [nil "--on-conflict" "If set, uses an ON CONFLICT clause to upsert rows."] 155 | 156 | [nil "--postgres-password PASS" "What password should we use to connect to postgres?" 157 | :default "pw"] 158 | 159 | [nil "--postgres-sslmode MODE" "What sslmode should we use to connect to postgres: require, disable?" 160 | :default "disable" 161 | :parse-fn str 162 | :validate [#{"require" "disable"} 163 | "Should be one of require, or disable"]] 164 | 165 | [nil "--postgres-port NUMBER" "What port should we connect to when talking to postgres?" 166 | :default 5432 167 | :parse-fn parse-long] 168 | 169 | [nil "--postgres-user NAME" "What username should we use to connect to postgres? Only use this with --existing-postgres, or you'll probably confuse the Stolon setup." 170 | :default "postgres"] 171 | 172 | [nil "--prepare-threshold INT" "Passes a prepareThreshold option to the JDBC spec." 173 | :parse-fn parse-long] 174 | 175 | ["-r" "--rate HZ" "Approximate request rate, in hz" 176 | :default 100 177 | :parse-fn read-string 178 | :validate [pos? "Must be a positive number."]] 179 | 180 | ["-v" "--version STRING" "What version of Postgres should we test?" 181 | :default "0.16.0"] 182 | 183 | ["-w" "--workload NAME" "What workload should we run?" 184 | :default :append 185 | :parse-fn keyword 186 | :validate [workloads (cli/one-of workloads)]] 187 | ]) 188 | 189 | (defn all-test-options 190 | "Takes base cli options, a collection of nemeses, workloads, and a test count, 191 | and constructs a sequence of test options." 192 | [cli nemeses workloads] 193 | (for [n nemeses, w workloads, i (range (:test-count cli))] 194 | (assoc cli 195 | :nemesis n 196 | :workload w))) 197 | 198 | (defn all-tests 199 | "Turns CLI options into a sequence of tests." 200 | [test-fn cli] 201 | (let [nemeses (if-let [n (:nemesis cli)] [n] all-nemeses) 202 | workloads (if-let [w (:workload cli)] [w] 203 | (if (:only-workloads-expected-to-pass cli) 204 | workloads-expected-to-pass 205 | all-workloads))] 206 | (->> (all-test-options cli nemeses workloads) 207 | (map test-fn)))) 208 | 209 | (defn opt-fn 210 | "Transforms CLI options before execution." 211 | [parsed] 212 | (update-in parsed [:options :expected-consistency-model] 213 | #(or % (get-in parsed [:options :isolation])))) 214 | 215 | (defn -main 216 | "Handles command line arguments. Can either run a test, or a web server for 217 | browsing results." 218 | [& args] 219 | (cli/run! (merge (cli/single-test-cmd {:test-fn postgres-test 220 | :opt-spec cli-opts 221 | :opt-fn opt-fn}) 222 | (cli/test-all-cmd {:tests-fn (partial all-tests 223 | postgres-test) 224 | :opt-spec cli-opts 225 | :opt-fn opt-fn}) 226 | (cli/serve-cmd)) 227 | args)) 228 | -------------------------------------------------------------------------------- /postgres/src/jepsen/postgres/client.clj: -------------------------------------------------------------------------------- 1 | (ns jepsen.postgres.client 2 | "Helper functions for interacting with PostgreSQL clients." 3 | (:require [clojure.tools.logging :refer [info warn]] 4 | [dom-top.core :refer [with-retry]] 5 | [jepsen [client :as client] 6 | [util :as util]] 7 | [next.jdbc :as j] 8 | [next.jdbc.result-set :as rs] 9 | [next.jdbc.sql.builder :as sqlb] 10 | [slingshot.slingshot :refer [try+ throw+]] 11 | [wall.hack :as wh]) 12 | (:import (java.sql Connection))) 13 | 14 | (defn open 15 | "Opens a connection to the given node." 16 | [test node] 17 | (let [spec {:dbtype "postgresql" 18 | ;:dbname "jepsen" 19 | :host node 20 | :port (:postgres-port test) 21 | :user (:postgres-user test) 22 | :password (:postgres-password test) 23 | :sslmode (:postgres-sslmode test)} 24 | spec (if-let [pt (:prepare-threshold test)] 25 | (assoc spec :prepareThreshold pt) 26 | spec) 27 | ds (j/get-datasource spec) 28 | conn (j/get-connection ds)] 29 | conn)) 30 | 31 | (defn set-transaction-isolation! 32 | "Sets the transaction isolation level on a connection. Returns conn." 33 | [conn level] 34 | (.setTransactionIsolation 35 | conn 36 | (case level 37 | :serializable Connection/TRANSACTION_SERIALIZABLE 38 | :repeatable-read Connection/TRANSACTION_REPEATABLE_READ 39 | :read-committed Connection/TRANSACTION_READ_COMMITTED 40 | :read-uncommitted Connection/TRANSACTION_READ_UNCOMMITTED)) 41 | conn) 42 | 43 | (defn close! 44 | "Closes a connection." 45 | [^java.sql.Connection conn] 46 | (.close conn)) 47 | 48 | (defn await-open 49 | "Waits for a connection to node to become available, returning conn. Helpful 50 | for starting up." 51 | [node] 52 | (with-retry [tries 100] 53 | (info "Waiting for" node "to come online...") 54 | (let [conn (open node)] 55 | (try (j/execute-one! conn 56 | ["create table if not exists jepsen_await ()"]) 57 | conn 58 | (catch org.postgresql.util.PSQLException e 59 | (condp re-find (.getMessage e) 60 | ; Ah, good, someone else already created the table 61 | #"duplicate key value violates unique constraint \"pg_type_typname_nsp_index\"" 62 | conn 63 | 64 | (throw e))))) 65 | (catch org.postgresql.util.PSQLException e 66 | (when (zero? tries) 67 | (throw e)) 68 | 69 | (Thread/sleep 5000) 70 | (condp re-find (.getMessage e) 71 | #"connection attempt failed" 72 | (retry (dec tries)) 73 | 74 | #"Connection to .+ refused" 75 | (retry (dec tries)) 76 | 77 | #"An I/O error occurred" 78 | (retry (dec tries)) 79 | 80 | (throw e))))) 81 | 82 | (defmacro with-errors 83 | "Takes an operation and a body; evals body, turning known errors into :fail 84 | or :info ops." 85 | [op & body] 86 | `(try ~@body 87 | (catch clojure.lang.ExceptionInfo e# 88 | (warn e# "Caught ex-info") 89 | (assoc ~op :type :info, :error [:ex-info (.getMessage e#)])) 90 | 91 | (catch org.postgresql.util.PSQLException e# 92 | (condp re-find (.getMessage e#) 93 | #"ERROR: cannot execute .+ in a read-only transaction" 94 | (assoc ~op :type :fail, :error [:read-only]) 95 | 96 | #"ERROR: could not serialize access" 97 | (assoc ~op :type :fail, :error [:could-not-serialize (.getMessage e#)]) 98 | 99 | #"ERROR: deadlock detected" 100 | (assoc ~op :type :fail, :error [:deadlock (.getMessage e#)]) 101 | 102 | #"An I/O error occurred" 103 | (assoc ~op :type :info, :error :io-error) 104 | 105 | #"connection has been closed" 106 | (assoc ~op :type :info, :error :connection-has-been-closed) 107 | 108 | (throw e#))))) 109 | -------------------------------------------------------------------------------- /postgres/src/jepsen/postgres/db.clj: -------------------------------------------------------------------------------- 1 | (ns jepsen.postgres.db 2 | "Database setup and automation." 3 | (:require [cheshire.core :as json] 4 | [clojure [pprint :refer [pprint]] 5 | [string :as str]] 6 | [clojure.java.io :as io] 7 | [clojure.tools.logging :refer [info warn]] 8 | [dom-top.core :refer [disorderly 9 | real-pmap]] 10 | [jepsen [control :as c] 11 | [core :as jepsen] 12 | [db :as db] 13 | [util :as util :refer [meh random-nonempty-subset]]] 14 | [jepsen.control.util :as cu] 15 | [jepsen.control.net :as cn] 16 | [jepsen.os.debian :as debian] 17 | [jepsen.postgres [client :as sc]] 18 | [slingshot.slingshot :refer [try+ throw+]])) 19 | 20 | (def user 21 | "The OS user which will run postgres." 22 | "postgres") 23 | 24 | (def just-postgres-log-file 25 | "/var/log/postgresql/postgresql-12-main.log") 26 | 27 | (defn install-pg! 28 | "Installs postgresql" 29 | [test node] 30 | (c/su 31 | ; Install apt key 32 | (c/exec :wget :--quiet :-O :- "https://www.postgresql.org/media/keys/ACCC4CF8.asc" c/| :apt-key :add :-) 33 | ; Add repo 34 | (debian/install [:lsb-release]) 35 | (let [release (c/exec :lsb_release :-cs)] 36 | (debian/add-repo! "postgresql" 37 | (str "deb http://apt.postgresql.org/pub/repos/apt/ " 38 | release "-pgdg main"))) 39 | ; Install 40 | (debian/install [:postgresql-12 :postgresql-client-12]) 41 | ; Deactivate default install 42 | (c/exec :service :postgresql :stop) 43 | (c/exec "update-rc.d" :postgresql :disable))) 44 | 45 | (defn db 46 | "A database which just runs a regular old single-node Postgres instance" 47 | [opts] 48 | (let [tcpdump (db/tcpdump {:ports [5432] 49 | ; Haaack, hardcoded for my particular cluster 50 | ; control node 51 | :filter "host 192.168.122.1"})] 52 | (reify db/DB 53 | (setup! [_ test node] 54 | (db/setup! tcpdump test node) 55 | (install-pg! test node) 56 | (c/su (c/exec :echo (slurp (io/resource "pg_hba.conf")) 57 | :> "/etc/postgresql/12/main/pg_hba.conf") 58 | (c/exec :echo (slurp (io/resource "postgresql.conf")) 59 | :> "/etc/postgresql/12/main/postgresql.conf")) 60 | 61 | ; Create fresh data dir 62 | (c/sudo user 63 | ; Can't create if it exists--installing will make this dir 64 | (c/exec :rm :-rf (c/lit "/var/lib/postgresql/12/main/*")) 65 | (c/exec "/usr/lib/postgresql/12/bin/initdb" 66 | :-D "/var/lib/postgresql/12/main")) 67 | 68 | (c/su (c/exec :service :postgresql :start))) 69 | 70 | (teardown! [_ test node] 71 | (c/su (try+ (c/exec :service :postgresql :stop) 72 | ; Not installed? 73 | (catch [:exit 5] _)) 74 | ; This might not actually work, so we have to kill the processes 75 | ; too 76 | (cu/grepkill! "postgres") 77 | (c/exec :rm :-rf (c/lit "/var/lib/postgresql/12/main/*"))) 78 | (try+ (c/sudo user 79 | (c/exec :truncate :-s 0 just-postgres-log-file)) 80 | (catch [:exit 1] _)) ; No user (not installed) 81 | (db/teardown! tcpdump test node)) 82 | 83 | db/LogFiles 84 | (log-files [_ test node] 85 | (concat (db/log-files tcpdump test node) 86 | [just-postgres-log-file])) 87 | 88 | db/Primary 89 | (setup-primary! [db test node]) 90 | (primaries [db test] 91 | ; Everyone's a winner! Really, there should only be one node here, 92 | ; so... it's kinda trivial. 93 | (:nodes test)) 94 | 95 | db/Process 96 | (start! [db test node] 97 | (c/su (c/exec :service :postgresql :restart))) 98 | 99 | (kill! [db test node] 100 | (doseq [pattern (shuffle 101 | ["postgres -D" ; Main process 102 | "main: checkpointer" 103 | "main: background writer" 104 | "main: walwriter" 105 | "main: autovacuum launcher"])] 106 | (Thread/sleep (rand-int 100)) 107 | (info "Killing" pattern "-" (cu/grepkill! pattern))))))) 108 | -------------------------------------------------------------------------------- /postgres/src/jepsen/postgres/workload/append.clj: -------------------------------------------------------------------------------- 1 | (ns jepsen.postgres.workload.append 2 | "Test for transactional list append." 3 | (:require [clojure.tools.logging :refer [info warn]] 4 | [clojure [pprint :refer [pprint]] 5 | [string :as str]] 6 | [dom-top.core :refer [loopr with-retry]] 7 | [elle.core :as elle] 8 | [jepsen [checker :as checker] 9 | [client :as client] 10 | [core :as jepsen] 11 | [generator :as gen] 12 | [util :as util]] 13 | [jepsen.checker.timeline :as timeline] 14 | [jepsen.tests.cycle.append :as append] 15 | [jepsen.postgres [client :as c]] 16 | [next.jdbc :as j] 17 | [next.jdbc.result-set :as rs] 18 | [next.jdbc.sql.builder :as sqlb] 19 | [slingshot.slingshot :refer [try+ throw+]])) 20 | 21 | (def default-table-count 3) 22 | 23 | (defn table-name 24 | "Takes an integer and constructs a table name." 25 | [i] 26 | (str "txn" i)) 27 | 28 | (defn table-for 29 | "What table should we use for the given key?" 30 | [table-count k] 31 | (table-name (mod (hash k) table-count))) 32 | 33 | (defn append-using-on-conflict! 34 | "Appends an element to a key using an INSERT ... ON CONFLICT statement." 35 | [conn test table k e] 36 | (j/execute! 37 | conn 38 | [(str "insert into " table " as t" 39 | " (id, sk, val) values (?, ?, ?)" 40 | " on conflict (id) do update set" 41 | " val = CONCAT(t.val, ',', ?) where " 42 | "t.id" 43 | ;(if (< (rand) 0.5) "t.id" "t.sk") 44 | " = ?") 45 | k k e e k])) 46 | 47 | (defn insert! 48 | "Performs an initial insert of a key with initial element e. Catches 49 | duplicate key exceptions, returning true if succeeded. If the insert fails 50 | due to a duplicate key, it'll break the rest of the transaction, assuming 51 | we're in a transaction, so we establish a savepoint before inserting and roll 52 | back to it on failure." 53 | [conn test txn? table k e] 54 | (try 55 | ;(info (if txn? "" "not") "in transaction") 56 | (when txn? (j/execute! conn ["savepoint upsert"])) 57 | (j/execute! conn 58 | [(str "insert into " table " (id, sk, val)" 59 | " values (?, ?, ?)") 60 | k k e]) 61 | (when txn? (j/execute! conn ["release savepoint upsert"])) 62 | true 63 | (catch org.postgresql.util.PSQLException e 64 | (if (re-find #"duplicate key value" (.getMessage e)) 65 | (do (info (if txn? "txn") "insert failed: " (.getMessage e)) 66 | (when txn? (j/execute! conn ["rollback to savepoint upsert"])) 67 | false) 68 | (throw e))))) 69 | 70 | (defn update! 71 | "Performs an update of a key k, adding element e. Returns true if the update 72 | succeeded, false otherwise." 73 | [conn test table k e] 74 | (let [res (-> conn 75 | (j/execute-one! [(str "update " table " set val = CONCAT(val, ',', ?)" 76 | " where id = ?") e k]))] 77 | ;(info :update res) 78 | (-> res 79 | :next.jdbc/update-count 80 | pos?))) 81 | 82 | (defn mop! 83 | "Executes a transactional micro-op on a connection. Returns the completed 84 | micro-op." 85 | [conn test txn? [f k v]] 86 | (let [table-count (:table-count test default-table-count) 87 | table (table-for table-count k)] 88 | (Thread/sleep (long (rand-int 10))) 89 | [f k (case f 90 | :r (let [r (j/execute! conn 91 | [(str "select (val) from " table " where " 92 | ;(if (< (rand) 0.5) "id" "sk") 93 | "id" 94 | " = ? ") 95 | k] 96 | {:builder-fn rs/as-unqualified-lower-maps})] 97 | (when-let [v (:val (first r))] 98 | (mapv parse-long (str/split v #",")))) 99 | 100 | :append 101 | (let [vs (str v)] 102 | (if (:on-conflict test) 103 | ; Use ON CONFLICT 104 | (append-using-on-conflict! conn test table k vs) 105 | ; Try an update, and if that fails, back off to an insert. 106 | (or (update! conn test table k vs) 107 | ; No dice, fall back to an insert 108 | (insert! conn test txn? table k vs) 109 | ; OK if THAT failed then we probably raced with another 110 | ; insert; let's try updating again. 111 | (update! conn test table k vs) 112 | ; And if THAT failed, all bets are off. This happens even 113 | ; under SERIALIZABLE, but I don't think it technically 114 | ; VIOLATES serializability. 115 | (throw+ {:type ::homebrew-upsert-failed 116 | :key k 117 | :element v}))) 118 | v))])) 119 | 120 | ; initialized? is an atom which we set when we first use the connection--we set 121 | ; up initial isolation levels, logging info, etc. This has to be stateful 122 | ; because we don't necessarily know what process is going to use the connection 123 | ; at open! time. 124 | (defrecord Client [node conn initialized?] 125 | client/Client 126 | (open! [this test node] 127 | (let [c (c/open test node)] 128 | (assoc this 129 | :node node 130 | :conn c 131 | :initialized? (atom false)))) 132 | 133 | (setup! [_ test] 134 | ; Secondaries may not be writable; always do writes on the primary node. 135 | (when (= node (jepsen/primary test)) 136 | (dotimes [i (:table-count test default-table-count)] 137 | (with-retry [conn conn 138 | tries 10] 139 | (j/execute! conn 140 | [(str "create table if not exists " (table-name i) 141 | " (id int not null primary key, 142 | sk int not null, 143 | val text)")]) 144 | (catch org.postgresql.util.PSQLException e 145 | (condp re-find (.getMessage e) 146 | #"duplicate key value violates unique constraint" 147 | :dup 148 | 149 | #"An I/O error occurred|connection has been closed" 150 | (do (when (zero? tries) 151 | (throw e)) 152 | (info "Retrying IO error") 153 | (Thread/sleep 1000) 154 | (c/close! conn) 155 | (retry (c/await-open node) 156 | (dec tries))) 157 | 158 | (throw e)))) 159 | ; Make sure we start fresh--in case we're using an existing postgres 160 | ; cluster and the DB automation isn't wiping the state for us. 161 | (j/execute! conn [(str "delete from " (table-name i))])))) 162 | 163 | (invoke! [_ test op] 164 | ; One-time connection setup 165 | (when (compare-and-set! initialized? false true) 166 | (j/execute! conn [(str "set application_name = 'jepsen process " 167 | (:process op) "'")]) 168 | (c/set-transaction-isolation! conn (:isolation test))) 169 | 170 | (c/with-errors op 171 | (let [txn (:value op) 172 | use-txn? (< 1 (count txn)) 173 | txn' (if use-txn? 174 | ;(if true 175 | (j/with-transaction [t conn 176 | {:isolation (:isolation test)}] 177 | (mapv (partial mop! t test true) txn)) 178 | (mapv (partial mop! conn test false) txn))] 179 | (assoc op :type :ok, :value txn')))) 180 | 181 | (teardown! [_ test]) 182 | 183 | (close! [this test] 184 | (c/close! conn))) 185 | 186 | (defn process->node 187 | "Converts a process back to a node ID." 188 | [test process] 189 | (nth (:nodes test) (mod process (count (:nodes test))))) 190 | 191 | (defn read-only 192 | "Converts writes to reads." 193 | [op] 194 | (loopr [txn' []] 195 | [[f k v :as mop] (:value op)] 196 | (recur (conj txn' (case f 197 | :r mop 198 | [:r k nil]))) 199 | (assoc op :f :read, :value txn'))) 200 | 201 | (defrecord ROGen [gen ro-nodes] 202 | gen/Generator 203 | (update [this test ctx event] 204 | (let [gen' (gen/update gen test ctx event)] 205 | (if (= [:read-only] (:error event)) 206 | ; Flag this node as read-only 207 | (let [node (process->node test (:process event))] 208 | (ROGen. gen' (conj ro-nodes node))) 209 | (ROGen. gen' ro-nodes)))) 210 | 211 | (op [this test ctx] 212 | (when-let [[op gen'] (gen/op gen test ctx)] 213 | (if (= :pending op) 214 | [:pending this] 215 | (let [node (process->node test (:process op))] 216 | (if (contains? ro-nodes node) 217 | (let [op (read-only op) 218 | ; Small chance of this node going back to normal 219 | ro-nodes' (if (< (rand) 0.001) 220 | (disj ro-nodes node) 221 | ro-nodes)] 222 | [op (ROGen. gen' ro-nodes')]) 223 | ; Pass through 224 | [op (ROGen. gen' ro-nodes)])))))) 225 | 226 | (defn ro-gen 227 | "Generator that detects read-only errors and flips to emitting read-only 228 | transactions on that node. Nodes fall out of the read-only pool randomly over 229 | time." 230 | [gen] 231 | (ROGen. gen #{})) 232 | 233 | (defn workload 234 | "A list append workload." 235 | [opts] 236 | (-> (append/test (assoc (select-keys opts [:key-count 237 | :max-txn-length 238 | :max-writes-per-key]) 239 | :min-txn-length 1 240 | :consistency-models [(:expected-consistency-model opts)])) 241 | (assoc :client (Client. nil nil nil)) 242 | (update :generator ro-gen))) 243 | -------------------------------------------------------------------------------- /postgres/src/jepsen/postgres/workload/ledger.clj: -------------------------------------------------------------------------------- 1 | (ns jepsen.postgres.workload.ledger 2 | "A test which aims to concretely demonstrate the impact of G2-item anomalies 3 | we found using the list-append test. We store a simulated bank ledger where 4 | each transaction is a row. Withdrawals require a positive balance across all 5 | rows. We attempt a double-spend attack, which should fail under 6 | serializability." 7 | (:require [clojure.tools.logging :refer [info warn]] 8 | [clojure [pprint :refer [pprint]] 9 | [string :as str]] 10 | [dom-top.core :refer [with-retry]] 11 | [jepsen [checker :as checker] 12 | [client :as client] 13 | [checker :as checker] 14 | [generator :as gen] 15 | [util :as util]] 16 | [jepsen.checker.timeline :as timeline] 17 | [jepsen.tests.cycle.append :as append] 18 | [jepsen.postgres [client :as c]] 19 | [knossos.op :as op] 20 | [next.jdbc :as j] 21 | [next.jdbc.result-set :as rs] 22 | [next.jdbc.sql.builder :as sqlb] 23 | [slingshot.slingshot :refer [try+ throw+]])) 24 | 25 | (def table "ledger") 26 | 27 | (defn add-entry! 28 | "Adds an account, amount pair to the ledger." 29 | [conn id account amount] 30 | (j/execute! 31 | conn 32 | [(str "insert into " table " (id, account, amount) values (?, ?, ?)") 33 | id account amount])) 34 | 35 | (defn balance 36 | "Selects the total of all ledger rows for an account." 37 | [conn test account] 38 | (or (:sum (j/execute-one! conn [(str "select sum(amount) from " table 39 | " where account = ?") 40 | account])) 41 | ; sum of no rows is null, not 0 42 | 0)) 43 | 44 | (defn balance-select 45 | "Selects the total of all ledger rows, by doing a direct read and summing 46 | ourselves." 47 | [conn test account id] 48 | (->> (j/execute! conn [(str "select (amount) from " table 49 | " where account = ? and id != ?") 50 | account id] 51 | {:builder-fn rs/as-unqualified-lower-maps}) 52 | (map :amount) 53 | (reduce + 0))) 54 | 55 | (defn transfer! 56 | "Alters the account balance by inserting a new ledger row iff the total of 57 | all rows would remain non-negative. Returns true if the transaction 58 | completed, false otherwise." 59 | [conn test id account amount] 60 | (if (pos? amount) 61 | (add-entry! conn id account amount) 62 | ; Check for minimum balance 63 | (let [balance (balance-select conn test account id)] 64 | (info :balance balance) 65 | (if (neg? (+ balance amount)) 66 | false 67 | (do (Thread/sleep (rand-int 10)) 68 | (add-entry! conn id account amount) 69 | true))))) 70 | 71 | ; initialized? is an atom which we set when we first use the connection--we set 72 | ; up initial isolation levels, logging info, etc. This has to be stateful 73 | ; because we don't necessarily know what process is going to use the connection 74 | ; at open! time. next-id is an atom we use to choose row identifiers. 75 | (defrecord Client [node conn initialized? next-id] 76 | client/Client 77 | (open! [this test node] 78 | (let [c (c/open test node)] 79 | (assoc this 80 | :node node 81 | :conn c 82 | :initialized? (atom false)))) 83 | 84 | (setup! [_ test] 85 | ; OK, so first worrying thing: why can this throw duplicate key errors if 86 | ; it's executed with "if not exists"? Ah, this is a known 87 | ; non-transactional DDL thing. 88 | (with-retry [conn conn 89 | tries 10] 90 | (j/execute! conn 91 | [(str "create table if not exists " table 92 | " (id int primary key, 93 | account int not null, 94 | amount int not null)")]) 95 | (j/execute! conn 96 | [(str "create index i_account on " table " (account)")]) 97 | (catch org.postgresql.util.PSQLException e 98 | (condp re-find (.getMessage e) 99 | #"duplicate key value violates unique constraint" 100 | :dup 101 | 102 | #"An I/O error occurred|connection has been closed" 103 | (do (when (zero? tries) 104 | (throw e)) 105 | (info "Retrying IO error") 106 | (Thread/sleep 1000) 107 | (c/close! conn) 108 | (retry (c/await-open test node) 109 | (dec tries))) 110 | 111 | (throw e)))) 112 | 113 | ; Make sure we start fresh--in case we're using an existing postgres 114 | ; cluster and the DB automation isn't wiping the state for us. 115 | (j/execute! conn [(str "delete from " table)])) 116 | 117 | (invoke! [_ test op] 118 | ; One-time connection setup 119 | (when (compare-and-set! initialized? false true) 120 | (j/execute! conn [(str "set application_name = 'jepsen process " 121 | (:process op) "'")]) 122 | (c/set-transaction-isolation! conn (:isolation test))) 123 | 124 | (let [[account amount] (:value op)] 125 | (c/with-errors op 126 | (j/with-transaction [t conn 127 | {:isolation (:isolation test)}] 128 | (assoc op :type (if (transfer! conn test 129 | (swap! next-id inc) 130 | account amount) 131 | :ok 132 | :fail)))))) 133 | 134 | (teardown! [_ test]) 135 | 136 | (close! [this test] 137 | (c/close! conn))) 138 | 139 | (defn check-account 140 | "Takes an [account, ops] tuple, and checks to make sure it has a non-negative 141 | balance." 142 | [[account ops]] 143 | ; When ops are indeterminate, we don't know if they took place or not. We 144 | ; have to take the most charitable interpretation: assume deposits succeed, 145 | ; withdrawals fail. 146 | (let [deposits (filter (comp pos? second :value) ops) 147 | withdrawals (filter (comp neg? second :value) ops) 148 | balance (->> (concat deposits (filter op/ok? withdrawals)) 149 | (map (comp second :value)) 150 | (reduce + 0))] 151 | (when (or (neg? balance) (pos? balance)) 152 | {:account account 153 | :balance balance}))) 154 | 155 | (defn checker 156 | [] 157 | (reify checker/Checker 158 | (check [_ test history opts] 159 | (let [errs (->> history 160 | (filter (comp #{:ok :info} :type)) 161 | (group-by (comp first :value)) 162 | (keep check-account))] 163 | {:valid? (not (seq errs)) 164 | :errors errs})))) 165 | 166 | (defn fund-then-double-spend-gen 167 | [] 168 | (->> (range) 169 | (map (fn [account] 170 | ; Initial fund 171 | (cons {:f :transfer, :value [account 10]} 172 | ; Double-spend attack 173 | (repeat (Math/pow 2 (rand-int 5)) 174 | {:f :transfer, :value [account -9]})))))) 175 | 176 | (defn rand-gen 177 | [] 178 | (->> (range) 179 | (map (fn [account] 180 | (->> (fn gen [] {:f :transfer, :value [account 181 | (- (rand-int 5) 3)]}) 182 | (gen/limit 16)))))) 183 | 184 | (defn workload 185 | "A package of client, checker, etc." 186 | [opts] 187 | {:client (Client. nil nil nil (atom 0)) 188 | :checker (checker) 189 | :generator (rand-gen)}) 190 | -------------------------------------------------------------------------------- /rds/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | /rds.edn 5 | /store 6 | profiles.clj 7 | pom.xml 8 | pom.xml.asc 9 | *.jar 10 | *.class 11 | /.lein-* 12 | /.nrepl-port 13 | .hgignore 14 | .hg/ 15 | -------------------------------------------------------------------------------- /rds/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/). 3 | 4 | ## [Unreleased] 5 | ### Changed 6 | - Add a new arity to `make-widget-async` to provide a different widget shape. 7 | 8 | ## [0.1.1] - 2020-05-29 9 | ### Changed 10 | - Documentation on how to make the widgets. 11 | 12 | ### Removed 13 | - `make-widget-sync` - we're all async, all the time. 14 | 15 | ### Fixed 16 | - Fixed widget maker to keep working when daylight savings switches over. 17 | 18 | ## 0.1.0 - 2020-05-29 19 | ### Added 20 | - Files from the new template. 21 | - Widget maker public API - `make-widget-sync`. 22 | 23 | [Unreleased]: https://github.com/your-name/jepsen.stolon/compare/0.1.1...HEAD 24 | [0.1.1]: https://github.com/your-name/jepsen.stolon/compare/0.1.0...0.1.1 25 | -------------------------------------------------------------------------------- /rds/LICENSE: -------------------------------------------------------------------------------- 1 | Eclipse Public License - v 2.0 2 | 3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE 4 | PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION 5 | OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 6 | 7 | 1. DEFINITIONS 8 | 9 | "Contribution" means: 10 | 11 | a) in the case of the initial Contributor, the initial content 12 | Distributed under this Agreement, and 13 | 14 | b) in the case of each subsequent Contributor: 15 | i) changes to the Program, and 16 | ii) additions to the Program; 17 | where such changes and/or additions to the Program originate from 18 | and are Distributed by that particular Contributor. A Contribution 19 | "originates" from a Contributor if it was added to the Program by 20 | such Contributor itself or anyone acting on such Contributor's behalf. 21 | Contributions do not include changes or additions to the Program that 22 | are not Modified Works. 23 | 24 | "Contributor" means any person or entity that Distributes the Program. 25 | 26 | "Licensed Patents" mean patent claims licensable by a Contributor which 27 | are necessarily infringed by the use or sale of its Contribution alone 28 | or when combined with the Program. 29 | 30 | "Program" means the Contributions Distributed in accordance with this 31 | Agreement. 32 | 33 | "Recipient" means anyone who receives the Program under this Agreement 34 | or any Secondary License (as applicable), including Contributors. 35 | 36 | "Derivative Works" shall mean any work, whether in Source Code or other 37 | form, that is based on (or derived from) the Program and for which the 38 | editorial revisions, annotations, elaborations, or other modifications 39 | represent, as a whole, an original work of authorship. 40 | 41 | "Modified Works" shall mean any work in Source Code or other form that 42 | results from an addition to, deletion from, or modification of the 43 | contents of the Program, including, for purposes of clarity any new file 44 | in Source Code form that contains any contents of the Program. Modified 45 | Works shall not include works that contain only declarations, 46 | interfaces, types, classes, structures, or files of the Program solely 47 | in each case in order to link to, bind by name, or subclass the Program 48 | or Modified Works thereof. 49 | 50 | "Distribute" means the acts of a) distributing or b) making available 51 | in any manner that enables the transfer of a copy. 52 | 53 | "Source Code" means the form of a Program preferred for making 54 | modifications, including but not limited to software source code, 55 | documentation source, and configuration files. 56 | 57 | "Secondary License" means either the GNU General Public License, 58 | Version 2.0, or any later versions of that license, including any 59 | exceptions or additional permissions as identified by the initial 60 | Contributor. 61 | 62 | 2. GRANT OF RIGHTS 63 | 64 | a) Subject to the terms of this Agreement, each Contributor hereby 65 | grants Recipient a non-exclusive, worldwide, royalty-free copyright 66 | license to reproduce, prepare Derivative Works of, publicly display, 67 | publicly perform, Distribute and sublicense the Contribution of such 68 | Contributor, if any, and such Derivative Works. 69 | 70 | b) Subject to the terms of this Agreement, each Contributor hereby 71 | grants Recipient a non-exclusive, worldwide, royalty-free patent 72 | license under Licensed Patents to make, use, sell, offer to sell, 73 | import and otherwise transfer the Contribution of such Contributor, 74 | if any, in Source Code or other form. This patent license shall 75 | apply to the combination of the Contribution and the Program if, at 76 | the time the Contribution is added by the Contributor, such addition 77 | of the Contribution causes such combination to be covered by the 78 | Licensed Patents. The patent license shall not apply to any other 79 | combinations which include the Contribution. No hardware per se is 80 | licensed hereunder. 81 | 82 | c) Recipient understands that although each Contributor grants the 83 | licenses to its Contributions set forth herein, no assurances are 84 | provided by any Contributor that the Program does not infringe the 85 | patent or other intellectual property rights of any other entity. 86 | Each Contributor disclaims any liability to Recipient for claims 87 | brought by any other entity based on infringement of intellectual 88 | property rights or otherwise. As a condition to exercising the 89 | rights and licenses granted hereunder, each Recipient hereby 90 | assumes sole responsibility to secure any other intellectual 91 | property rights needed, if any. For example, if a third party 92 | patent license is required to allow Recipient to Distribute the 93 | Program, it is Recipient's responsibility to acquire that license 94 | before distributing the Program. 95 | 96 | d) Each Contributor represents that to its knowledge it has 97 | sufficient copyright rights in its Contribution, if any, to grant 98 | the copyright license set forth in this Agreement. 99 | 100 | e) Notwithstanding the terms of any Secondary License, no 101 | Contributor makes additional grants to any Recipient (other than 102 | those set forth in this Agreement) as a result of such Recipient's 103 | receipt of the Program under the terms of a Secondary License 104 | (if permitted under the terms of Section 3). 105 | 106 | 3. REQUIREMENTS 107 | 108 | 3.1 If a Contributor Distributes the Program in any form, then: 109 | 110 | a) the Program must also be made available as Source Code, in 111 | accordance with section 3.2, and the Contributor must accompany 112 | the Program with a statement that the Source Code for the Program 113 | is available under this Agreement, and informs Recipients how to 114 | obtain it in a reasonable manner on or through a medium customarily 115 | used for software exchange; and 116 | 117 | b) the Contributor may Distribute the Program under a license 118 | different than this Agreement, provided that such license: 119 | i) effectively disclaims on behalf of all other Contributors all 120 | warranties and conditions, express and implied, including 121 | warranties or conditions of title and non-infringement, and 122 | implied warranties or conditions of merchantability and fitness 123 | for a particular purpose; 124 | 125 | ii) effectively excludes on behalf of all other Contributors all 126 | liability for damages, including direct, indirect, special, 127 | incidental and consequential damages, such as lost profits; 128 | 129 | iii) does not attempt to limit or alter the recipients' rights 130 | in the Source Code under section 3.2; and 131 | 132 | iv) requires any subsequent distribution of the Program by any 133 | party to be under a license that satisfies the requirements 134 | of this section 3. 135 | 136 | 3.2 When the Program is Distributed as Source Code: 137 | 138 | a) it must be made available under this Agreement, or if the 139 | Program (i) is combined with other material in a separate file or 140 | files made available under a Secondary License, and (ii) the initial 141 | Contributor attached to the Source Code the notice described in 142 | Exhibit A of this Agreement, then the Program may be made available 143 | under the terms of such Secondary Licenses, and 144 | 145 | b) a copy of this Agreement must be included with each copy of 146 | the Program. 147 | 148 | 3.3 Contributors may not remove or alter any copyright, patent, 149 | trademark, attribution notices, disclaimers of warranty, or limitations 150 | of liability ("notices") contained within the Program from any copy of 151 | the Program which they Distribute, provided that Contributors may add 152 | their own appropriate notices. 153 | 154 | 4. COMMERCIAL DISTRIBUTION 155 | 156 | Commercial distributors of software may accept certain responsibilities 157 | with respect to end users, business partners and the like. While this 158 | license is intended to facilitate the commercial use of the Program, 159 | the Contributor who includes the Program in a commercial product 160 | offering should do so in a manner which does not create potential 161 | liability for other Contributors. Therefore, if a Contributor includes 162 | the Program in a commercial product offering, such Contributor 163 | ("Commercial Contributor") hereby agrees to defend and indemnify every 164 | other Contributor ("Indemnified Contributor") against any losses, 165 | damages and costs (collectively "Losses") arising from claims, lawsuits 166 | and other legal actions brought by a third party against the Indemnified 167 | Contributor to the extent caused by the acts or omissions of such 168 | Commercial Contributor in connection with its distribution of the Program 169 | in a commercial product offering. The obligations in this section do not 170 | apply to any claims or Losses relating to any actual or alleged 171 | intellectual property infringement. In order to qualify, an Indemnified 172 | Contributor must: a) promptly notify the Commercial Contributor in 173 | writing of such claim, and b) allow the Commercial Contributor to control, 174 | and cooperate with the Commercial Contributor in, the defense and any 175 | related settlement negotiations. The Indemnified Contributor may 176 | participate in any such claim at its own expense. 177 | 178 | For example, a Contributor might include the Program in a commercial 179 | product offering, Product X. That Contributor is then a Commercial 180 | Contributor. If that Commercial Contributor then makes performance 181 | claims, or offers warranties related to Product X, those performance 182 | claims and warranties are such Commercial Contributor's responsibility 183 | alone. Under this section, the Commercial Contributor would have to 184 | defend claims against the other Contributors related to those performance 185 | claims and warranties, and if a court requires any other Contributor to 186 | pay any damages as a result, the Commercial Contributor must pay 187 | those damages. 188 | 189 | 5. NO WARRANTY 190 | 191 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT 192 | PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS" 193 | BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR 194 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF 195 | TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR 196 | PURPOSE. Each Recipient is solely responsible for determining the 197 | appropriateness of using and distributing the Program and assumes all 198 | risks associated with its exercise of rights under this Agreement, 199 | including but not limited to the risks and costs of program errors, 200 | compliance with applicable laws, damage to or loss of data, programs 201 | or equipment, and unavailability or interruption of operations. 202 | 203 | 6. DISCLAIMER OF LIABILITY 204 | 205 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT 206 | PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS 207 | SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 208 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST 209 | PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 210 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 211 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 212 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE 213 | POSSIBILITY OF SUCH DAMAGES. 214 | 215 | 7. GENERAL 216 | 217 | If any provision of this Agreement is invalid or unenforceable under 218 | applicable law, it shall not affect the validity or enforceability of 219 | the remainder of the terms of this Agreement, and without further 220 | action by the parties hereto, such provision shall be reformed to the 221 | minimum extent necessary to make such provision valid and enforceable. 222 | 223 | If Recipient institutes patent litigation against any entity 224 | (including a cross-claim or counterclaim in a lawsuit) alleging that the 225 | Program itself (excluding combinations of the Program with other software 226 | or hardware) infringes such Recipient's patent(s), then such Recipient's 227 | rights granted under Section 2(b) shall terminate as of the date such 228 | litigation is filed. 229 | 230 | All Recipient's rights under this Agreement shall terminate if it 231 | fails to comply with any of the material terms or conditions of this 232 | Agreement and does not cure such failure in a reasonable period of 233 | time after becoming aware of such noncompliance. If all Recipient's 234 | rights under this Agreement terminate, Recipient agrees to cease use 235 | and distribution of the Program as soon as reasonably practicable. 236 | However, Recipient's obligations under this Agreement and any licenses 237 | granted by Recipient relating to the Program shall continue and survive. 238 | 239 | Everyone is permitted to copy and distribute copies of this Agreement, 240 | but in order to avoid inconsistency the Agreement is copyrighted and 241 | may only be modified in the following manner. The Agreement Steward 242 | reserves the right to publish new versions (including revisions) of 243 | this Agreement from time to time. No one other than the Agreement 244 | Steward has the right to modify this Agreement. The Eclipse Foundation 245 | is the initial Agreement Steward. The Eclipse Foundation may assign the 246 | responsibility to serve as the Agreement Steward to a suitable separate 247 | entity. Each new version of the Agreement will be given a distinguishing 248 | version number. The Program (including Contributions) may always be 249 | Distributed subject to the version of the Agreement under which it was 250 | received. In addition, after a new version of the Agreement is published, 251 | Contributor may elect to Distribute the Program (including its 252 | Contributions) under the new version. 253 | 254 | Except as expressly stated in Sections 2(a) and 2(b) above, Recipient 255 | receives no rights or licenses to the intellectual property of any 256 | Contributor under this Agreement, whether expressly, by implication, 257 | estoppel or otherwise. All rights in the Program not expressly granted 258 | under this Agreement are reserved. Nothing in this Agreement is intended 259 | to be enforceable by any entity that is not a Contributor or Recipient. 260 | No third-party beneficiary rights are created under this Agreement. 261 | 262 | Exhibit A - Form of Secondary Licenses Notice 263 | 264 | "This Source Code may also be made available under the following 265 | Secondary Licenses when the conditions for such availability set forth 266 | in the Eclipse Public License, v. 2.0 are satisfied: {name license(s), 267 | version(s), and exceptions or additional permissions here}." 268 | 269 | Simply including a copy of this Agreement, including this Exhibit A 270 | is not sufficient to license the Source Code under Secondary Licenses. 271 | 272 | If it is not possible or desirable to put the notice in a particular 273 | file, then You may include the notice in a location (such as a LICENSE 274 | file in a relevant directory) where a recipient would be likely to 275 | look for such a notice. 276 | 277 | You may add additional accurate notices of copyright ownership. 278 | -------------------------------------------------------------------------------- /rds/README.md: -------------------------------------------------------------------------------- 1 | # jepsen.postgres.rds 2 | 3 | Jepsen tests for Postgres RDS. This is a huge pile of hacks and will probably 4 | require some aggressive sanding/debugging in your environment. I'm so sorry, 5 | you deserve better, but I'm just drowning in backlogged tasks. 6 | 7 | ## Quickstart 8 | 9 | Create an RDS cluster. This takes about 15 minutes, so we make it a separate 10 | phase from normal setup/teardown. Replace this security group with whatever 11 | security group you'd like to associate with your # cluster. This security group 12 | should let your control node (i.e. the computer where you're running this test) 13 | talk to it. 14 | 15 | ```sh 16 | lein run rds-setup --security-group sg-03948b38d0afdd49a 17 | ``` 18 | 19 | This will spit out a file to `rds.edn` which has the details of how to connect 20 | to your cluster. `lein run test` will use this file to connect to the right 21 | servers. 22 | 23 | ```sh 24 | lein run test \ 25 | --isolation repeatable-read \ 26 | --expected-consistency-model snapshot-isolation \ 27 | --concurrency 10n \ 28 | --rate 10000 29 | ``` 30 | 31 | When you're done, you can tear down all (DANGER! YES THIS MEANS ALL, 32 | INCLUDING CLUSTERS THIS TOOL DID NOT CREATE) RDS clusters and associated 33 | resources with 34 | 35 | ```sh 36 | lein run rds-teardown 37 | ``` 38 | 39 | ## License 40 | 41 | Copyright © Jepsen, LLC 42 | 43 | This program and the accompanying materials are made available under the 44 | terms of the Eclipse Public License 2.0 which is available at 45 | http://www.eclipse.org/legal/epl-2.0. 46 | 47 | This Source Code may also be made available under the following Secondary 48 | Licenses when the conditions for such availability set forth in the Eclipse 49 | Public License, v. 2.0 are satisfied: GNU General Public License as published by 50 | the Free Software Foundation, either version 2 of the License, or (at your 51 | option) any later version, with the GNU Classpath Exception which is available 52 | at https://www.gnu.org/software/classpath/license.html. 53 | -------------------------------------------------------------------------------- /rds/doc/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction to jepsen.stolon 2 | 3 | TODO: write [great documentation](http://jacobian.org/writing/what-to-write/) 4 | -------------------------------------------------------------------------------- /rds/project.clj: -------------------------------------------------------------------------------- 1 | (defproject io.jepsen/postgres.rds "0.1.1-SNAPSHOT" 2 | :description "Jepsen tests for AWS Postgres RDS." 3 | :url "https://github.com/jepsen-io/postgres" 4 | :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0" 5 | :url "https://www.eclipse.org/legal/epl-2.0/"} 6 | :dependencies [[org.clojure/clojure "1.12.0"] 7 | [io.jepsen/postgres "0.1.1" 8 | :exclusions [; Fights with aws-api 9 | org.eclipse.jetty/jetty-http 10 | org.eclipse.jetty/jetty-util 11 | org.eclipse.jetty/jetty-io 12 | ]] 13 | [io.jepsen/rds "0.1.0"]] 14 | :main jepsen.postgres.rds 15 | :jvm-opts ["-Djava.awt.headless=true" 16 | "-server"] 17 | :repl-options {:init-ns jepsen.postgres.rds}) 18 | -------------------------------------------------------------------------------- /rds/src/jepsen/postgres/rds.clj: -------------------------------------------------------------------------------- 1 | (ns jepsen.postgres.rds 2 | "Constructs tests, handles CLI arguments, etc." 3 | (:require [clojure.tools.logging :refer [info warn]] 4 | [clojure [edn :as edn] 5 | [pprint :refer [pprint]] 6 | [string :as str]] 7 | [clojure.java [io :as io]] 8 | [jepsen [cli :as cli] 9 | [checker :as checker] 10 | [db :as jdb] 11 | [generator :as gen] 12 | [os :as os] 13 | [rds :as rds] 14 | [tests :as tests] 15 | [util :as util]] 16 | [jepsen.os.debian :as debian] 17 | [jepsen.postgres.cli :as p])) 18 | 19 | (def all-nemeses 20 | "Combinations of nemeses for tests" 21 | [[]]) 22 | 23 | (def special-nemeses 24 | "A map of special nemesis names to collections of faults" 25 | {:none [] 26 | :all []}) 27 | 28 | (defn parse-nemesis-spec 29 | "Takes a comma-separated nemesis string and returns a collection of keyword 30 | faults." 31 | [spec] 32 | (->> (str/split spec #",") 33 | (map keyword) 34 | (mapcat #(get special-nemeses % [%])))) 35 | 36 | (def rds-file 37 | "Where do we store RDS cluster info?" 38 | "rds.edn") 39 | 40 | (defn postgres-rds-test 41 | "Given an options map from the command line runner (e.g. :nodes, :ssh, 42 | :concurrency, ...), constructs a test map." 43 | [opts] 44 | (let [r (edn/read-string (slurp rds-file)) 45 | _ (assert r "No rds.edn found; try running `lein run rds-create`.") 46 | opts (assoc opts 47 | ; We're providing postgres, which disables the OS, DB, and 48 | ; nemesis 49 | :existing-postgres true 50 | ; SSL is mandatory 51 | :postgres-sslmode "require" 52 | ; Nodes are drawn from primary and reader endpoints 53 | :nodes [(:endpoint r) 54 | (:reader-endpoint r)] 55 | ; Credentials 56 | :postgres-port (:port r) 57 | :postgres-user (:master-username r) 58 | :postgres-password (:master-user-password r))] 59 | (p/postgres-test opts))) 60 | 61 | (def cli-opts 62 | "Additional CLI options. These are drawn mainly from 63 | jepsen.postgres.cli/opts." 64 | [["-i" "--isolation LEVEL" "What level of isolation we should set: serializable, repeatable-read, etc." 65 | :default :serializable 66 | :parse-fn keyword 67 | :validate [#{:read-uncommitted 68 | :read-committed 69 | :repeatable-read 70 | :serializable} 71 | "Should be one of read-uncommitted, read-committed, repeatable-read, or serializable"]] 72 | 73 | [nil "--expected-consistency-model MODEL" "What level of isolation do we *expect* to observe? Defaults to the same as --isolation." 74 | :default nil 75 | :parse-fn keyword] 76 | 77 | [nil "--key-count NUM" "Number of keys in active rotation." 78 | :default 10 79 | :parse-fn parse-long 80 | :validate [pos? "Must be a positive integer"]] 81 | 82 | [nil "--nemesis FAULTS" "A comma-separated list of nemesis faults to enable" 83 | :parse-fn parse-nemesis-spec 84 | :validate [(partial every? #{}) 85 | "Faults are not supported yet."]] 86 | 87 | [nil "--max-txn-length NUM" "Maximum number of operations in a transaction." 88 | :default 4 89 | :parse-fn parse-long 90 | :validate [pos? "Must be a positive integer"]] 91 | 92 | [nil "--max-writes-per-key NUM" "Maximum number of writes to any given key." 93 | :default 64 94 | :parse-fn parse-long 95 | :validate [pos? "Must be a positive integer."]] 96 | 97 | [nil "--nemesis-interval SECS" "Roughly how long between nemesis operations." 98 | :default 5 99 | :parse-fn read-string 100 | :validate [pos? "Must be a positive number."]] 101 | 102 | [nil "--on-conflict" "If set, uses an ON CONFLICT clause to upsert rows."] 103 | 104 | [nil "--prepare-threshold INT" "Passes a prepareThreshold option to the JDBC spec." 105 | :parse-fn parse-long] 106 | 107 | ["-r" "--rate HZ" "Approximate request rate, in hz" 108 | :default 100 109 | :parse-fn read-string 110 | :validate [pos? "Must be a positive number."]] 111 | 112 | ["-w" "--workload NAME" "What workload should we run?" 113 | :default :append 114 | :parse-fn keyword 115 | :validate [p/workloads (cli/one-of p/workloads)]] 116 | ]) 117 | 118 | (defn all-tests 119 | "Turns CLI options into a sequence of tests." 120 | [test-fn cli] 121 | (let [nemeses (if-let [n (:nemesis cli)] [n] all-nemeses) 122 | workloads (if-let [w (:workload cli)] [w] 123 | (if (:only-workloads-expected-to-pass cli) 124 | p/workloads-expected-to-pass 125 | p/all-workloads))] 126 | (for [n nemeses, w workloads, i (range (:test-count cli))] 127 | (test-fn (assoc cli 128 | :nemesis n 129 | :workload w))))) 130 | 131 | (def rds-setup-cmd 132 | "A command which creates an RDS cluster." 133 | {:opt-spec 134 | [["-s" "--security-group ID" "The ID of a security group you'd like to associate with this cluster."] 135 | ["-v" "--version VERSION" "What version of Postgres should we request?" 136 | :default "17.4"]] 137 | :usage "Creates a fresh RDS cluster, writing its details to ./rds.edn" 138 | :opt-fn identity 139 | :run (fn run [{:keys [options]}] 140 | (let [c (rds/create-postgres! 141 | {:engine-version (:version options) 142 | :security-group-id (:security-group options)})] 143 | (pprint c) 144 | (spit rds-file (with-out-str (pprint c))) 145 | (info "RDS cluster ready.")))}) 146 | 147 | (def rds-teardown-cmd 148 | "A command which tears down all RDS clusters." 149 | {:opt-spec [] 150 | :usage "Tears down all RDS clusters. Yes, all. ALL. DANGER." 151 | :opt-fn identity 152 | :run (fn run [{:keys [options]}] 153 | (.delete (io/file rds-file)) 154 | (rds/teardown!))}) 155 | 156 | (defn -main 157 | "Handles command line arguments. Can either run a test, or a web server for 158 | browsing results." 159 | [& args] 160 | (cli/run! (merge (cli/single-test-cmd {:test-fn postgres-rds-test 161 | :opt-spec cli-opts 162 | :opt-fn p/opt-fn}) 163 | (cli/test-all-cmd {:tests-fn (partial all-tests 164 | postgres-rds-test) 165 | :opt-spec cli-opts 166 | :opt-fn p/opt-fn}) 167 | (cli/serve-cmd) 168 | {"rds-setup" rds-setup-cmd 169 | "rds-teardown" rds-teardown-cmd}) 170 | args)) 171 | --------------------------------------------------------------------------------