<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mehdi Pourfar</title>
    <description>The latest articles on DEV Community by Mehdi Pourfar (@mehdipourfar).</description>
    <link>https://dev.to/mehdipourfar</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F202499%2F4abd6087-b23e-4f0d-85db-ea7bb578d863.jpeg</url>
      <title>DEV Community: Mehdi Pourfar</title>
      <link>https://dev.to/mehdipourfar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mehdipourfar"/>
    <language>en</language>
    <item>
      <title>Easily use multiple proxies with PAC rules</title>
      <dc:creator>Mehdi Pourfar</dc:creator>
      <pubDate>Sun, 27 Sep 2020 16:54:40 +0000</pubDate>
      <link>https://dev.to/mehdipourfar/easily-use-multiple-proxies-with-pac-rules-3e4b</link>
      <guid>https://dev.to/mehdipourfar/easily-use-multiple-proxies-with-pac-rules-3e4b</guid>
      <description>&lt;p&gt;I use proxies to view the contents of the websites that are blocked either for censorship reasons or by sanctions.&lt;/p&gt;

&lt;p&gt;But using proxies all the time makes working with websites that are not blocked a little bit slower. There are also some internal websites (like for example.. bank websites) which cannot be opened with any proxy. And also for my own job, I need an ssh tunnel connection to open internal urls.&lt;/p&gt;

&lt;p&gt;It can be really tiresome to switch between these proxies manually. After some research, I found a solution for this kind of situations: Setting up a PAC (Proxy auto-config) file.&lt;/p&gt;

&lt;p&gt;PAC is a javascript file consisting of a function named &lt;code&gt;FindProxyForUrl&lt;/code&gt; with &lt;code&gt;.pac&lt;/code&gt; extension&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;FindProxyForURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;host&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;host&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DIRECT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;host&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;b.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SOCKS5 127.0.0.1:1081&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SOCKS5 127.0.0.1:1080&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;When you configure your proxy setting to use a PAC file, each time you open an address, your browser checks the url with this function to find out which proxy to use. But writing a function like this and changing it frequently can be complex and error prone.  &lt;/p&gt;

&lt;p&gt;So I decided to create a command line tool named &lt;a href="https://github.com/mehdipourfar/pacgen"&gt;PacGen&lt;/a&gt; to create this file. Here I want to show you how to work with it.&lt;/p&gt;

&lt;p&gt;First we install it using pip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;pacgen
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then we define our rules in &lt;code&gt;.yml&lt;/code&gt; format in a file at &lt;code&gt;~/.pacgen.yml&lt;/code&gt;. This file consists of four segements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;proxies&lt;/code&gt; which defines our proxy servers with a name assigned to each of them.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;routes&lt;/code&gt; which is a list of predefined routes for some specific hosts.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;excludes&lt;/code&gt; which is list of the hosts that we want to browse without any proxy.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;default_proxy&lt;/code&gt; which is the name of the default proxy for the hosts that are neither in &lt;code&gt;routes&lt;/code&gt; nor in &lt;code&gt;excludes&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is an example of &lt;code&gt;~/.pacgen.yml&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;proxies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ssh_tunnel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;socks5://127.0.0.1:1081&lt;/span&gt;
  &lt;span class="na"&gt;shadowsocks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;socks5://127.0.0.1:1080&lt;/span&gt;
  &lt;span class="na"&gt;httpproxy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://127.0.0.1:1082&lt;/span&gt;
&lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;172.19.20.10&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ssh_tunnel&lt;/span&gt;
  &lt;span class="s"&gt;youtube.com&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;shadowsocks&lt;/span&gt;
  &lt;span class="s"&gt;viemo.com&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;shadowsocks&lt;/span&gt;
  &lt;span class="s"&gt;news.com&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;httpproxy&lt;/span&gt;
  &lt;span class="s"&gt;analytics.google.com&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;shadowsocks&lt;/span&gt;
&lt;span class="na"&gt;default_proxy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;shadowsocks&lt;/span&gt;
&lt;span class="na"&gt;excludes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bank.com&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;google.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;After you defined your rules and saved the file, you should run the command below in your terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;updatepac
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then it will create your PAC file in this path: &lt;code&gt;~/.proxy.pac&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now that we have created our pac files, it's time to serve it with a webserver. The most straightforward way to do it is using nginx:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt install nginx
sudo cp ~/.proxy.pac /var/www/html/proxy.pac
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Your file will be served at &lt;code&gt;http://localhost/proxy.pac&lt;/code&gt;.&lt;br&gt;
Now go to your system proxy setting and choose automatic proxy option and copy the address above into &lt;code&gt;Configuration URL&lt;/code&gt; field. That's all.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>proxy</category>
      <category>pac</category>
    </item>
    <item>
      <title>Backup and replicate Postgresql 12 using pgBackRest in Ubuntu 20.04</title>
      <dc:creator>Mehdi Pourfar</dc:creator>
      <pubDate>Sun, 27 Sep 2020 12:40:01 +0000</pubDate>
      <link>https://dev.to/mehdipourfar/backup-and-replicate-postgresql-12-using-pgbackrest-in-ubuntu-20-04-5c4e</link>
      <guid>https://dev.to/mehdipourfar/backup-and-replicate-postgresql-12-using-pgbackrest-in-ubuntu-20-04-5c4e</guid>
      <description>&lt;p&gt;In this post, I will guide you through the process of setting up a continuous backup system and a replication server using pgBackRest.&lt;/p&gt;

&lt;p&gt;For this guide, we need to have 3 servers: &lt;code&gt;pg-master&lt;/code&gt;, &lt;code&gt;pg-replica&lt;/code&gt; and &lt;code&gt;pg-backup&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;On all of these servers, we need to add these lines to &lt;code&gt;/etc/hosts&lt;/code&gt; file and put the correct ips there:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{master_server_ip} pg-master
{replica_server_ip} pg-replica
{backup_server_ip} pg-backup
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;On &lt;code&gt;pg-master&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt install postgresql-12 pgbackrest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;On &lt;code&gt;pg-replica&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt install postgresql-12 pgbackrest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;On &lt;code&gt;pg-backup&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt install pgbackrest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Since our servers should communicate with each other using ssh connection, we create ssh key pairs on each of them:&lt;/p&gt;

&lt;p&gt;On &lt;code&gt;pg-master&lt;/code&gt;, &lt;code&gt;pg-replica&lt;/code&gt; and &lt;code&gt;pg-backup&lt;/code&gt; run these commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo su postgres
ssh-keygen -t rsa -b 4096 -N ""
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And then share all of the public keys inside &lt;code&gt;/var/lib/postgresql/.ssh/authorized_keys&lt;/code&gt; file on each server. Then we need to check that all connections can be established without any problem:&lt;/p&gt;

&lt;p&gt;On &lt;code&gt;pg-master&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo -u postgres ssh postgres@pg-backup
[exit]
sudo -u postgres ssh postgres@pg-replica
[exit]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;On &lt;code&gt;pg-replica&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo -u postgres ssh postgres@pg-backup
[exit]
sudo -u postgres ssh postgres@pg-master
[exit]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;On &lt;code&gt;pg-backup&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo -u postgres ssh postgres@pg-master
[exit]
sudo -u postgres ssh postgres@pg-replica
[exit]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If everything works correctly, it's time to configure pgBackRest.&lt;/p&gt;

&lt;p&gt;On &lt;code&gt;pg-master&lt;/code&gt;, change the content of &lt;code&gt;/etc/pgbackrest.conf&lt;/code&gt; with these lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[my_cluster]
pg1-path=/var/lib/postgresql/12/main

[global]
repo1-host=pg-backup
repo1-host-user=postgres

[global:archive-push]
compress-level=3
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And then, open &lt;code&gt;/etc/postgresql/12/main/postgresql.conf&lt;/code&gt; and change these variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;archive_command = 'pgbackrest --stanza=my_cluster archive-push %p'
archive_mode = on
listen_addresses = '*'
max_wal_senders = 3
wal_level = replica
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And then restart postgresql:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl restart postgresql
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;After that, we will create some test data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo -u postgres psql

psql (12.4 (Ubuntu 12.4-1.pgdg18.04+1))
Type "help" for help.

postgres=# CREATE DATABASE my_db;
CREATE DATABASE
postgres=# \c my_db
You are now connected to database "my_db" as user "postgres".
my_db=# CREATE TABLE nums(val int);
CREATE TABLE
my_db=# INSERT INTO nums VALUES(1);
INSERT 0 1
my_db=# INSERT INTO nums VALUES(2);
INSERT 0 1
my_db=# INSERT INTO nums VALUES(3);
INSERT 0 1

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we have a database named &lt;code&gt;my_db&lt;/code&gt; with a table named &lt;code&gt;nums&lt;/code&gt; with three values.&lt;/p&gt;

&lt;p&gt;It's time to create our backup on &lt;code&gt;pg-backup&lt;/code&gt; server. First edit &lt;code&gt;/etc/pgbackrest.conf&lt;/code&gt; on &lt;code&gt;pg-backup&lt;/code&gt; as below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[my_cluster]
pg1-host=pg-master
pg1-host-user=postgres
pg1-path=/var/lib/postgresql/12/main                                                                                                               

[global]                                                                                                                                  
process-max=2
repo1-path=/var/lib/pgbackrest
repo1-retention-full=2
repo1-retention-diff=1
repo1-host-user=postgres
start-fast=y
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then run this command to create a backup stanza on &lt;code&gt;pg-backup&lt;/code&gt; server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo -u postgres pgbackrest --stanza=my_cluster --log-level-console=info stanza-create
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then, run this command on both &lt;code&gt;pg-master&lt;/code&gt; and &lt;code&gt;pg-backup&lt;/code&gt; to check everything is okay:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo -u postgres pgbackrest --stanza=my_cluster --log-level-console=info check
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If everything works good, it's time to perform our first backup on &lt;code&gt;pg-backup&lt;/code&gt; server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo -u postgres pgbackrest --stanza=my_cluster --log-level-console=info backup
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;By default, pgBackRest works by copying new archive files to backup server but if we let it be like that, the hard disk will be full very soon. So we need to create new backups based on some routines and pgBackRest will remove old backups for us based on retention policies that we have defined above. To do this, we will use Cronjob.&lt;/p&gt;

&lt;p&gt;On &lt;code&gt;pg-backup&lt;/code&gt; server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo -u postgres
crontab -e
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And add these lines at the end of the file and press enter to create a new line. Then save and exit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;30 06  *   *   0     pgbackrest --type=full --stanza=my_cluster backup
30 06  *   *   1-6   pgbackrest --type=diff --stanza=my_cluster backup
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can read more about full backup, diff backup and retention policies in pgBackRest documentation.&lt;/p&gt;

&lt;p&gt;Now that our backup server has been configured correctly, it's time to configure our replication server. Since we want to have a streaming replication, we should first create a replication user on &lt;code&gt;pg-master&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo -u postgres psql
CREATE USER replicator PASSWORD '123123' REPLICATION";
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;On &lt;code&gt;pg-master&lt;/code&gt;, open &lt;code&gt;/etc/postgresql/12/main/pg_hba.conf&lt;/code&gt; and add this line and change &lt;code&gt;pg_replicate_ip&lt;/code&gt; to the ip of &lt;code&gt;pg-replica&lt;/code&gt; server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;host    replication     replicator      {pg_replicate_ip}/32       md5
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And then restart postgresql&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;systemctl restart postgresql
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, on &lt;code&gt;pg-replica&lt;/code&gt; open the file &lt;code&gt;/etc/pgbackrest.conf&lt;/code&gt; and change the content with these lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[my_cluster]
pg1-path=/var/lib/postgresql/12/main
recovery-option=primary_conninfo=host=pg-master port=5432 user=replicator password=123123 application_name=replica1

[global]
repo1-host=pg-backup
repo1-host-user=postgres
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Don't worry if you don't know what &lt;code&gt;application_name&lt;/code&gt; is. We will talk about it later.&lt;br&gt;
On &lt;code&gt;pg-replica&lt;/code&gt;, run these commands to fetch master database data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;systemctl stop postgresql
sudo -u postgres pgbackrest --stanza=my_cluster --delta --type=standby --log-level-console=info restore
systemctl start postgresql
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To check that everything works correctly, on &lt;code&gt;pg-replica&lt;/code&gt; go to psql and check that &lt;code&gt;my_db&lt;/code&gt; database exists:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo -u postgres psql -d my_db
SELCET * FROM nums;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You should see these values as result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt; val 
-----
   1
   2
   3
(3 rows)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, we add some data on &lt;code&gt;pg-master&lt;/code&gt; to check changes will be reflected on replica:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo -u postgres psql -d my_db
INSERT INTO nums VALUES (4);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;On, &lt;code&gt;pg-replica&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT * FROM nums;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now you should see these values as result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt; val 
-----
   1
   2
   3
   4
(3 rows)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you don't see 4 values, look at the &lt;code&gt;pg-replica&lt;/code&gt;'s log file to find out the problem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tail /var/log/postgresql/postgresql-12-main.log
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As you saw earlier, we have set application_name=replica1 in our connection info, but what is that? By default, Postgresql will use asynchronous replication, meaning that, replication will be done with a lag (usually just a few milliseconds) but if you want to have synchronous replication (meaning that commits will be blocked until replication is done) then you can set this application_name in &lt;code&gt;pg-master&lt;/code&gt;'s postgresql.conf.&lt;br&gt;
If you want to do this, on &lt;code&gt;pg-master&lt;/code&gt;, open the file &lt;code&gt;/ect/postgresql/12/main/postgresql.conf&lt;/code&gt; and change this value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;synchronous_standby_names = 'replica1'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And then restart postgresql. Now the replication should be synchronous. To test it, on &lt;code&gt;pg-master&lt;/code&gt; go to sql shell and add a new row:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo -u postgres psql -d my_db
INSERT INTO nums VALUES(5);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If it doesn't hang, that means everything is okay. Buy if you stop postgresql replica server, command will hang and client will be blocked.&lt;/p&gt;

&lt;p&gt;On &lt;code&gt;pg-replica&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;systemctl stop postgresql
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;On &lt;code&gt;pg-master&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INSERT INTO nums VALUES(6);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now as you see, &lt;code&gt;pg-master&lt;/code&gt;'s psql client is hanged but as soon as you start &lt;code&gt;pg-replica&lt;/code&gt; postgresql server, it will be okay.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;systemctl start postgresql
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now it's time to test recovery process. For the test purpose, we truncate &lt;code&gt;nums&lt;/code&gt; table and recover the data.&lt;br&gt;
On &lt;code&gt;pg-master&lt;/code&gt;, run the command below and copy the time value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo -u postgres psql -Atc  "select current_timestamp"
2020-09-24 14:17:23.105194+03:30
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Open psql and truncate nums table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo -u postgres psql -d my_db
TRUNCATE nums;
SELECT * FROM nums;
 val 
-----
(0 rows)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Stop Postgresql servers on &lt;code&gt;pg-master&lt;/code&gt; and &lt;code&gt;pg-replica&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;systemctl stop postgresql
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;On &lt;code&gt;pg-master&lt;/code&gt; run the command below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo -u postgres pgbackrest --stanza=my_cluster --delta --type=time "--target=2020-09-24 14:17:23.105194+03:30" --target-action=promote restore
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And then start postgresql server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;systemctl start postgresql
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Check the nums table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo -u postgres psql -d my_db

 val 
-----
   1
   2
   3
   4
   5
   6
(6 rows)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As you can see, we have restored our truncated table on &lt;code&gt;pg-master&lt;/code&gt;. Now we need to restore it on &lt;code&gt;pg-replica&lt;/code&gt;. First, go to &lt;code&gt;pg-backup&lt;/code&gt; server and get a new backup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo -u postgres pgbackrest --stanza=my_cluster --log-level-console=info backup
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then, on &lt;code&gt;pg-replica&lt;/code&gt; server, restore the data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo -u postgres pgbackrest --stanza=my_cluster --delta --type=standby restore
systemctl start postgresql
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now for testing that everything works correctly add a value to &lt;code&gt;nums&lt;/code&gt; table in &lt;code&gt;pg-master&lt;/code&gt; and check that it will reflect on &lt;code&gt;pg-replica&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That's all.&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>pgbackrest</category>
      <category>backup</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Faster CSV export with Django &amp; Postgres</title>
      <dc:creator>Mehdi Pourfar</dc:creator>
      <pubDate>Mon, 07 Oct 2019 11:17:19 +0000</pubDate>
      <link>https://dev.to/mehdipourfar/faster-csv-export-with-django-postgres-5bi5</link>
      <guid>https://dev.to/mehdipourfar/faster-csv-export-with-django-postgres-5bi5</guid>
      <description>&lt;p&gt;As a Django developer, there are many times that you are asked for CSV export of your database tables.&lt;/p&gt;

&lt;p&gt;To do this, Python’s CSV module is a simple library to use. But when your dataset is large, it becomes so inefficient that often leads to timeout error. There are two problem with this method:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Python is not very fast.&lt;/li&gt;
&lt;li&gt;Generating model objects by Django Orm is very resource consuming.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As an alternative approach, I suggest using Postgres CSV functions. This way, not only we don’t need to create thousands of model objects, but also we rapidly generate CSV thanks to Postgres speed.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;But do I need to write complex SQL queries? &lt;br&gt;
What should I do if I currently have a filtered queryset, for example, in Django admin?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Don’t worry. There is no need to write SQL. Here, we write a function that accepts a queryset and a filename and returns a CSV response&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from django.db import connection
from django.utils import timezone
from django.http import HttpResponse

def qs_to_csv_response(qs, filename):
    sql, params = qs.query.sql_with_params()
    sql = f"COPY ({sql}) TO STDOUT WITH (FORMAT CSV, HEADER, DELIMITER E'\t')"
    filename = f'{filename}-{timezone.now():%Y-%m-%d_%H-%M-%S}.csv'
    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = f'attachment; filename={filename}'
    with connection.cursor() as cur:
        sql = cur.mogrify(sql, params)
        cur.copy_expert(sql, response)
    return response
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Suppose we have these models:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from django.db import models
from django.utils.translation import ugettext_lazy as _

class City(models.Model):
    name = models.CharField(max_length=50)

class Place(models.Model):
    PLACE_TYPE_CHOICES = (
        (1, _('Park')),
        (2, _('Cafe')),
        (3, _('Resturant')),
        (4, _('Cinema')),
    )
    name = models.CharField(max_length=50)
    city = models.ForeignKey(City, on_delete=models.CASCADE)
    place_type = models.PositiveSmallIntegerField(
        choices=PLACE_TYPE_CHOICES
    )
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If we want to have a CSV export from Place table containing &lt;em&gt;place_id, place_name, place_type and city_name,&lt;/em&gt; we pass this query to the function above:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from django.db.models import F

Place.objects.values(
  'id',
  'name',
  'place_type',
  city_name=F('city__name')
)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There is some caveat here. Postgres doesn’t know anything about our pretty, human readable and translated place types and putting some numbers in &lt;em&gt;place_type&lt;/em&gt; columns can be completely useless.&lt;/p&gt;

&lt;p&gt;The function below can be used to tell Postgres about this kind of mappings using Case and When:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from django.db.models import Case, When, Value, CharField

def map_choices(field_name, choices):
    return Case(
        *[When(**{field_name: value, 'then': Value(str(representation))})
          for value, representation in choices],
        output_field=CharField()
    )
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then, we use it in the query:&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Place.objects.values(
  'id',
  'name',
  verbose_type=map_choices('place_type', Place.PLACE_TYPE_CHOICES),
  city_name=F('city__name')
)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;By using Django ORM tools like F, Func, ExpressionWrapper, RawSQL … you can easily write more complex queries in a performant way.&lt;/p&gt;

</description>
      <category>django</category>
      <category>postgres</category>
      <category>csv</category>
      <category>orm</category>
    </item>
  </channel>
</rss>
