<!--  -->
<sect1 id="modifyingthings"> <title>Modifying Things in a Replication Cluster</title>
<sect2>
<title>Adding a Table To Replication</title>
<indexterm><primary>adding objects to replication</primary></indexterm>
<indexterm><primary> adding a table to replication </primary></indexterm>

<para>

After your &slony1; cluster is setup and nodes are subscribed to your
replication set you can still add more tables to replication.  
To do this you must first create the table on each node. You can
do this using psql (on each node) or using the <xref linkend="stmtddlscript"> 
command. Next, you should create a new replication set and add the table
(or sequence) to the new replication set.  Then you subscribe your
subscribers to the new replictation set.  Once the subscription 
process is finished you can merge your new replication set into
the old one.
</para>

<programlisting>
slonik <<_EOF_
	#--
	# define the namespace the replication system uses in our example it is
	# slony_example
	#--
	cluster name = $CLUSTERNAME;

	#--
	# admin conninfo's are used by slonik to connect to the nodes one for each
	# node on each side of the cluster, the syntax is that of PQconnectdb in
	# the C-API
	# --
	node 1 admin conninfo = 'dbname=$MASTERDBNAME host=$MASTERHOST user=$REPLICATIONUSER';
	node 2 admin conninfo = 'dbname=$SLAVEDBNAME host=$SLAVEHOST user=$REPLICATIONUSER';


        create set (id=2, origin=1, comment='a second replication set');
        set add table (set id=2, origin=1, id=5, fully qualified name = 'public.newtable', comment='some new table');
        subscribe set(id=2, provider=1,receiver=2);
        merge set(id=1, add id=2,origin=1);

</programlisting>

</sect2>


<sect2><title> How To Add Columns To a Replicated Table </title>

<indexterm><primary> adding columns to a replicated table </primary></indexterm>

<para> There are two approaches you can use for adding (or renaming) columns
to an existing replicated table.  </para>

<para> The first approach involves you using the 
<xref linkend="stmtddlscript"> command.  With this approach you would
<orderedlist>
<listitem><para> Create a SQL script with your ALTER table statements</para>
</listitem>

<listitem><para> Stop any application updates to the table you are changing
(ie have an outage)</para></listitem>
<listitem><para> Use the slonik <xref linkend="stmtddlscript"> command to
run your script</para></listitem>
</orderedlist>

Your table should now be updated on all databases in the cluster.

</para>

<para> Alternatively, if you have the <link linkend="altperl"> altperl
scripts </link> installed, you may use
<command>slonik_execute_script</command> for this purpose: </para>

<para> <command> slonik_execute_script [options] set#
full_path_to_sql_script_file </command></para>

<para> See <command>slonik_execute_script -h</command> for further
options; note that this uses <xref linkend="stmtddlscript">
underneath. </para>

<para> There are a number of <quote>sharp edges</quote> to note...</para>

<itemizedlist>
<listitem><para> You absolutely <emphasis>must not</emphasis> include
transaction control commands, particularly <command>BEGIN</command>
and <command>COMMIT</command>, inside these DDL scripts. &slony1;
wraps DDL scripts with a <command>BEGIN</command>/<command>COMMIT</command> 
pair; adding extra transaction control will mean that parts of the DDL
will commit outside the control of &slony1; </para></listitem>


<listitem><para>Version 2.0 of &slony1; does not explicitly lock
tables when performing an execute script.  To avoid some race-conditions
exposed by MVCC it is important that no other transactions are altering
the tables being used by the ddl script while it is running</para></listitem>

</itemizedlist>
</sect2>

<sect2><title> How to remove replication for a node</title>

<indexterm><primary>removing replication for a node</primary></indexterm>

<para> You will want to remove the various &slony1; components
connected to the database(s).</para>

<para> We will just consider, for now, doing this to one node. If you
have multiple nodes, you will have to repeat this as many times as
necessary.</para>

<para> Components to be Removed: </para>
<itemizedlist>

<listitem><para> Log Triggers / Update Denial Triggers

</para></listitem>
<listitem><para> The <quote>cluster</quote> schema containing &slony1;
tables indicating the state of the node as well as various stored
functions
</para></listitem>
<listitem><para> &lslon; process that manages the node </para></listitem>
<listitem><para> Optionally, the SQL and pl/pgsql scripts and &slony1;
binaries that are part of the &postgres; build. (Of course, this would
make it challenging to restart replication; it is unlikely that you
truly need to do this...)
</para></listitem>
</itemizedlist>

<para>
The second approach involves using psql to alter the table directly on each 
database in the cluster.
<orderedlist>
<listitem> <para> Stop any application updates to the table you are
changing(ie have on outage)</para></listitem>
<listitem><para>Connect to each database in the cluster (in turn) and
make the required changes to the table</para></listitem>
</orderedlist>

<warning><para> The psql approach is only safe with &slony1; 2.0 or greater
</para>
</warning>
</para>

<indexterm><primary>adding a node</primary></indexterm>

<para>Things are not fundamentally different whether you are adding a
brand new, fresh node, or if you had previously dropped a node and are
recreating it. In either case, you are adding a node to
replication. </para>

<sect2>
<title>Adding a Replication Node</title>
<indexterm><primary>adding a node to replication</primary></indexterm>

<para>
To add a node to the replication cluster you should
</para>

<orderedlist>
<listitem><para>Create a database for the node and install your application 
schema in it.
<programlisting>
createdb -h $NEWSLAVE_HOST $SLAVEDB
pg_dump -h $MASTER_HOST -s $MASTERDB | psql -h $NEWSLAVE_HOST $SLAVEDB
</programlisting>
</para>
</listitem>
<listitem><para>Create the node with the <xref linkend="stmtstorenode"> command
<programlisting>
cluster name=testcluster;
node 5 admin conninfo='host=slavehost dbname=slavedb user=slony password=slony';

store node(id=5,comment='some slave node',event node=1);
</programlisting>

</para></listitem>
<listitem><para>Create paths between the new node and its provider node with
the <xref linkend="stmtstorepath"> command.
<programlisting>
cluster name=testcluster;
node 5 admin conninfo='host=slavehost dbname=slavedb user=slony password=slony';
node 1 admin conninfo='host=masterhost dbname=masterdb user=slony password=slony';
# also include the admin conninfo lines for any other nodes in your cluster.
#
#
cluster name=testcluster;
store path(server=1,client=5,conninfo='host=masterhost,dbname=masterdb,user=slony,password=slony');
store path(server=5,client=1,conninfo='host=slavehost,dbname=masterdb,user=slony,password=slony');
</programlisting>

</para></listitem>

<listitem><para>Subscribe the new node to the relevant replication sets
<programlisting>
cluster name=testcluster;
node 5 admin conninfo='host=slavehost dbname=slavedb user=slony password=slony';
node 1 admin conninfo='host=masterhost dbname=slavedb user=slony password=slony';
#
# also include the admin conninfo lines for any other nodes in the cluster
#
#
cluster name=testcluster;
subscribe set(id=1,provider=1, receiver=5,forward=yes);
</programlisting>

</orderedlist>


<sect2>
<title>Adding a Cascaded Replica</title>
<para>
In a standard &slony1; configuration all slaves(replicas) communicate directly 
with the master (origin).  Sometimes it is more desirable to have some of
your slaves(replicas) feed off of another replica.  This is called a 
cascaded replica and is supported by &slony1;

For example you might have a &slony1; cluster with 1 replication set (set id=1)
and three nodes.  The master (origin) for set 1 (node id=1), a node in a 
different data center that reads 
directly from the master (node id=2) and a third node in the same data center
as the slave (node id=3).

To the subscription sets in this configuration you need to make sure that
paths exist between nodes 2 and nodes 3.  Then to perform the subscription
you could use the following slonik commands.

</para>
<programlisting>
#Setup path between node 1==>2
store path(server=1,client=2,conninfo='host=masterhost,dbname=masterdb,user=slony,password=slony');
store path(server=2,client=1,conninfo='host=slave2host,dbname=slave2db,user=slony,password=slony');

#Setup path between node 2==>3
store path(server=3,client=2,conninfo='host=slave3host,dbname=slave3db,user=slony,password=slony');
store path(server=2,client=3,conninfo='host=slave2host,dbname=slave2db,user=slony,password=slony');

subscribe set(set id=1, provider=1, receiver=2,forward=yes);
subscribe set (set id=1,provider=2, receiver=3,forward=yes);
wait for event(origin=1, confirmed=all, wait on=1);
</programlisting>
<para>
In the above example we define paths from 1==>2 and from 2==>3 but do
not define a path between nodes 1===>3.  If a path between node 1 and 3
was defined the data data for set 1 would still flow through node
2 because node 2 is the origin for set 1.   However if node 2 were to fail
nodes 1 and 3 would be unable to talk to each other unless a path between
nodes 1 and nodes 3 had been defined.
</para>

<para>
&slony1; requires that all sets from a given origin cascade to each receiver
in the same manner.  If node 1 was also the origin for set 2 then set 2
would also need to go directly to node 2 and be cascaded from node 2 to node 3.
Node 3 can not receive set 2 from node 1 directly if it is already receiving
set 1 via node 2.
</para>

</sect2>

<sect2><title> How do I use <link linkend="logshipping">Log Shipping?</link> </title> 

<para> Discussed in the <link linkend="logshipping"> Log Shipping </link> section... </para>

<sect2><title> How To Remove Replication For a Node</title>

<para> You will want to remove the various &slony1; components
connected to the database(s).</para>

<para> We will just discuss doing this to one node. If you
have multiple nodes, you will have to repeat this as many times as
necessary.</para>

<para>
Removing slony from a node involves deleting the slony schema (tables,
functions and triggers) from the node in question and telling the
remaining nodes that the deleted node no longer exists.  The slony
<xref linkend="stmtdropnode"> command does both of these items
while hte <xref linkend="stmtuninstallnode"> command only removes
the slony schema from the node.
</para>
<para> In the case of a failed node (where you
used <xref linkend="stmtfailover"> to switch to another node), you may
need to use <xref linkend="stmtuninstallnode"> to drop out the
triggers and schema and functions.</para>

<warning><para>Removing &slony1; from a replica in versions before
2.0 is more complicated. If this applies to you then you should
consult the &slony1; documentation for the version of &slony1; you
are using.
</para>
</warning>

</sect2>


<sect2><title>Changing a Nodes Provider</title>


<indexterm><primary>reshaping subscriptions</primary></indexterm>

<para> For instance, you might want subscriber node 3 to draw data from node
4 (for all sets originating on node 1), when it is presently drawing data from node 2. </para>

<para>The  <xref
linkend="stmtresubscribenode"> command can be used to do this.
For existing subscriptions it can revise the subscription information.

<programlisting>
resubscribe node(origin=1, provider=4,receiver=3);
</programlisting>


</sect2>




<sect2><title>Moving The Master From One Node To Another</title> 
<para>
Sometimes you will want to promote one of your replicas (slaves) to 
become the master and at the same time turn the former master into
a slave.  &slony1; supports this with the <xref linkend="stmtmoveset">
command.
</para>

<indexterm><primary>moving master</primary></indexterm>

<para> You must first pick a node that is connected to the former
origin (otherwise it is not straightforward to reverse connections in
the move to keep everything connected). </para>

<para> Second, you must run a &lslonik; script with the
command <xref linkend="stmtlockset"> to lock the set on the origin
node.  Note that at this point you have an application outage under
way, as what this does is to put triggers on the origin that rejects
updates. </para>

<para> Now, submit the &lslonik; <xref linkend="stmtmoveset"> request.
It's perfectly reasonable to submit both requests in the same
&lslonik; script.  Now, the origin gets switched over to the new
origin node.  If the new node is a few events behind, it may take a
little while for this to take place. </para> 

<programlisting>
LOCK SET(id=1,ORIGIN=1);
MOVE SET(ID=1,OLD ORIGIN=1, NEW ORIGIN=3);
SYNC(ID=3);
WAIT FOR(ORIGIN=1, CONFIRMED=ALL,WAIT ON=1);
</programlisting>

<para>
It is important to stop all non-Slony application activity against all tables
in the replication set before locking the sets.  The move set procedure
involves obtaining a lock on every table in the replication set.  Other
activities on these tables can result in a deadlock.
</para>

</sect2>

<!-- Keep this comment at the end of the file
Local variables:
mode:sgml
sgml-omittag:nil
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
sgml-parent-document:"slony.sgml"
sgml-exposed-tags:nil
sgml-local-catalogs:("/usr/lib/sgml/catalog")
sgml-local-ecat-files:nil
End:
-->
