<!-- $Id: defineset.sgml,v 1.18 2005/06/22 16:56:59 cbbrowne Exp $ -->
<sect1 id="definingsets">
<title>Defining &slony1; Replication Sets</title>

<para>Defining the nodes indicated the shape of the cluster of
database servers; it is now time to determine what data is to be
copied between them.  The groups of data that are copied are defined
as <quote>sets.</quote></para>

<para>A replication set consists of the following:</para>
<itemizedlist>

<listitem><para>Keys on tables that are to be replicated that have no
suitable primary key</para></listitem>

<listitem><para>Tables that are to be replicated</para></listitem>

<listitem><para>Sequences that are to be replicated</para></listitem>
</itemizedlist>

<sect2><title>Primary Keys</title>

<para>&slony1; <emphasis>needs</emphasis> to have a primary key or
candidate thereof on each table that is replicated.  PK values are
used as the primary identifier for each tuple that is modified in the
source system.  There are three ways that you can get &slony1; to use
a primary key:</para>

<itemizedlist>

<listitem><para> If the table has a formally identified primary key,
<xref linkend="stmtsetaddtable"> can be used without any need to
reference the primary key.  &slony1; will pick up that there is a
primary key, and use it.</para></listitem>

<listitem><para> If the table hasn't got a primary key, but has some
<emphasis>candidate</emphasis> primary key, that is, some index on a
combination of fields that is UNIQUE and NOT NULL, then you can
specify the key, as in</para>

<programlisting>
SET ADD TABLE (set id = 1, origin = 1, id = 42, 
               full qualified name = 'public.this_table', 
               key = 'this_by_that', 
     comment='this_table has this_by_that as a candidate primary key');
</programlisting>

<para> Notice that while you need to specify the namespace for the
table, you must <emphasis>not</emphasis> specify the namespace for the
key, as it infers the namespace from the table.</para></listitem>

<listitem><para> If the table hasn't even got a candidate primary key,
you can ask &slony1; to provide one.  This is done by first using
<xref linkend="stmttableaddkey"> to add a column populated using a
&slony1; sequence, and then having the <xref linkend="stmtsetaddtable"> include the directive
<option>key=serial</option>, to indicate that &slony1;'s own column
should be used.</para></listitem>

</itemizedlist>

<para> It is not terribly important whether you pick a
<quote>true</quote> primary key or a mere <quote>candidate primary
key;</quote> it is, however, recommended that you have one of those
instead of having &slony1; populate the PK column for you.  If you
don't have a suitable primary key, that means that the table hasn't
got any mechanism from your application's standpoint of keeping values
unique.  &slony1; may therefore introduce a new failure mode for your
application, and this implies that you had a way to enter confusing
data into the database.</para>
</sect2>

<sect2 id="definesets"><title>Grouping tables into sets</title>

<para> It will be vital to group tables together into a single set if
those tables are related via foreign key constraints.  If tables that
are thus related are <emphasis>not</emphasis> replicated together,
you'll find yourself in trouble if you switch the <quote>master
provider</quote> from one node to another, and discover that the new
<quote>master</quote> can't be updated properly because it is missing
the contents of dependent tables.</para>

<para> There are also several reasons why you might
<emphasis>not</emphasis> want to have all of the tables in one
replication set:

<itemizedlist>

<listitem><para> The initial <command>COPY_SET</command> event for a
large set leads to a <link linkend="longtxnsareevil"> long running
transaction </link> on the provider node.  The <link linkend="faq">
FAQ </link> outlines a number of problems that result from long
running transactions that will injure system performance.</para>

<para> If you can split such a large set into several smaller pieces,
that will shorten the length of each of the transactions, lessening
the degree of <quote>injury</quote> to performance.</para>

<para> These <quote>negative effects</quote> tend to emerge when the
database being subscribed to is tens of gigabytes in size.  For
relatively small databases, this shouldn't be a material factor.
</para>
</listitem>

<listitem><para> Any time you invoke <xref linkend="stmtddlscript">,
this requests a lock on <emphasis> every single table in the
replication set. </emphasis></para>

<para> There have been reports <quote>in the field</quote> of this
leading to deadlocks such that the <xref linkend="stmtddlscript">
request had to be submitted many times in order for it to actually
complete successfully.</para>

<para> The more tables you have in a set, the more tables need to be
locked, and the greater the chances of deadlocks. </para>

<para> By the same token, if a particular DDL script only needs to
affect a couple of tables, you might use <xref
linkend="stmtsetmovetable"> to move them temporarily to a new
replication set.  By diminishing the number of locks needed, this
should ease the ability to get the DDL change into place.</para>
</listitem>

</itemizedlist></para>

</sect2>

<sect2> <title> The Pathology of Sequences </title>

<para> Each time a SYNC is processed, values are recorded for
<emphasis>all</emphasis> of the sequences in the set.  If there are a
lot of sequences, this can cause <xref linkend="table.sl-seqlog"> to
grow rather large.</para>

<para> This points to an important difference between tables and
sequences: if you add additional tables that do not see much/any
activity, this does not add any material load to the work being done
by replication.  For a replicated sequence, values must
<emphasis>regularly</emphasis> be propagated to subscribers.  Consider
the effects:

<itemizedlist>

<listitem><para> A replicated table that is never updated does not
introduce much work to the system.</para>

<para> If it is not updated, the trigger on the table on the origin
never fires, and no entries are added to <xref
       linkend="table.sl-log-1">.  The table never appears in any of the
further replication queries (<emphasis>e.g.</emphasis> in the
<command>FETCH 100 FROM LOG</command> queries used to find
replicatable data) as they only look for tables for which there are
entries in <xref linkend="table.sl-log-1">.</para></listitem>

<listitem><para> In contrast, a fixed amount of work is introduced to
each SYNC by each sequence that is replicated.</para>

<para> Replicate 300 sequence and 300 rows need to be added to <xref
       linkend="table.sl-seqlog"> on a regular basis.</para>

<para> It is more than likely that if the value of a particular
sequence hasn't changed since it was last checked, perhaps the same
value need not be stored over and over; some thought needs to go into
how to do that safely.</para></listitem>

<listitem><para> <ulink url=
"http://gborg.postgresql.org/project/slony1/bugs/bugupdate.php?1226">
Bug #1226 </ulink> indicates an error condition that can come up if
you have a replication set that consists solely of sequences. </para>

<para> This is documented more in the <link linkend="sequenceset"> FAQ
here;</link> the long and short is that having a replication set
consisting only of sequences is not a particularly good
idea.</para></listitem>

</itemizedlist></para></sect2>

</sect1>

<!-- 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:"book.sgml"
sgml-exposed-tags:nil
sgml-local-catalogs:("/usr/lib/sgml/catalog")
sgml-local-ecat-files:nil
End:
-->
