<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Inside Oracle - Julian Dyke</title>
	<atom:link href="http://juliandyke.wordpress.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://juliandyke.wordpress.com</link>
	<description>Understanding Oracle Internals</description>
	<lastBuildDate>Wed, 02 Nov 2011 18:01:18 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='juliandyke.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://s2.wp.com/i/buttonw-com.png</url>
		<title>Inside Oracle - Julian Dyke</title>
		<link>http://juliandyke.wordpress.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://juliandyke.wordpress.com/osd.xml" title="Inside Oracle - Julian Dyke" />
	<atom:link rel='hub' href='http://juliandyke.wordpress.com/?pushpress=hub'/>
		<item>
		<title>DBFS</title>
		<link>http://juliandyke.wordpress.com/2011/11/02/dbfs/</link>
		<comments>http://juliandyke.wordpress.com/2011/11/02/dbfs/#comments</comments>
		<pubDate>Wed, 02 Nov 2011 18:01:16 +0000</pubDate>
		<dc:creator>Julian Dyke</dc:creator>
				<category><![CDATA[Database]]></category>

		<guid isPermaLink="false">http://juliandyke.wordpress.com/?p=262</guid>
		<description><![CDATA[Oracle customers purchasing Exadata are not permitted to install an HBA in their new hardware in order to connect to the legacy SAN. As ASM storage is provided internally, this limitation does not affect most production systems as external data can still be accessed via the Ethernet network. However, using a network protocol such as [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=juliandyke.wordpress.com&amp;blog=12569785&amp;post=262&amp;subd=juliandyke&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Oracle customers purchasing Exadata are not permitted to install an HBA in their new hardware in order to connect to the legacy SAN. As ASM storage is provided internally, this limitation does not affect most production systems as external data can still be accessed via the Ethernet network. However, using a network protocol such as NFS may not be the preferred option during initial migration and data warehouse ETL processes. In these cases Oracle recommends staging data in a Database File System (DBFS). This post examines some basic concepts behind this new feature. The installation was tested on Oracle 11.2.0.1.0 running on 32-bit Enterprise Linux 5.</p>
<p>DBFS was introduced in Oracle 11.2. It enables the database to be used as a POSIX-compatible file system. It is based on the SecureFile technology which was introduced in Oracle 11.1 to support LOB functionality. Consequently DBFS is documented in the Oracle Database SecureFiles and Large Objects Developer&#8217;s Guide. At the time of writing DBFS only appears to be supported on Linux (both 32-bit and 64-bit). As a Solaris option is now available with Exadata, presumably DBFS will be extended to support this operating system at some point in the future.</p>
<h3>Prerequisites</h3>
<p>DBFS is based on the FUSE library infrastructure. FUSE (Filesystem in USErspace) is an open-source project that supports the implementation of a fully functional file-system in a user-space program. More information can be found about the FUSE library on its project page (http://fuse.sourceforge.net). FUSE is delivered as a set of source files which must be compiled and linked on the target libraries. FUSE runs as a dynamic kernel module; it is not necessary to recompile the kernel during installation.</p>
<p>Prior to compiling the FUSE library, the Linux kernel development RPM must be installed. This package and its dependencies may already be installed. To verify use:</p>
<pre>$ rpm -q kernel-devel
kernel-devel-2.6.18-92.el5</pre>
<p>If this package does not exist it can be installed from the distribution media or using <em>yum</em>.</p>
<p>FUSE can be downloaded from <em>http://fuse.sourceforge.net</em> and delivered as a zipped archive called <em>fuse-2.7.3.tar.gz</em>. I copied this to a new directory owned by the root user called <em>/root/fuse</em>. Make sure you download version 2.7.3; subsequent versions will not work with the Oracle client.</p>
<p>As the root user unzip the archive using</p>
<pre>[root@server1]# tar -xzvf fuse-2.7.3.tar.gz</pre>
<p>This will create a subdirectory, in this case called <em>/root/fuse/fuse-2.7.3</em>.</p>
<p>Before building the FUSE library determine the kernel directory for the current kernel. On Red Hat and Oracle Enterprise Linux this directory will have the format:</p>
<pre>/usr/src/kernels/`uname -r`-`uname -p`</pre>
<p>For example:</p>
<pre>/usr/src/kernels/2.6.18-92.el5-i686</pre>
<p>Change to the FUSE source directory and run the configure script specifying the kernel directory for the <em>with-kernel</em> parameter:</p>
<pre>[root@server1]# cd /root/fuse/fuse-2.7.3
[root@server1]# ./configure --prefix=/usr --with-kernel=/usr/src/kernels/2.6.18-92.el5-i686</pre>
<p>Build and install the library</p>
<pre>[root@server1]# make
[root@server1]# make install</pre>
<p>The documentation recommends performing the following steps to complete the installation. I am not entirely convinced that any of these steps are necessary when the previous steps have been performed by the root user. However they all appear to be harmless.</p>
<pre>[root@server1]# /sbin/depmod
[root@server1]# /sbin/modprobe fuse
[root@server1]# chmod 666 /dev/fuse
[root@server1]# echo "/sbin/modprobe fuse" &gt;&gt; /etc/rc.modules</pre>
<h3>Creating a DBFS file system</h3>
<p>It is necessary to create a tablespace for the DBFS file system. For a simple test the new tablespace does not need to be very large. For example:</p>
<pre>CREATE TABLESPACE dbfs1
DATAFILE '/u01/oradata/TEST/dbfs01.dbf' SIZE 100M;</pre>
<p>I initially attempted to create a DBFS file system using the SYS (AS SYSDBA) user. However, the create script failed when SYS attempted to GRANT privileges to itself so I switched to a non-default user called US01. In order to manage DBFS the user must be assigned the DBFS_ROLE. For example:</p>
<pre>[oracle@server1]$ sqlplus / as sysdba
SQL&gt; GRANT dbfs_role TO us01</pre>
<p>To create a file system use the <em>dbms_create_filesystem.sql</em> script which is located in <em>$ORACLE_HOME/rdbms/admin</em>. This script calls <em>dbms_create_filesystem_advanced.sql</em> which is located in the same directory. In my version, SQL*Plus was unable to locate the latter script unless it was in the current working directory. Therefore I had to change directory to <em>$ORACLE_HOME/rdbms/admin</em> before executing SQL*Plus. For example:</p>
<pre>[oracle@server1]$ cd $ORACLE_HOME/rdbms/admin
[oracle@server1]$ sqlplus us01/us01
SQL&gt; @dbfs_create_filesystem.sql DBFS1 dbfs1</pre>
<p>The <em>dbfs_create_filesystem.sql</em> script takes two parameters; the tablespace name (<em>DBFS1</em>) and the file system name (<em>dbfs1</em>). In retrospect I should have assigned different names to the tablespace and file system.</p>
<h3>Mounting a DBFS file system</h3>
<p>Now the new file system exists within the database. However, we cannot access it externally yet. A new utility called <em>dbms_client</em> which is shipped in <em>$ORACLE_HOME/bin</em> in Oracle 11.2.0.1 and above.allows the DBFS file system to be mounted and managed from the command line. The 11.2.0.1 version of this utility is quite basic and it is easy to imagine customer feedback will drive improvements in functionality in subsequent releases.</p>
<p>As the root user create a mount point. In this case the mount point will be called <em>/mnt/dbfs1</em> and will be owned by the oracle user:</p>
<pre>[root@server1]# mkdir /mnt/dbfs1
[root@server1]# chown oracle:dba /mnt/dbfs1</pre>
<p>In Oracle 11.2.0.1, unlike almost all other tools and utilities in <em>$ORACLE_HOME/bin</em> the <em>dbms_client</em> utility does not have a wrapper script. On my server invoking <em>dbfs_client</em> failed with the following error:</p>
<pre>[oracle@server1]$ cd $ORACLE_HOME/bin
[oracle@server1]$ ./dbfs_client
./dbfs_client: error while loading shared libraries: libclntsh.so.11.1: cannot open shared object file: No such file or directory</pre>
<p>This error occurs because the LD_LIBRARY_PATH environment variable has not been set correctly. This is an oversight, but one that is likely to be increasingly common as this environment variable is normally set by the other wrapper scripts. To fix the error set LD_LIBRARY_PATH, preferably in <em>.bash_profile</em>. For example:</p>
<pre>export LD_LIBRARY_PATH=$ORACLE_HOME/lib</pre>
<p>The client should now start and display the following help message:</p>
<pre>[oracle@server1]$ cd $ORACLE_HOME/bin
[oracle@server1]$ ./dbfs_client
usage: ./dbfs_client @ [options]
  db_user:              Name of Database user that owns DBFS content repository filesystem(s)
  db_server:            A valid connect string for Oracle database server
                        (for example, hrdb_host:1521/hrservice)
  mountpoint:           Path to mount Database File System(s)
                        All the file systems owned by the database user will be seen at the mountpoint.
DBFS options:
  -o direct_io          Bypass the Linux page cache. Gives much better performance for large files.
                        Programs in the file system cannot be executed with this option.
                        This option is recommended when DBFS is used as an ETL staging area.
  -o wallet             Run ./dbfs_client in background.
                        Wallet must be configured to get credentials.
  -o failover           ./dbfs_client fails over to surviving database instance with no data loss.
                        Some performance cost on writes, especially for small files.
  -o allow_root         Allows root access to the filesystem.
                        This option requires setting 'user_allow_other' parameter in '/etc/fuse.conf'.
  -o allow_other        Allows other users access to the file system.
                        This option requires setting 'user_allow_other' parameter in '/etc/fuse.conf'.
  -o rw                 Mount the filesystem read-write. [Default]
  -o ro                 Mount the filesystem read-only. Files cannot be modified.
  -o trace_file=STR     Tracing  | 'syslog'
  -o trace_level=N      Trace Level: 1-&gt;DEBUG, 2-&gt;INFO, 3-&gt;WARNING, 4-&gt;ERROR, 5-&gt;CRITICAL [Default: 4]
  -h                    help
  -V                    version</pre>
<p>Note that in Oracle 11.2.0.1 the above usage message omits the <em>&#8211;command</em> option.</p>
<p>This version of the <em>dbfs_client</em> utility does not support the bequeath protocol, so on the server it is not possible to identify the target instance using the $ORACLE_SID environment variable. It is therefore necessary to ensure that a TNS address is specified in <em>$TNS_ADMIN/tnsnames.ora</em>.</p>
<pre>TEST=
(DESCRIPTION=
  (ADDRESS=
    (PROTOCOL=TCP)(HOST=vm7)(PORT=1521)
  )
  (CONNECT_DATA=(SERVICE_NAME=TEST))
)</pre>
<p>It is also possible to specify a database service and DBFS does support instance failover though I have not yet tested it in a RAC environment.</p>
<p>To mount a DBFS file system use the following syntax</p>
<pre>dbfs_client @ [options]</pre>
<p>For example:</p>
<pre>[oracle@server1]$ cd $ORACLE_HOME/bin
[oracle@server1]$ ./dbfs_client us01@TEST /mnt/dbfs1</pre>
<p>A password will be requested. In this case it is the Oracle database password for the <em>US01</em> user.</p>
<p>By default <em>dbfs_client</em> will block until the file system is un-mounted.</p>
<p>In this version it is possible to workaround the blocking of <em>dbfs_client</em> using the <em>nohup</em> command to run the utility in the background. For example:</p>
<pre>[oracle@server1]$ cd $ORACLE_HOME/bin
[oracle@server1]$ nohup ./dbfs_client us01@TEST /mnt/dbfs1 &amp;</pre>
<p>In another shell session verify that the file system has been mounted using the <em>mount</em> command. For example:</p>
<pre>[oracle@server1]$ mount
/dev/mapper/VolGroup00-LogVol00 on / type ext3 (rw)
proc on /proc type proc (rw)
sysfs on /sys type sysfs (rw)
devpts on /dev/pts type devpts (rw,gid=5,mode=620)
/dev/sda1 on /boot type ext3 (rw)
tmpfs on /dev/shm type tmpfs (rw)
/dev/sdb1 on /u01/oradata type ext3 (rw)
/dev/sdc1 on /home/oracle/stage type ext3 (rw)
none on /proc/sys/fs/binfmt_misc type binfmt_misc (rw)
sunrpc on /var/lib/nfs/rpc_pipefs type rpc_pipefs (rw)
dbfs on /mnt/dbfs1 type fuse (rw,nosuid,nodev,max_read=1048576,default_permissions,user=oracle)</pre>
<p>At this point it is possible to navigate into the new file system from the Linux shell using commands such as <em>pwd, cd, mkdir, cp, rm</em> etc.</p>
<p>For example:</p>
<pre>[oracle@server1]$ cd /mnt/dbfs1/dbfs1
[oracle@server1]$ pwd
/mnt/dbfs1/dbfs1
[oracle@server1]$ mkdir dir1
[oracle@server1]$ cd dir1
[oracle@server1]$ pwd
/mnt/dbfs1/dbfs1/dir1
[oracle@server1]$ cp /tmp/gp/*.csv /mnt/dbfs1/dbfs1/dir1
[oracle@server1]$ ls -l
total 42
-rw-r--r-- 1 oracle oinstall  1129 Jul  7 19:33 circuit.csv
-rw-r--r-- 1 oracle oinstall   161 Jul  7 12:02 classification.csv
-rw-r--r-- 1 oracle oinstall   535 Jul  7 12:02 country.csv
-rw-r--r-- 1 oracle oinstall 12762 Jul  7 12:02 driver.csv
-rw-r--r-- 1 oracle oinstall   708 Jul  7 12:02 engine.csv
-rw-r--r-- 1 oracle oinstall   506 Jul  7 12:02 grandprix.csv
-rw-r--r-- 1 oracle oinstall 22385 Jul  7 12:02 race.csv
-rw-r--r-- 1 oracle oinstall   550 Jul  7 12:02 season.csv
-rw-r--r-- 1 oracle oinstall  1487 Jul  7 12:02 team.csv</pre>
<p>It is possible to copy files in and out of the DBFS using standard operating system commands.</p>
<h3>Unmounting a DBFS filesystem</h3>
<p>To unmount the DBFS file system use the <em>fusemount</em> command. For example:</p>
<pre>[oracle@server1]$ fusermount -u /mnt/dbfs1</pre>
<h3>Creating an Oracle Wallet</h3>
<p>It is also possible to include the filesystem in <em>/etc/fstab so that it can be mounted using the mount command. In order to ensure that the mount command is executed with the correct Oracle credentials, an Oracle wallet must be created. Note that use of Oracle Wallets may require licensing of the Oracle Advanced Security option. There are several steps to this process:</em></p>
<p>As the oracle user create a subdirectory for the wallet e.g.:</p>
<pre>[oracle@server1]$ mkdir /home/oracle/wallet</pre>
<p>As the oracle user create the wallet.</p>
<pre>[oracle@server1]$ mkstore -wrl /home/oracle/wallet -create
Oracle Secret Store Tool : Version 11.2.0.1.0 - Production
Copyright (c) 2004, 2009, Oracle and/or its affiliates. All rights reserved.

Enter password:
Enter password again:</pre>
<p>This creates the following files in /home/oracle/wallet:</p>
<pre>[oracle@server1]$ ls -l /home/oracle/wallet
-rw------- 1 oracle oinstall 3589 Jul  7 11:11 cwallet.sso
-rw------- 1 oracle oinstall 3512 Jul  7 11:11 ewallet.p12</pre>
<p>Edit <em>$TNS_ADMIN/sqlnet.ora</em> and add the following entries:</p>
<pre>WALLET_LOCATION=(SOURCE=(METHOD=FILE)(METHOD_DATA=(DIRECTORY=/home/oracle/wallet)))

SQLNET.WALLET_OVERRIDE=TRUE</pre>
<p>Create the <em>DBConnectString</em> credential in the wallet specifying the Oracle username and password that should be associated with this credential. In this case both the username and password are <em>&#8220;us01&#8243;</em>.</p>
<pre>[oracle@server1]$ mkstore -wrl /home/oracle/wallet -createCredential DBConnectString us01 us01
Oracle Secret Store Tool : Version 11.2.0.1.0 - Production
Copyright (c) 2004, 2009, Oracle and/or its affiliates. All rights reserved.

Enter wallet password:  &lt;&lt; Oracle11gR2 &gt;&gt; 

Create credential oracle.security.client.connect_string1</pre>
<p>Following creation of this credential it should be possible to mount the new DBFS file system using the wallet. For example:</p>
<pre>[oracle@server1]$ dbfs_client -o wallet /@DBConnectString /mnt/dbfs1</pre>
<p>Note that the &#8220;/@&#8221; syntax specifies that the connection should obtain its credentials from the Oracle wallet.</p>
<p>In Oracle 11.2.0.1, the above command mounts the DBFS file system and returns to the command line without blocking.</p>
<h3>Configuring the mount command</h3>
<p>A few more steps are required to enable the mount command to use an entry in <em>/etc/fstab</em>.</p>
<p>The 11.2 documentation recommends that a symbolic link should be created between <em>$ORACLE_HOME/bin/dbfs_client</em> and <em>/sbin/mount.dbfs</em>. In my opinion the recommended syntax has been reversed. I believe that the following is correct:</p>
<pre>[root@server1]# ln -s $ORACLE_HOME/bin/dbfs_client /sbin/mount.dbfs</pre>
<p>However, I would not recommend using a symbolic link. Although I modified the bash profile for my root user to include $LD_LIBRARY_PATH environment variable, the mount command appear to unset this variable when it called the <em>mount.dbfs</em> helper. Therefore it may be more appropriate to create <em>/sbin/mount.dbfs</em> as a script containing commands similar to the following:</p>
<pre>#!/bin/bash
export ORACLE_HOME=/u01/app/oracle/product/11.2.0/dbhome_1
export LD_LIBRARY_PATH=$ORACLE_HOME/lib
nohup $ORACLE_HOME/bin/dbfs_client $* 2&gt; /dev/null &amp;</pre>
<p>A new operating system is required for fuse. For example:</p>
<pre>[root@server1]# /usr/sbin/groupadd fuse</pre>
<p>The user that will be mounting the DBFS file system should be added to the <em>fuse</em> group. I used the <em>oracle</em> user which on my server already belonged to primary group <em>oinstall</em> (specified in /etc/passwd) and secondary group <em>dba</em> (specified in <em>/etc/group</em>). I used the following command:</p>
<pre>[root@server1]# usermod -G dba,fuse oracle</pre>
<p>The above command adds the <em>oracle</em> user to both the <em>dba</em> and <em>fuse</em> groups. Take care when specifying this command &#8211; it requires a list of all secondary groups so in this example if only the <em>fuse</em> group is specified (<em>usermod -G fuse oracle</em>) then the <em>oracle user will be removed from the <em>dba</em> group. The changes should be written to <em>/etc/group</em>.</em></p>
<p>Finally the following line should be added to <em>/etc/fstab</em>:</p>
<pre>/sbin/mount.dbfs#/@DBConnectString /mnt/dbfs1 fuse rw,user,noauto 0 0</pre>
<p>Note the use of the Oracle wallet credential.</p>
<p>If the above configuration has been completed successfully it should be possible to mount the new DBFS file system using the <em>mount</em> command. For example:</p>
<pre>[root@server1]# mount /mnt/dbfs1</pre>
<p>The command will mount the file system and then block until the file system is unmounted. Again the <em>nohup</em> command can be used to ensure the <em>mount</em> command runs as a background process:</p>
<pre>[root@server1]# nohup mount /mnt/dbfs1 &amp;</pre>
<p>I remain unconvinced that enabling use of the <em>mount</em> command for the DBFS file system justifies the amount of configuration required. It is not possible to configure DBFS to be auto-mounted during a system boot as this is not supported by the current version of FUSE. There is also a timing issue &#8211; the database must already be started before the DBFS file system can be mounted. In view of potential uses for DBFS (migration and data warehouse loading) it is probably sufficient to mount the DBFS file systems manually when required.</p>
<h3>Executing Commands</h3>
<p>In addition to the shell interface it is also possible to execute commands using <em>dbfs_client</em>. Whilst this is unnecessary when connected directly to the database server, it could be useful when the <em>dbfs_client</em> utility is being executed on another client.</p>
<p>Unfortunately in Oracle 11.2.0.1 <em>dbfs_client</em> does not appear to be sufficiently mature to accept options and to execute commands in the same invocation. This means that it is not possible to execute an operating system whilst using a wallet. Consequently it is necessary to manually enter a password for each execution of <em>dbfs_client</em> which restricts scripting options.</p>
<p>The following are examples of <em>dbfs_client</em> operating system commands:</p>
<pre>$ dbfs_client us01@TEST --command mkdir dbfs:/dbfs1/dir1
$ dbfs_client us01@TEST --command cp /home/oracle/gp dbfs:/dbfs1/dir1
$ dbfs_client us01@TEST --command ls -l dbfs:/dbfs1/dir1
-rw-r--r--            oracle        oinstall          725859    Jul 07 19:33    dbfs:/dbfs1/dir1/car.csv
-rw-r--r--            oracle        oinstall            1129    Jul 07 19:33    dbfs:/dbfs1/dir1/circuit.csv
-rw-r--r--            oracle        oinstall             708    Jul 07 12:02    dbfs:/dbfs1/dir1/engine.csv
-rw-r--r--            oracle        oinstall             550    Jul 07 12:02    dbfs:/dbfs1/dir1/season.csv
-rw-r--r--            oracle        oinstall             161    Jul 07 12:02    dbfs:/dbfs1/dir1/classification.csv
-rw-r--r--            oracle        oinstall             506    Jul 07 12:02    dbfs:/dbfs1/dir1/grandprix.csv
-rw-r--r--            oracle        oinstall           22385    Jul 07 12:02    dbfs:/dbfs1/dir1/race.csv
-rw-r--r--            oracle        oinstall           12762    Jul 07 12:02    dbfs:/dbfs1/dir1/driver.csv
-rw-r--r--            oracle        oinstall             535    Jul 07 12:02    dbfs:/dbfs1/dir1/country.csv
-rw-r--r--            oracle        oinstall            1487    Jul 07 12:02    dbfs:/dbfs1/dir1/team.csv
$ dbfs_client us01@TEST --command rm dbfs:/dbfs1/dir1/car.csv</pre>
<p>When copying large files to the DBFS file system, <em>-o directio</em> may be an attractive <em>dbfs_client</em> option as it bypasses the operating system page cache.</p>
<h3>Conclusion</h3>
<p>DBFS is relatively easy to set up and manage. In Oracle 11.2.0.1 there are still plenty of niggling design and usability issues. However I am confident that these issues will be rapidly resolved as DBFS appears to be a fundamental component of the Exadata strategy and should therefore experience accelerated development for a few releases.</p>
<p>It is important to understand the differences between DBFS and ACFS. Note that in Oracle 11.2.0.2 a separate licence is required for ACFS on some platforms.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/juliandyke.wordpress.com/262/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/juliandyke.wordpress.com/262/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/juliandyke.wordpress.com/262/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/juliandyke.wordpress.com/262/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/juliandyke.wordpress.com/262/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/juliandyke.wordpress.com/262/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/juliandyke.wordpress.com/262/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/juliandyke.wordpress.com/262/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/juliandyke.wordpress.com/262/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/juliandyke.wordpress.com/262/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/juliandyke.wordpress.com/262/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/juliandyke.wordpress.com/262/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/juliandyke.wordpress.com/262/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/juliandyke.wordpress.com/262/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=juliandyke.wordpress.com&amp;blog=12569785&amp;post=262&amp;subd=juliandyke&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://juliandyke.wordpress.com/2011/11/02/dbfs/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/22a8ad7674d16428224bf7cd8d41d488?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">juliandyke</media:title>
		</media:content>
	</item>
		<item>
		<title>Using Perl DBI with Oracle</title>
		<link>http://juliandyke.wordpress.com/2011/10/31/using-perl-dbi-with-oracle/</link>
		<comments>http://juliandyke.wordpress.com/2011/10/31/using-perl-dbi-with-oracle/#comments</comments>
		<pubDate>Mon, 31 Oct 2011 19:50:18 +0000</pubDate>
		<dc:creator>Julian Dyke</dc:creator>
				<category><![CDATA[Perl]]></category>

		<guid isPermaLink="false">http://juliandyke.wordpress.com/?p=253</guid>
		<description><![CDATA[I have been using Perl scripts extensively for a couple of years now to analyze data and generate charts and tables. However, I have only recently investigated using Perl to communicate with an Oracle database. This post was researched using Perl v5.10 with Oracle 11.2.0.2 Express Edition on Windows XP 32-bit. The examples are based [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=juliandyke.wordpress.com&amp;blog=12569785&amp;post=253&amp;subd=juliandyke&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>I have been using Perl scripts extensively for a couple of years now to analyze data and generate charts and tables. However, I have only recently investigated using Perl to communicate with an Oracle database.</p>
<p>This post was researched using Perl v5.10 with Oracle 11.2.0.2 Express Edition on Windows XP 32-bit. The examples are based on a table listing drivers in the 2011 Formula One World Championship.</p>
<p>The example table is defined as follows:</p>
<pre>
CREATE TABLE driver
(
  key NUMBER,
  name VARCHAR2(30),
  team VARCHAR2(30),
  points NUMBER
);
</pre>
<p>The following rows were inserted into the table (based on championship standings after 17 of the 19 races:</p>
<pre>
INSERT INTO driver VALUES (1,'Sebastian Vettel','Red Bull',374);
INSERT INTO driver VALUES (2,'Jenson Button','McLaren',240);
INSERT INTO driver VALUES (3,'Fernando Alonso','Ferrari',227);
INSERT INTO driver VALUES (4,'Mark Webber','Red Bull',221);
COMMIT;
</pre>
<h3>Single Row Select</h3>
<p>The first script counts the number of rows in the DRIVER table. Although trivial this script demonstrates the basic components of all Perl DBI scripts.</p>
<pre>use DBI;

if (!($dbh = DBI-&gt;connect ('DBI:Oracle:XE',"us01","us01")))
{
  die ("Failed to connect to database: " . DBI-&gt;errstr);
};

$sql = "SELECT COUNT(*) FROM driver";

($count) = $dbh-&gt;selectrow_array ($sql);

printf ("Count = %d\n",$count);

$dbh-&gt;disconnect;</pre>
<p>The <i>use DBI</i> pragma instructs Perl to load the Database Interface (DBI) module. I normally use the strict pragma, but have omitted it within this post to improve clarity.</p>
<p>A connection must be created before any statements are executed. A successful connection returns what is referred to as a database handle. Most Perl scripts seem to use <i>$dbh</i> for the database handle.</p>
<p>In this example the database SID is <i>XE</i> and therefore the connect string is <i>DBI:Oracle:XE</i>. In my highly unsecure database both the username and password are us01.</p>
<p>I like to define the SQL statement using a separate variable (<i>$sql</i>). This is not necessary, but in my opinion results in clearer and more supportable code as the length and complexity of the SQL statements increases.</p>
<p>In this example the query can only return one row. I have used the <i>selectrow_array</i> subroutine to execute the query and to fetch the result. The row is returned into the <i>$count</i> variable.</p>
<p>Finally the script disconnects from the database.</p>
<p>The script should return the following output:</p>
<pre>Count = 4</pre>
<h3>Multi-Row Select</h3>
<p>This example demonstrates how to select multiple rows from a cursor.</p>
<pre>use DBI;

if (!($dbh = DBI-&gt;connect ('DBI:Oracle:XE',"us01","us01")))
{
  die ("Failed to connect to database: " . DBI-&gt;errstr);
};

$sql =
  "SELECT name,team,points ".
  "FROM driver ".
  "ORDER BY points DESC";

if (!($sth = $dbh-&gt;prepare ($sql)))
{
  die ("Failed to prepare statement: " . DBI-&gt;errstr);
};

$sth-&gt;execute;

while (@data = $sth-&gt;fetchrow_array())
{
  $name = $data[0];
  $team = $data[1];
  $points = $data[2];
  printf ("%-20s %-20s %3d\n",$name,$team,$points);
}

$sth-&gt;finish;

$dbh-&gt;disconnect;</pre>
<p>In this example the SQL statement is parsed using the <i>prepare</i> subroutine. This returns a statement handle ($sth) which will be used to refer to the statement throughout the rest of the script.</p>
<p>This query does not have any bind variables, so it can be executed using the execute subroutine. Rows can then be fetched one at a time using the <i>fetchrow_array</i> subroutine which returns an array of results. In this case I have assigned each array element to a separate variable to demonstrate that the array is zero-based.<br />
When all required rows have been fetched from the cursor the statement can be closed using the <i>finish</i> subroutine.</p>
<p>This script generates the following output:</p>
<pre>Sebastian Vettel Red Bull 374
Jenson Button McLaren 240
Fernando Alonso Ferrari 227
Mark Webber Red Bull 221</pre>
<p>The following script demonstrates a slightly different way of coding the fetch:</p>
<pre>use DBI;

if (!($dbh = DBI-&gt;connect ('DBI:Oracle:XE',"us01","us01")))
{
  die ("Failed to connect to database: " . DBI-&gt;errstr);
};

$sql =
  "SELECT name,team,points ".
  "FROM driver ".
  "ORDER BY points DESC";

if (!($sth = $dbh-&gt;prepare ($sql)))
{
  die ("Failed to prepare statement: " . DBI-&gt;errstr);
};

$sth-&gt;execute;

while (($name,$team,$points) = $sth-&gt;fetchrow_array())
{
  printf ("%-20s %-20s %3d\n",$name,$team,$points);
}

$sth-&gt;finish;

$dbh-&gt;disconnect;</pre>
<p>In the above example each row is fetched directly into a list of variables.</p>
<p>The second script generates the same output:</p>
<pre>Sebastian Vettel Red Bull 374
Jenson Button McLaren 240
Fernando Alonso Ferrari 227
Mark Webber Red Bull 221</pre>
<p>Both the above methods are functionally equivalent and the choice of one or the other is simply a matter of personal taste.</p>
<p>However, there is a third way to achieve the same results. This method, however, is slightly different:</p>
<pre>use DBI;

if (!($dbh = DBI-&gt;connect ('DBI:Oracle:XE',"us01","us01")))
{
  die ("Failed to connect to database: " . DBI-&gt;errstr);
};

$sql =
  "SELECT name,team,points ".
  "FROM driver ".
  "ORDER BY points DESC";

if (!($sth = $dbh-&gt;prepare ($sql)))
{
  die ("Failed to prepare statement: " . DBI-&gt;errstr);
};

$sth-&gt;execute;

$array = $sth-&gt;fetchall_arrayref ();

foreach $row (@$array)
{
  ($name,$team,$points) = @$row;
  printf ("%-20s %-20s %3d\n",$name,$team,$points);
}

$sth-&gt;finish;

$dbh-&gt;disconnect;</pre>
<p>In the above example, the <i>fetchall_arrayref</i> call fetches all rows into <i>$array</i>. This array can subsequently be processed without any further communication with the database. In some circumstances this technique could be very efficient &#8211; for example returning the top ten results. It could be less efficient in other circumstances particularly if a large number of rows may be returned or the later rows are likely to be discarded.</p>
<p>The third script generates the same output as its predecessors:</p>
<pre>Sebastian Vettel Red Bull 374
Jenson Button McLaren 240
Fernando Alonso Ferrari 227
Mark Webber Red Bull 221</pre>
<h3>Bind Variables</h3>
<p>We will normally want to use bind variables with SQL queries. The following example uses a bind variable to select the rows for a single team &#8211; in this case Red Bull:</p>
<pre>use DBI;

if (!($dbh = DBI-&gt;connect ('DBI:Oracle:XE',"us01","us01")))
{
  die ("Failed to connect to database: " . DBI-&gt;errstr);
};

$sql =
  "SELECT name,team,points ".
  "FROM driver ".
  "WHERE team = ?";

if (!($sth = $dbh-&gt;prepare ($sql)))
{
  die ("Failed to prepare statement: " . DBI-&gt;errstr);
};

$sth-&gt;bind_param (1,"Red Bull");

$sth-&gt;execute;

while (@data = $sth-&gt;fetchrow_array())
{
  $name = $data[0];
  $team = $data[1];
  $points = $data[2];
  printf ("%-20s %-20s %3d\n",$name,$team,$points);
}

$sth-&gt;finish;

$dbh-&gt;disconnect;</pre>
<p>In Perl bind variables are represented by a &#8220;?&#8221; in the SQL statement text. Each bind variable should have an associated <i>bind_param call</i>. Bind parameters are numbered from 1 upwards.</p>
<p>You can avoid calls to <i>bind_param</i> by listing bind variables in the <i>execute</i> call:</p>
<pre>use DBI;

if (!($dbh = DBI-&gt;connect ('DBI:Oracle:XE',"us01","us01")))
{
  die ("Failed to connect to database: " . DBI-&gt;errstr);
};

$sql =
  "SELECT name,team,points ".
  "FROM driver ".
  "WHERE team = ?";

if (!($sth = $dbh-&gt;prepare ($sql)))
{
  die ("Failed to prepare statement: " . DBI-&gt;errstr);
};

$sth-&gt;execute ("Red Bull");

while (($name,$team,$points) = $sth-&gt;fetchrow_array())
{
  printf ("%-20s %-20s %3d\n",$name,$team,$points);
}

$sth-&gt;finish;

$dbh-&gt;disconnect;</pre>
<p>In the above example the bind variable value (&#8220;Red Bull&#8221;) is specified as the first parameter in the <i>execute</i> call. Any number of bind variables can be specified in this call.</p>
<h3>INSERT statements</h3>
<p>The following script is an example of a simple INSERT statement:</p>
<pre>use DBI;

if (!($dbh = DBI-&gt;connect ('DBI:Oracle:XE',"us01","us01")))
{
  die ("Failed to connect to database: " . DBI-&gt;errstr);
};

$sql = "INSERT INTO driver VALUES (5,'Lewis Hamilton','McLaren',202)";

if (!($dbh-&gt;do ($sql)))
{
  die ("Failed to insert row: " . DBI-&gt;errstr);
};

$dbh-&gt;disconnect;</pre>
<p>In the above script literal values have been specified for each column value.</p>
<p>The <i>do</i> subroutine allows us to execute a simple statement without the need to explicitly initialize a statement context.</p>
<p>Bind variables can also be specified. For example:</p>
<pre>use DBI;

if (!($dbh = DBI-&gt;connect ('DBI:Oracle:XE',"us01","us01")))
{
  die ("Failed to connect to database: " . DBI-&gt;errstr);
};

$sql = "INSERT INTO driver VALUES (?,?,?,?)";

if (!($sth = $dbh-&gt;prepare ($sql)))
{
  die ("Failed to prepare statement: " . DBI-&gt;errstr);
};

$sth-&gt;execute (6,"Felipe Massa","Ferrari",98);

$dbh-&gt;disconnect;</pre>
<p>In the above example the bind variables are listed as parameters in the <i>execute</i> call.</p>
<p>It is also possible to set bind variables using the <i>bind_param</i> call. For example:</p>
<pre>use DBI;

if (!($dbh = DBI-&gt;connect ('DBI:Oracle:XE',"us01","us01")))
{
  die ("Failed to connect to database: " . DBI-&gt;errstr);
};

$sql = "INSERT INTO driver VALUES (?,?,?,?)";

if (!($sth = $dbh-&gt;prepare ($sql)))
{
  die ("Failed to prepare statement: " . DBI-&gt;errstr);
};

$sth-&gt;bind_param (1,7);
$sth-&gt;bind_param (2,'Nico Rosberg');
$sth-&gt;bind_param (3,'Mercedes');
$sth-&gt;bind_param (4,75);

$sth-&gt;execute;

$dbh-&gt;disconnect;</pre>
<p>Note that bind parameters are numbered from 1 upwards.</p>
<p>In this example the primary key column could be generated automatically using a sequence. We can create the sequence as follows:</p>
<pre>CREATE SEQUENCE driver_sequence START WITH 8;</pre>
<p>The sequence starts at 8 as the table already contains 7 rows.</p>
<p>We can then create an INSERT trigger on the DRIVER table as follows:</p>
<pre>CREATE OR REPLACE TRIGGER driver_trigger
BEFORE INSERT ON driver
FOR EACH ROW
DECLARE
  l_key NUMBER;
BEGIN
  SELECT driver_sequence.NEXTVAL INTO l_key FROM dual;
  :new.key := l_key;
END;
/</pre>
<p>When we insert rows into the DRIVER table we no longer need to specify a key as this will be generated by the trigger.</p>
<p>However we might wish to know the value of the internally generated key. In this case we can use the RETURNING clause with the INSERT statement. For example:</p>
<pre>use DBI;

if (!($dbh = DBI-&gt;connect ('DBI:Oracle:XE',"us01","us01")))
{
  die ("Failed to connect to database: " . DBI-&gt;errstr);
};

$sql =
  "INSERT INTO driver (name,team,points) ".
  "VALUES (?,?,?) ".
  "RETURNING key INTO ?";

if (!($sth = $dbh-&gt;prepare ($sql)))
{
  die ("Failed to prepare statement: " . DBI-&gt;errstr);
};

$key = 0;

$sth-&gt;bind_param (1,'Michael Schumacher');
$sth-&gt;bind_param (2,'Mercedes');
$sth-&gt;bind_param (3,70);
$sth-&gt;bind_param_inout (4,\$key,SQL_NUMERIC);

$sth-&gt;execute;

printf ("Key = %d\n",$key);

$dbh-&gt;disconnect;</pre>
<p>Note that the RETURNING clause effectively declares a fourth bind variable. This variable differs from the others as it accepts a return value and is therefore declared as an INOUT parameter. Note that the <i>$key</i> parameter is passed by reference (not value) in the <i>bind_param_inout</i> call. and is therefore preceded by a backslash.</p>
<p>The above query returns the following output:</p>
<pre>Key = 8</pre>
<h3>UPDATE Statements</h3>
<p>As with INSERT statements the simplest form of an UPDATE statement uses literal values and the <i>do</i> subroutine.</p>
<pre>use DBI;

if (!($dbh = DBI-&gt;connect ('DBI:Oracle:XE',"us01","us01")))
{
  die ("Failed to connect to database: " . DBI-&gt;errstr);
};

$sql = "UPDATE driver SET points = 399 WHERE key = 1";

if (!($dbh-&gt;do ($sql)))
{
  die ("Failed to update row: " . DBI-&gt;errstr);
};

$dbh-&gt;disconnect;</pre>
<p>The above example adds 25 points to Sebastian Vettel&#8217;s current score of 374 points.</p>
<p>Alternatively we could use bind variables to achieve the same result. For example:</p>
<pre>use DBI;

if (!($dbh = DBI-&gt;connect ('DBI:Oracle:XE',"us01","us01")))
{
  die ("Failed to connect to database: " . DBI-&gt;errstr);
};

$sql = "UPDATE driver SET points = ? WHERE key = ?";

if (!($sth = $dbh-&gt;prepare ($sql)))
{
  die ("Failed to prepare statement: " . DBI-&gt;errstr);
};

$sth-&gt;execute (399,1);

$dbh-&gt;disconnect;</pre>
<p>In this example the bind variable values are supplied as arguments to the <i>execute</i> call.</p>
<p>We can also use <i>bind_param</i> calls to achieve the same objective:</p>
<pre>use DBI;

if (!($dbh = DBI-&gt;connect ('DBI:Oracle:XE',"us01","us01")))
{
  die ("Failed to connect to database: " . DBI-&gt;errstr);
};

$sql =
  "UPDATE driver ".
  "SET points = points + ? ".
  "WHERE key = ?";

if (!($sth = $dbh-&gt;prepare ($sql)))
{
  die ("Failed to prepare statement: " . DBI-&gt;errstr);
};

$sth-&gt;bind_param (1,25);
$sth-&gt;bind_param (2,1);

$sth-&gt;execute;

$dbh-&gt;disconnect;</pre>
<p>In the above example bind parameters have been used to specify the number of points and also the primary key of the row to be updated.</p>
<p>Assume we want to know how many points Vettel has now scored. To avoid selecting the row we can specify the RETURNING clause to the UPDATE statement</p>
<pre>use DBI;

if (!($dbh = DBI-&gt;connect ('DBI:Oracle:XE',"us01","us01")))
{
  die ("Failed to connect to database: " . DBI-&gt;errstr);
};

$sql =
  "UPDATE driver ".
  "SET points = points + ? ".
  "WHERE key = ? ".
  "RETURNING points INTO ?";

if (!($sth = $dbh-&gt;prepare ($sql)))
{
  die ("Failed to prepare statement: " . DBI-&gt;errstr);
};

$points = 0;

$sth-&gt;bind_param (1,25);
$sth-&gt;bind_param (2,1);
$sth-&gt;bind_param_inout (3,\$points,SQL_NUMERIC);

$sth-&gt;execute;

printf ("Value = %d\n",$points);

$dbh-&gt;disconnect;</pre>
<p>In the above example a call to <i>bind_param_inout</i> has been added to return the total number of points following execution of the UPDATE statement. In this case the script generates the following output:</p>
<pre>Value = 399</pre>
<h3>DELETE statements</h3>
<p>DELETE statements can use literal values or bind variables. For example:</p>
<pre>use DBI;

if (!($dbh = DBI-&gt;connect ('DBI:Oracle:XE',"us01","us01")))
{
  die ("Failed to connect to database: " . DBI-&gt;errstr);
};

$sql =
  "DELETE FROM driver ".
  "WHERE key = ?";

if (!($sth = $dbh-&gt;prepare ($sql)))
{
  die ("Failed to prepare statement: " . DBI-&gt;errstr);
};

$sth-&gt;bind_param (1,8);

$sth-&gt;execute;

$dbh-&gt;disconnect;</pre>
<p>In the above example the <i>bind_param</i> call is used to set the bind variable value to 8.</p>
<h3>Transactions</h3>
<p>The above examples do not include commit or rollback calls. This is because, by default, Perl DBI enables automatic commits. Few Oracle users will desire this behaviour. To disable automatic commits set the <i>AutoCommit</i> variable in the connection handle to 0. For example:</p>
<pre>$dbh-&gt;{AutoCommit} = 0;</pre>
<p>When automatic commits have been disabled, to commit a transaction use</p>
<pre>$dbh-&gt;commit;</pre>
<p>To rollback the transaction use:</p>
<pre>$dbh-&gt;rollback;</pre>
<p>The following script disables automatic commits and then explicitly specifies a commit at the end of the transaction:</p>
<pre>use DBI;

if (!($dbh = DBI-&gt;connect ('DBI:Oracle:XE',"us01","us01")))
{
  die ("Failed to connect to database: " . DBI-&gt;errstr);
};

$dbh-&gt;{AutoCommit} = 0;

$sql = "INSERT INTO driver (name,team,points) VALUES (?,?,?)";

if (!($sth = $dbh-&gt;prepare ($sql)))
{
  die ("Failed to prepare statement: " . DBI-&gt;errstr);
};

$sth-&gt;bind_param (1,'Michael Schumacher');
$sth-&gt;bind_param (2,'Mercedes');
$sth-&gt;bind_param (3,70);

$sth-&gt;execute;

$dbh-&gt;commit;

$dbh-&gt;disconnect;</pre>
<p>The <i>AutoCommit</i> parameter is set after the connection has been created and before the first DML statement.</p>
<p>The <i>commit</i> call is executed before the session is disconnected.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/juliandyke.wordpress.com/253/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/juliandyke.wordpress.com/253/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/juliandyke.wordpress.com/253/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/juliandyke.wordpress.com/253/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/juliandyke.wordpress.com/253/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/juliandyke.wordpress.com/253/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/juliandyke.wordpress.com/253/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/juliandyke.wordpress.com/253/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/juliandyke.wordpress.com/253/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/juliandyke.wordpress.com/253/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/juliandyke.wordpress.com/253/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/juliandyke.wordpress.com/253/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/juliandyke.wordpress.com/253/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/juliandyke.wordpress.com/253/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=juliandyke.wordpress.com&amp;blog=12569785&amp;post=253&amp;subd=juliandyke&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://juliandyke.wordpress.com/2011/10/31/using-perl-dbi-with-oracle/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/22a8ad7674d16428224bf7cd8d41d488?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">juliandyke</media:title>
		</media:content>
	</item>
		<item>
		<title>More on DBMS_STREAMS_ADM.MAINTAIN_TABLES</title>
		<link>http://juliandyke.wordpress.com/2011/10/17/more-on-dbms_streams_adm-maintain_tables/</link>
		<comments>http://juliandyke.wordpress.com/2011/10/17/more-on-dbms_streams_adm-maintain_tables/#comments</comments>
		<pubDate>Mon, 17 Oct 2011 11:18:24 +0000</pubDate>
		<dc:creator>Julian Dyke</dc:creator>
				<category><![CDATA[Streams]]></category>

		<guid isPermaLink="false">http://juliandyke.wordpress.com/?p=247</guid>
		<description><![CDATA[I recently worked with a Streams customer who needed to configure Streams dynamically as new tables were added. Unfortunately their schema design contained bi-directional, uni-directional and unreplicated tables so more obvious option of calling the MAINTAIN_SCHEMA procedure in DBMS_STREAMS_ADM was not available, at least in the short term. Therefore I decided to investigate the MAINTAIN_TABLES [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=juliandyke.wordpress.com&amp;blog=12569785&amp;post=247&amp;subd=juliandyke&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>I recently worked with a Streams customer who needed to configure Streams dynamically as new tables were added. Unfortunately their schema design contained bi-directional, uni-directional and unreplicated tables so more obvious option of calling the MAINTAIN_SCHEMA procedure in DBMS_STREAMS_ADM was not available, at least in the short term.</p>
<p>Therefore I decided to investigate the MAINTAIN_TABLES procedure which is also in the DBMS_STREAMS_ADM package. MAINTAIN_TABLE sets up Streams replication for one or more tables. I have used this procedure a few times in the past. I like it because it eliminates the need to code a series of fiddly steps including DataPump replication of the table from source to target database. However, Streams replication already existed for a number of tables and therefore any use of MAINTAIN_TABLES needed to complement this configuration.</p>
<h3>Prerequisites</h3>
<p>There are a number of pre-requisites that should be observed when using MAINTAIN_TABLES. Both source and target databases should have global names and database links using the global name should exist in both directions. I have been told in the past that the global names are not mandatory, just highly recommended. Also there should be a Streams administrative user (often STRMADMIN) in each database with appropriate privileges. Many customers create a dedicated tablespace for objects used by the Streams administrative user. If you are not using Automatic Memory Management then there are some minimum values for the STREAMS_POOL_SIZE parameter which should be configured.</p>
<h3>Documentation</h3>
<p>This site had an existing application running 10.2.0.4 and upgrading was not an option. The first thing I learned was that the MAINTAIN_TABLES API is not fully documented in Oracle 10.2. Although the procedure has more than 20 parameters, only five were mentioned at all in the documentation. In this situation I normally look at the most recent version of the manual only nine of the parameters are documented.</p>
<p>The poor documentation means a lot of guesswork is required when working with MAINTAIN_TABLES. The first question is whether MAINTAIN_TABLES can be used to add new tables incrementally to existing configurations or if it is just a single use procedure (like a wizard). Fortunately the answer is that it can be used incrementally without damaging the existing configuration. However extreme care needs to be taken if it is necessary to rollback the recoverable scripts as I noticed some steps were dropping existing captures, queues and propagations. Before using DBMS_RECOVERABLE_SCRIPT to rollback I therefore recommend you check each step carefully.</p>
<h3>Table Names</h3>
<p>Some care needs to be taken with the TABLE_NAMES parameter as the format is non-standard. The schema name must be included with the table name. For example:</p>
<pre>'SCHEMA1.TABLE1'</pre>
<p>My testing never extended to configuration of streams for a list of tables. I did read a couple of notes that suggested that MAINTAIN_TABLES was being used to configure Streams for tables in more than one schema, then it should be called individually for each schema &#8211; presumably a bug.</p>
<h3>Capture, Propagation and Apply</h3>
<p>If you already have a Streams configuration and you wish to use the existing capture, apply, propagation and underlying queues, you will need to do some research before invoking MAINTAIN_TABLES. If you do not explicitly specify any of these objects, MAINTAIN_TABLES will create new ones with system-defined names. MAINTAIN_TABLES is not great at working things out for itself; if you just specify a capture name it does try to figure out the capture queue table name etc, it just creates new ones again with system-defined names. So in Oracle 10.2 at least you need to tell it everything.</p>
<p>You can check the names of existing objects using:</p>
<pre>SELECT capture_name FROM dba_capture;

SELECT propagation_name FROM dba_propagation;

SELECT apply_name FROM dba_apply;

SELECT owner, queue_table FROM dba_queue_tables;

SELECT owner,name FROM dba_queues;</pre>
<p>If you make a mistake, you can drop any incorrectly created objects. However, this can be quite time-consuming as MAINTAIN_TABLES can create around 50 new objects even for a trivial table.</p>
<p>Capture, propagation and apply can be dropped using:</p>
<pre>DBMS_CAPTURE_ADM.drop_capture
(
    capture_name =&gt; &lt;capture_name&gt;,
    drop_unused_rule_sets =&gt; TRUE
);

DBMS_PROPAGATION_ADM.drop_propagation
(
    propagation_name =&gt; &lt;propagation_name&gt;,
    drop_unused_rule_sets =&gt; TRUE
);

DBMS_APPLY_ADM.drop_apply
(
    apply_name =&gt; &lt;apply_name&gt;,
    drop_unused_rule_sets =&gt; TRUE
);</pre>
<p>In each of the above examples I have set DROP_UNUSED_RULE_SETS to TRUE. MAINTAIN_TABLES creates a handful of rules for each object. Each rule is an individual data dictionary object and using the above parameter is the simplest way to clean up redundant ones.</p>
<p>Normally when a queue table is dropped, any queues supported by that table are also dropped. So the following can be used to drop a queue table and all of its queues:</p>
<pre>DBMS_AQADM.DROP_QUEUE_TABLE
(
    queue_table =&gt; &lt;queue_table_name&gt;,
    force =&gt; TRUE
);</pre>
<p>If you need to drop individual queues use:</p>
<pre>DBMS_AQADM.DROP_QUEUE
(
    queue_name =&gt; &lt;queue_name&gt;
);</pre>
<h3>Instantiation</h3>
<p>There are two ways to instantiate the table in the target database; using a Data Pump dump file and using Data Pump across the network. For large files you may want to use the dump file; for small or newly created tables the network option is easier.</p>
<p>The instantiation method is controlled by the INSTANTIATION parameter. To use the network set this parameter to DBMS_STREAMS_ADM.INSTANTIATION_TABLE_NETWORK. The default value is DBMS_STREAMS_ADM.INSTANTIATION_TABLE which performs a full Data Pump export on the source and a full Data Pump import on the target. Note that if the table already exists on the target then it will not be modified.</p>
<p>One issue that I hit with instantiation was with tablespace names. After MAINTAIN_TABLES failed to create a table in the target database, I discovered that a tablespace name had been explicitly specified in the CREATE TABLE statement. The only way around this was to create a new tablespace with the correct name in the target database.</p>
<p>Note that there is a PROPAGATION_NAME parameter, but there is no parameter for the propagation owner. In my example a propagation already existed for the Streams administrative user. However if I called MAINTAIN_TABLES for any other user it would create a new propagation together with the underlying objects that was owned by that user. From this I conclude that in most cases it probably only makes sense for the Streams administrative user to call MAINTAIN_TABLES.</p>
<h3>Scripts</h3>
<p>The PERFORM_ACTIONS parameter specifies whether or not MAINTAIN_TABLES should actually make changes to the database or just generate a script listing the actions it would perform. Beware the default value for this parameter is TRUE (implement changes).</p>
<p>If PERFORM_ACTIONS is set to FALSE then you should also specify a SCRIPT_NAME and a SCRIPT_DIRECTORY_OBJECT. The latter is just an Oracle directory object &#8211; I used EXPDP_DIR which already existed.</p>
<p>Experience now suggests that the best way of working with MAINTAIN_TABLES is to set PERFORM_ACTIONS=FALSE in order to generate a script which can be inspected manually. When you are happy with this script, run MAINTAIN_TABLES again with the same parameters and PERFORM_ACTIONS=TRUE. You could alternatively run the script manually. However you would not benefit from the recoverable scripts feature which does allow simple errors to be detected and resolved.</p>
<p>A typical call to MAINTAIN_TABLES to generate a script follows</p>
<pre>BEGIN
  dbms_streams_adm.maintain_tables 
  (
    table_names =&gt; 'SCHEMA1.TABLE1',
    source_directory_object =&gt; NULL,
    destination_directory_object =&gt; NULL,
    source_database =&gt; 'EAST',
    destination_database =&gt; 'WEST',
    perform_actions =&gt; FALSE,
    script_name =&gt; 'table1.txt',
    script_directory_object =&gt; 'EXPDP_DIR',
    capture_name =&gt; 'CAPTURE1',
    capture_queue_table =&gt; 'CAPTURE_QUEUE_TABLE',
    capture_queue_name =&gt; 'CAPTURE_QUEUE',
    capture_queue_user =&gt; 'STRMADMIN',
    propagation_name =&gt; 'PROPAGATION1',
    apply_name =&gt; 'APPLY1',
    apply_queue_table =&gt; 'APPLY_QUEUE_TABLE',
    apply_queue_name =&gt; 'APPLY_QUEUE',
    apply_queue_user =&gt; 'STRMADMIN',
    include_ddl =&gt; TRUE,
    instantiation =&gt; DBMS_STREAMS_ADM.INSTANTIATION_TABLE_NETWORK
  );
END;
/</pre>
<p>The script generated by MAINTAIN_TABLES is long, but highly instructive.</p>
<pre>SET ECHO ON
SET VERIFY OFF
WHENEVER SQLERROR EXIT SQL.SQLCODE;
-------------------------------------------------------------------
-- get TNSNAME and streams admin user details for both the databases
--------------------------------------------------------------------
PROMPT
PROMPT 'Enter TNS Name of site 1 as parameter 1:'
DEFINE db1                 = &amp;1
PROMPT
PROMPT 'Enter streams admin username for site 1 as parameter 2:'
DEFINE strm_adm_db1        = &amp;2
PROMPT
PROMPT 'Enter streams admin password for site 1 as parameter 3:'
DEFINE strm_adm_pwd_db1    = &amp;3
PROMPT
PROMPT 'Enter TNS Name of site 2 as parameter 4:'
DEFINE db2                 = &amp;4
PROMPT
PROMPT 'Enter streams admin username for site 2 as parameter 5:'
DEFINE strm_adm_db2        = &amp;5
PROMPT
PROMPT 'Enter streams admin password for site 2 as parameter 6:'
DEFINE strm_adm_pwd_db2    = &amp;6
-- connect as streams administrator to site 1
PROMPT Connecting as streams administrator to site 1
CONNECT &amp;strm_adm_db1/&amp;strm_adm_pwd_db1@&amp;db1
--
-- Add supplemental log group for table "SCHEMA1"."TABLE1"
--
BEGIN
  EXECUTE IMMEDIATE 'ALTER TABLE "SCHEMA1"."TABLE1" ADD SUPPLEMENTAL LOG DATA (PRIMARY KEY, FOREIGN KEY, UNIQUE INDEX) COLUMNS';
EXCEPTION WHEN OTHERS THEN
  IF sqlcode = -32588 THEN NULL;  -- Logging attribute exists
  ELSE RAISE;
  END IF;
END;
/
--
-- Set up queue "STRMADMIN"."CAPTURE_QUEUE"
--
BEGIN
  dbms_streams_adm.set_up_queue(
    queue_table =&gt; '"STRMADMIN"."CAPTURE_QUEUE_TABLE"',
    storage_clause =&gt; NULL,
    queue_name =&gt; '"STRMADMIN"."CAPTURE_QUEUE"',
    queue_user =&gt; '"STRMADMIN"');
END;
/
--
-- PROPAGATE changes for table "SCHEMA1"."TABLE1"
--
DECLARE
  version_num            NUMBER := 0;
  release_num            NUMBER := 0;
  pos                    NUMBER;
  initpos                NUMBER;
  q2q                    BOOLEAN;
  stmt                   VARCHAR2(100);
  ver                    VARCHAR2(30);
  compat                 VARCHAR2(30); 
BEGIN
  BEGIN
    stmt := 'BEGIN dbms_utility.db_version@WEST(:ver , :compat); END;';
    EXECUTE IMMEDIATE stmt USING OUT ver, OUT compat;
    -- Extract version number
    initpos := 1;
    pos := INSTR(compat, '.', initpos, 1);
    IF pos &gt; 0 THEN
      version_num := TO_NUMBER(SUBSTR(compat, initpos, pos - initpos));
      initpos := pos + 1;
      -- Extract release number
      pos := INSTR(compat, '.', initpos, 1);
      IF pos &gt; 0 THEN
        release_num := TO_NUMBER(SUBSTR(compat, initpos,
                                   pos - initpos));
        initpos := pos + 1;
      ELSE
        release_num := TO_NUMBER(SUBSTR(compat, initpos));
      END IF;
    ELSE
      version_num := TO_NUMBER(SUBSTR(compat, initpos));
    END IF;
    -- use q2q propagation if compatibility &gt;= 10.2
    IF version_num &gt; 10 OR
       (version_num = 10 AND release_num &gt;=2) THEN
      q2q := TRUE;
    ELSE
      q2q := FALSE;
    END IF;
  EXCEPTION WHEN OTHERS THEN
    q2q := FALSE;
  END;
  dbms_streams_adm.add_table_propagation_rules(
    table_name =&gt; '"SCHEMA1"."TABLE1"',
    streams_name =&gt; '"PROPAGATION1"',
    source_queue_name =&gt; '"STRMADMIN"."CAPTURE_QUEUE"',
    destination_queue_name =&gt; '"STRMADMIN"."APPLY_QUEUE"@WEST',
    include_dml =&gt; TRUE,
    include_ddl =&gt; TRUE,
    include_tagged_lcr =&gt; TRUE,
    source_database =&gt; 'EAST',
    inclusion_rule =&gt; TRUE,
    and_condition =&gt; NULL,
    queue_to_queue =&gt; q2q);
END;
/
--
-- Disable propagation. Enable after destination has been setup
--
DECLARE
  q2q       VARCHAR2(10);
  destn_q   VARCHAR2(65);
BEGIN
  SELECT queue_to_queue INTO q2q
  FROM dba_propagation
  WHERE source_queue_owner = 'STRMADMIN' AND
        source_queue_name = 'CAPTURE_QUEUE' AND
        destination_queue_owner = 'STRMADMIN' AND
        destination_queue_name = 'APPLY_QUEUE' AND
        destination_dblink = 'WEST';
  IF q2q = 'TRUE' THEN
    destn_q := '"STRMADMIN"."APPLY_QUEUE"';
  ELSE
    destn_q := NULL;
  END IF;
  dbms_aqadm.disable_propagation_schedule(
    queue_name =&gt; '"STRMADMIN"."CAPTURE_QUEUE"',
    destination =&gt; 'WEST',
    destination_queue =&gt; destn_q);
EXCEPTION WHEN OTHERS THEN
  IF sqlcode = -24065 THEN NULL;  -- propagation already disabled
  ELSE RAISE;
  END IF;
END;
/
--
-- CAPTURE changes for table "SCHEMA1"."TABLE1"
--
DECLARE
  compat         VARCHAR2(512);
  initpos        NUMBER;
  pos            NUMBER;
  version_num    NUMBER;
  release_num    NUMBER;
  compat_func    VARCHAR2(65);
  get_compatible VARCHAR2(4000);
BEGIN
  SELECT value INTO compat
  FROM v$parameter
  WHERE name = 'compatible';
  -- Extract version number
  initpos := 1;
  pos := INSTR(compat, '.', initpos, 1);
  IF pos &gt; 0 THEN
    version_num := TO_NUMBER(SUBSTR(compat, initpos, pos - initpos));
    initpos := pos + 1;
    -- Extract release number
    pos := INSTR(compat, '.', initpos, 1);
    IF pos &gt; 0 THEN
      release_num := TO_NUMBER(SUBSTR(compat, initpos, pos - initpos));
      initpos := pos + 1;
    ELSE
      release_num := TO_NUMBER(SUBSTR(compat, initpos));
    END IF;
  END IF;
  IF version_num &lt; 10 THEN
    compat_func := 'dbms_streams.compatible_9_2';
  ELSIF version_num = 10 THEN
    IF release_num &lt; 2 THEN
      compat_func := 'dbms_streams.compatible_10_1';
    ELSE
      compat_func := 'dbms_streams.compatible_10_2';
    END IF;
  ELSE
    compat_func := 'dbms_streams.compatible_10_2';
  END IF;
  get_compatible := ':lcr.get_compatible() &lt;= '||compat_func;
  dbms_streams_adm.add_table_rules(
    table_name =&gt; '"SCHEMA1"."TABLE1"',
    streams_type =&gt; 'CAPTURE',
    streams_name =&gt; '"CAPTURE_STREAM"',
    queue_name =&gt; '"STRMADMIN"."CAPTURE_QUEUE"',
    include_dml =&gt; TRUE,
    include_ddl =&gt; TRUE,
    include_tagged_lcr =&gt; TRUE,
    source_database =&gt; 'EAST',
    inclusion_rule =&gt; TRUE,
    and_condition =&gt; get_compatible);
END;
/
--
-- Start capture process CAPTURE_STREAM
--
BEGIN
  dbms_capture_adm.start_capture(
    capture_name =&gt; '"CAPTURE_STREAM"');
EXCEPTION WHEN OTHERS THEN
  IF sqlcode = -26666 THEN NULL;  -- CAPTURE process already running
  ELSE RAISE;
  END IF;
END;
/
-- connect as streams administrator to site 2
PROMPT Connecting as streams administrator to site 2
CONNECT &amp;strm_adm_db2/&amp;strm_adm_pwd_db2@&amp;db2
--
-- Datapump TABLE MODE IMPORT (NETWORK)
--
DECLARE
  h1                NUMBER;       -- data pump job handle
  name_expr_list    VARCHAR2(32767); -- for metadata_filter
  object_name       dbms_utility.uncl_array; -- object names
  cnt               NUMBER;
  object_owner      VARCHAR2(30); -- owner
  job_state         VARCHAR2(30); -- job state
  status            ku$_Status; -- data pump status
  job_not_exist     exception;
  pragma            exception_init(job_not_exist, -31626);
BEGIN
  object_name(1) := 'TABLE1';
  object_owner := 'SCHEMA1';
  FOR idx IN 1..1LOOP
    SELECT COUNT(1) INTO cnt FROM all_tables@WEST
      WHERE owner = object_owner AND table_name = object_name(idx);
    -- table does not exist locally, need instantiation
    IF cnt = 0 THEN
      IF name_expr_list IS NULL THEN
        name_expr_list := '(';
      ELSE
        name_expr_list := name_expr_list ||',';
      END IF;
      name_expr_list := name_expr_list||''''||object_name(idx)||'''';
    END IF;
  ENDLOOP;
  IF name_expr_list IS NOT NULL THEN
    name_expr_list := name_expr_list || ')';
  ELSE
    COMMIT;
    RETURN;
  END IF;
  h1 := dbms_datapump.open(operation=&gt;'IMPORT',job_mode=&gt;'TABLE',
    remote_link=&gt;'EAST',
    job_name=&gt;NULL, version=&gt;'COMPATIBLE');
  dbms_datapump.metadata_filter(
    handle=&gt;h1,
    name=&gt;'NAME_EXPR',
    value=&gt;'IN'||name_expr_list);
  dbms_datapump.metadata_filter(
    handle=&gt;h1,
    name=&gt;'SCHEMA_EXPR',
    value=&gt;'IN'||'(''SCHEMA1'')');
  dbms_datapump.start_job(h1);
  job_state := 'UNDEFINED';
  BEGIN
    WHILE (job_state != 'COMPLETED') AND (job_state != 'STOPPED') LOOP
      status := dbms_datapump.get_status(
        handle =&gt; h1,
        mask =&gt; dbms_datapump.ku$_status_job_error +
                dbms_datapump.ku$_status_job_status +
                dbms_datapump.ku$_status_wip,
        timeout =&gt; -1);
      job_state := status.job_status.state;
      dbms_lock.sleep(10);
    ENDLOOP;
  EXCEPTION WHEN job_not_exist THEN
    dbms_output.put_line('job finished');
  END;
  COMMIT;
EXCEPTION WHEN OTHERS THEN
  ROLLBACK;
  RAISE;
END;
/
--
-- Set up queue "STRMADMIN"."APPLY_QUEUE"
--
BEGIN
  dbms_streams_adm.set_up_queue(
    queue_table =&gt; '"STRMADMIN"."APPLY_QUEUE_TABLE"',
    storage_clause =&gt; NULL,
    queue_name =&gt; '"STRMADMIN"."APPLY_QUEUE"',
    queue_user =&gt; '"STRMADMIN"');
END;
/
--
-- APPLY changes for table "SCHEMA1"."TABLE1"
--
DECLARE
  compat         VARCHAR2(512);
  initpos        NUMBER;
  pos            NUMBER;
  version_num    NUMBER;
  release_num    NUMBER;
  compat_func    VARCHAR2(65);
  get_compatible VARCHAR2(4000);
BEGIN
  SELECT value INTO compat
  FROM v$parameter
  WHERE name = 'compatible';
  -- Extract version number
  initpos := 1;
  pos := INSTR(compat, '.', initpos, 1);
  IF pos &gt; 0 THEN
    version_num := TO_NUMBER(SUBSTR(compat, initpos, pos - initpos));
    initpos := pos + 1;
    -- Extract release number
    pos := INSTR(compat, '.', initpos, 1);
    IF pos &gt; 0 THEN
      release_num := TO_NUMBER(SUBSTR(compat, initpos, pos - initpos));
      initpos := pos + 1;
    ELSE
      release_num := TO_NUMBER(SUBSTR(compat, initpos));
    END IF;
  END IF;
  IF version_num &lt; 10 THEN
    compat_func := 'dbms_streams.compatible_9_2';
  ELSIF version_num = 10 THEN
    IF release_num &lt; 2 THEN
      compat_func := 'dbms_streams.compatible_10_1';
    ELSE
      compat_func := 'dbms_streams.compatible_10_2';
    END IF;
  ELSE
    compat_func := 'dbms_streams.compatible_10_2';
  END IF;
  get_compatible := ':lcr.get_compatible() &lt;= '||compat_func;
  dbms_streams_adm.add_table_rules(
    table_name =&gt; '"SCHEMA1"."TABLE1"',
    streams_type =&gt; 'APPLY',
    streams_name =&gt; '"APPLY_STREAM"',
    queue_name =&gt; '"STRMADMIN"."APPLY_QUEUE"',
    include_dml =&gt; TRUE,
    include_ddl =&gt; TRUE,
    include_tagged_lcr =&gt; TRUE,
    source_database =&gt; 'EAST',
    inclusion_rule =&gt; TRUE,
    and_condition =&gt; get_compatible);
END;
/
--
-- Get tag value to be used for Apply
--
DECLARE
  found   BINARY_INTEGER := 0;
  tag_num NUMBER;
BEGIN
  -- Use the apply object id as the tag
  SELECT o.object_id INTO tag_num
  FROM dba_objects o
  WHERE o.object_name= 'APPLY_STREAM' AND
        o.object_type='APPLY';
 LOOP
    BEGIN
      found := 0;
      SELECT 1 INTO found FROM dba_apply
      WHERE apply_name != 'APPLY_STREAM' AND
            apply_tag = hextoraw(tag_num);
    EXCEPTION WHEN no_data_found THEN
      EXIT;
    END;
    EXIT WHEN (found = 0);
    tag_num := tag_num + 1;
  ENDLOOP;
  -- alter apply
  dbms_apply_adm.alter_apply(
    apply_name =&gt; '"APPLY_STREAM"',
    apply_tag =&gt; hextoraw(tag_num));
END;
/
--
-- Start apply process APPLY_STREAM
--
BEGIN
  dbms_apply_adm.start_apply(
    apply_name =&gt; '"APPLY_STREAM"');
EXCEPTION WHEN OTHERS THEN
  IF sqlcode = -26666 THEN NULL;  -- APPLY process already running
  ELSE RAISE;
  END IF;
END;
/
-- connect as streams administrator to site 1
PROMPT Connecting as streams administrator to site 1
CONNECT &amp;strm_adm_db1/&amp;strm_adm_pwd_db1@&amp;db1
--
-- Enable propagation schedule for "STRMADMIN"."CAPTURE_QUEUE"
-- to WEST
--
DECLARE
  q2q       VARCHAR2(10);
  destn_q   VARCHAR2(65);
BEGIN
  SELECT queue_to_queue INTO q2q
  FROM dba_propagation
  WHERE source_queue_owner = 'STRMADMIN' AND
        source_queue_name = 'CAPTURE_QUEUE' AND
        destination_queue_owner = 'STRMADMIN' AND
        destination_queue_name = 'APPLY_QUEUE' AND
        destination_dblink = 'WEST';
  IF q2q = 'TRUE' THEN
    destn_q := '"STRMADMIN"."APPLY_QUEUE"';
  ELSE
    destn_q := NULL;
  END IF;
  dbms_aqadm.enable_propagation_schedule(
    queue_name =&gt; '"STRMADMIN"."CAPTURE_QUEUE"',
    destination =&gt; 'WEST',
    destination_queue =&gt; destn_q);
EXCEPTION WHEN OTHERS THEN
  IF sqlcode = -24064 THEN NULL; -- propagation already enabled
  ELSE RAISE;
  END IF;
END;
/</pre>
<p>When you are happy with the actions described in the script, you can use MAINTAIN_TABLES to update the objects using:</p>
<pre>BEGIN
  dbms_streams_adm.maintain_tables 
  (
    table_names =&gt; 'SCHEMA1.TABLE1',
    source_directory_object =&gt; NULL,
    destination_directory_object =&gt; NULL,
    source_database =&gt; 'EAST',
    destination_database =&gt; 'WEST',
    perform_actions =&gt; TRUE,
    capture_name =&gt; 'CAPTURE1',
    capture_queue_table =&gt; 'CAPTURE_QUEUE_TABLE',
    capture_queue_name =&gt; 'CAPTURE_QUEUE',
    capture_queue_user =&gt; 'STRMADMIN',
    propagation_name =&gt; 'PROPAGATION1',
    apply_name =&gt; 'APPLY1',
    apply_queue_table =&gt; 'APPLY_QUEUE_TABLE',
    apply_queue_name =&gt; 'APPLY_QUEUE',
    apply_queue_user =&gt; 'STRMADMIN',
    include_ddl =&gt; TRUE,
    instantiation =&gt; DBMS_STREAMS_ADM.INSTANTIATION_TABLE_NETWORK
  );
END;
/</pre>
<h3>Calling MAINTAIN_TABLES from a PL/SQL procedure</h3>
<p>Whilst I was initially testing MAINTAIN_TABLES I used anonymous PL/SQL blocks to invoke the procedure. However the customer requirement was to configure Streams from within a package. As a first step I therefore attempted to encapsulate the call to MAINTAIN_TABLES in a PL/SQL procedure. I spent several hours trying to get this simple change to work, but once the call was included in the procedure it failed repeatedly with</p>
<pre>
ORA-00942: table or view does not exist
</pre>
<p>I never solved this. Event 10046 level 4 trace suggested the API was encountering a parse error when attempting to select from DBA_OBJECTS. I tried copying all parameters to temporary variables, using IN OUT parameters and setting PLSQL_OPTIMIZER_LEVEL to 0, but none were successful.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/juliandyke.wordpress.com/247/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/juliandyke.wordpress.com/247/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/juliandyke.wordpress.com/247/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/juliandyke.wordpress.com/247/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/juliandyke.wordpress.com/247/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/juliandyke.wordpress.com/247/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/juliandyke.wordpress.com/247/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/juliandyke.wordpress.com/247/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/juliandyke.wordpress.com/247/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/juliandyke.wordpress.com/247/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/juliandyke.wordpress.com/247/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/juliandyke.wordpress.com/247/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/juliandyke.wordpress.com/247/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/juliandyke.wordpress.com/247/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=juliandyke.wordpress.com&amp;blog=12569785&amp;post=247&amp;subd=juliandyke&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://juliandyke.wordpress.com/2011/10/17/more-on-dbms_streams_adm-maintain_tables/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/22a8ad7674d16428224bf7cd8d41d488?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">juliandyke</media:title>
		</media:content>
	</item>
		<item>
		<title>What&#8217;s wrong with Oracle? The CREATE SPFILE statement</title>
		<link>http://juliandyke.wordpress.com/2011/07/11/whats-wrong-with-oracle-the-create-spfile-statement/</link>
		<comments>http://juliandyke.wordpress.com/2011/07/11/whats-wrong-with-oracle-the-create-spfile-statement/#comments</comments>
		<pubDate>Mon, 11 Jul 2011 09:07:09 +0000</pubDate>
		<dc:creator>Julian Dyke</dc:creator>
				<category><![CDATA[Grid Infrastructure / RAC]]></category>

		<guid isPermaLink="false">http://juliandyke.wordpress.com/?p=242</guid>
		<description><![CDATA[Over the past couple of years I have implemented Oracle Data Guard physical standby around a dozen times. Every time I fine-tune the procedure a little more &#8211; either eliminating an unnecessary step or discovering some new and often undocumented Oracle functionality.  My preference when building a standby environment is to keep the design as [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=juliandyke.wordpress.com&amp;blog=12569785&amp;post=242&amp;subd=juliandyke&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Over the past couple of years I have implemented Oracle Data Guard physical standby around a dozen times. Every time I fine-tune the procedure a little more &#8211; either eliminating an unnecessary step or discovering some new and often undocumented Oracle functionality. </p>
<p>My preference when building a standby environment is to keep the design as symmetrical as possible. This preference mainly affects naming conventions. </p>
<p>The DB_NAME parameter specifies the name of the database and is the same at all locations. In this example the DB_NAME parameter is PROD. </p>
<p>The DB_UNIQUE_NAME parameter allows a location specific alias to be created for a database. I always try to avoid using names indicative of the role such as PROD_PRIMARY and PROD_STANDBY. These names work well until a switchover is performed at which point the switch-back operation can become really confusing and difficult to document. Therefore I usually try to use geographical values for the DB_UNIQUE_NAME parameter such as PROD_EAST and PROD_WEST. The following discussion uses these values. </p>
<p>The DB_UNIQUE_NAME parameter defaults to the value of the DB_NAME parameter. This is useful when you are creating a new database; fewer parameters to specify; but is not so efficient if that database will become part of a standby configuration. </p>
<p>The DB_UNIQUE_NAME parameter is actually used by Oracle in a number of places. It is used to specify</p>
<ul>
<li>the database name in the OCR;</li>
<li>the database directory name within ASM disk groups</li>
<li>the diagnostic directory name. </li>
</ul>
<p>For example if I use DBCA to create a database using the DB_NAME of PROD, the DB_UNIQUE_NAME will default to PROD and will result in the following:</p>
<ul>
<li>The database will be called PROD in the OCR</li>
<li>The database directory name within ASM will be PROD e.g. +DATA/PROD/ and +FRA/PROD/</li>
<li>The diagnostic location will be $ORACLE_BASE/diag/rdbms/prod </li>
</ul>
<p>Under normal conditions this is probably what you want. However for a Data Guard standby database you may however, want the location specific value for the DB_UNIQUE_NAME in which case:</p>
<ul>
<li>The database will be called PROD_EAST in the OCR (the database name is still PROD)</li>
<li>The database directory name within ASM will be PROD_EAST e.g. +DATA/PROD_EAST/ and +FRA/PROD_EAST/</li>
<li>The diagnostic location will be $ORACLE_BASE/diag/rdbms/prod_east</li>
</ul>
<p>It is easy enough to modify the DB_UNIQUE_NAME parameter after the database has been created, but then you are left with a few problems.</p>
<ul>
<li>The database will be called PROD in the OCR. The original data files etc will have been created in +DATA/PROD</li>
<li>Any archived redo logs will be in +FRA/PROD</li>
<li>The diagnostic location is $ORACLE_BASE/diag/rdbms/prod</li>
</ul>
<p>So what happens when we rename DB_UNIQUE_NAME from PROD to PROD_EAST?</p>
<ul>
<li>SRVCTL does not allow databases to be renamed so the entries for the database and instances must be deleted and recreated using the new name of PROD_EAST.</li>
<li>If we really care then we need to copy all the data files and redo logs from +DATA/PROD to +DATA/PROD_EAST and to update the control file</li>
<li>I have never figured out how to relocate files in the FRA; it may not even be possible.</li>
<li>The diagnostic location is created automatically as $ORACLE_BASE/diag/rdbms/prod_east,  but the previous location will still exist and contains some useful files notably the alert.log generated during database creation.</li>
</ul>
<p>It is possible to modify the DB_UNIQUE_NAME parameter, but the result is a bit messy; not so good for standards-conscious organizations.</p>
<p>How can you work around this? Well the answer is really simple; set the DB_UNIQUE_NAME parameter during database creation. The parameter can be set in the Initialization Parameters page in the DBCA.</p>
<p>However, setting the DB_UNIQUE_NAME parameter in the DBCA will probably result in a failure during database creation. The cause of the failure is the CREATE SPFILE file which is one of the first statements executed during database creation.</p>
<p>If the DB_UNIQUE_NAME is PROD_EAST then by default the CREATE SPFILE statement will attempt to create an SPFILE called +DATA/PROD_EAST/spfilePROD_EAST.ora. However at this stage the +DATA/PROD_EAST directory will not exist within the +DATA disk group and the statement will fail. This appears to be an issue with the CREATE SPFILE statement; other statements such as CREATE DATABASE appear to create the directory if it does not already exist.</p>
<p>You can workaround this problem by creating the directory manually using SQL*Plus or ASMCMD. For example:</p>
<pre>$ asmcmd mkdir +DATA/PROD_EAST</pre>
<p>If the directory is created before DBCA is executed then the database should be created successfully with the correct names in the OCR, ASM and diagnostic areas.</p>
<p>However, this should really not be necessary; I don&#8217;t have access to the source code, but I&#8217;m guessing that it would be trivial for Oracle to fix the CREATE SPFILE to create intermediate ASM directories if they do not exist. So I am hoping Oracle product manager will read this and fix the problem in the next patch set&#8230;.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/juliandyke.wordpress.com/242/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/juliandyke.wordpress.com/242/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/juliandyke.wordpress.com/242/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/juliandyke.wordpress.com/242/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/juliandyke.wordpress.com/242/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/juliandyke.wordpress.com/242/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/juliandyke.wordpress.com/242/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/juliandyke.wordpress.com/242/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/juliandyke.wordpress.com/242/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/juliandyke.wordpress.com/242/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/juliandyke.wordpress.com/242/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/juliandyke.wordpress.com/242/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/juliandyke.wordpress.com/242/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/juliandyke.wordpress.com/242/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=juliandyke.wordpress.com&amp;blog=12569785&amp;post=242&amp;subd=juliandyke&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://juliandyke.wordpress.com/2011/07/11/whats-wrong-with-oracle-the-create-spfile-statement/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/22a8ad7674d16428224bf7cd8d41d488?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">juliandyke</media:title>
		</media:content>
	</item>
		<item>
		<title>Oracle Database Registry Components</title>
		<link>http://juliandyke.wordpress.com/2011/06/19/oracle-database-registry-components/</link>
		<comments>http://juliandyke.wordpress.com/2011/06/19/oracle-database-registry-components/#comments</comments>
		<pubDate>Sun, 19 Jun 2011 10:34:29 +0000</pubDate>
		<dc:creator>Julian Dyke</dc:creator>
				<category><![CDATA[Grid Infrastructure / RAC]]></category>

		<guid isPermaLink="false">http://juliandyke.wordpress.com/?p=238</guid>
		<description><![CDATA[I recently developed a procedure to upgrade an Oracle 11.1 single instance database to Oracle 11.2 RAC. I remembered to add new redo threads, undo tablespaces, modify initialization parameters and to add the new database to the OCR. However, I forgot to run $ORACLE_HOME/rdbms/admin/catclust.sql which caused some errors in Enterprise Manager. To my knowledge, the [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=juliandyke.wordpress.com&amp;blog=12569785&amp;post=238&amp;subd=juliandyke&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>I recently developed a procedure to upgrade an Oracle 11.1 single instance database to Oracle 11.2 RAC. I remembered to add new redo threads, undo tablespaces, modify initialization parameters and to add the new database to the OCR. However, I forgot to run $ORACLE_HOME/rdbms/admin/catclust.sql which caused some errors in Enterprise Manager.</p>
<p>To my knowledge, the catclust.sql script has existed since Oracle 9.0.1. I think it was originally developed to maintain backward compatibility between OPS and RAC so that the same tools could operate on both products. It creates new views that adhere to new feature names such as GCS and GES, based on older views based on OPS concepts such as DLM.</p>
<p>In order to prevent a repetition during the production upgrade, I wanted to know if there was an easy way to determine whether or not catclust.sql had been run. Obviously I could check if the views created by this script already existed, but perhaps there was a better way.</p>
<p>Recent versions of Oracle include a database registry. This structure records which components have been installed in the database. There is one database registry for each database; not to be confused with the Oracle Cluster Registry (OCR).</p>
<p>The contents of the database registry are externalized a dictionary view called DBA_REGISTRY.  This view describes the components currently installed in the database and contains the following columns:</p>
<pre>SQL&gt; DESC dba_registry
Name             Type
---------------  -------------
COMP_ID          VARCHAR2(30)
COMP_NAME        VARCHAR2(255)
VERSION          VARCHAR2(30)
STATUS           VARCHAR2(11)
MODIFIED         VARCHAR2(20)
NAMESPACE        VARCHAR2(30)
CONTROL          VARCHAR2(30)
SCHEMA           VARCHAR2(30)
PROCEDURE        VARCHAR2(61)
STARTUP          VARCHAR2(8)
PARENT_ID        VARCHAR2(30)
OTHER_SCHEMAS    VARCHAR2(4000)</pre>
<p>In our example the database registry contained the following:</p>
<pre>COLUMN comp_id FORMAT A10
COLUMN comp_name FORMAT A35
COLUMN version FORMAT A14
SELECT comp_id,comp_name,version FROM dba_registry;
COMP_ID    COMP_NAME                           VERSION
---------- ----------------------------------- -------------
APEX       Oracle Application Express          3.2.1.00.12
EM         Oracle Enterprise Manager           11.2.0.2.0
ORDIM      Oracle Multimedia                   11.2.0.2.0
XDB        Oracle XML Database                 11.2.0.2.0
CONTEXT    Oracle Text                         11.2.0.2.0
EXF        Oracle Expression Filter            11.2.0.2.0
RUL        Oracle Rules Manager                11.2.0.2.0
OWM        Oracle Workspace Manager            11.2.0.2.0
CATALOG    Oracle Database Catalog Views       11.2.0.2.0
CATPROC    Oracle Database Packages and Types  11.2.0.2.0
JAVAVM     JServer JAVA Virtual Machine        11.2.0.2.0
XML        Oracle XDK                          11.2.0.2.0
CATJAVA    Oracle Database Java Packages       11.2.0.2.0
13 rows selected.</pre>
<p>After running $ORACLE_HOME/rdbms/admin/catclust.sql the registry now contained:</p>
<pre>SELECT comp_id,comp_name,version FROM dba_registry;
COMP_ID    COMP_NAME                           VERSION
---------- ----------------------------------- -------------
APEX       Oracle Application Express          3.2.1.00.12
EM         Oracle Enterprise Manager           11.2.0.2.0
ORDIM      Oracle Multimedia                   11.2.0.2.0
XDB        Oracle XML Database                 11.2.0.2.0
CONTEXT    Oracle Text                         11.2.0.2.0
EXF        Oracle Expression Filter            11.2.0.2.0
RUL        Oracle Rules Manager                11.2.0.2.0
OWM        Oracle Workspace Manager            11.2.0.2.0
CATALOG    Oracle Database Catalog Views       11.2.0.2.0
CATPROC    Oracle Database Packages and Types  11.2.0.2.0
JAVAVM     JServer JAVA Virtual Machine        11.2.0.2.0
XML        Oracle XDK                          11.2.0.2.0
CATJAVA    Oracle Database Java Packages       11.2.0.2.0
<strong>RAC Oracle Real Application Clusters 11.2.0.2.0</strong>
14 rows selected.</pre>
<p>So it is possible to verify whether or not components have been installed simply by querying the registry. Whilst useful for catclust.sql it is even more useful if you are auditing which components have been installed in each database. For example APEX has been installed by mistake in this database and the database upgrade might be a good opportunity to remove it.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/juliandyke.wordpress.com/238/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/juliandyke.wordpress.com/238/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/juliandyke.wordpress.com/238/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/juliandyke.wordpress.com/238/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/juliandyke.wordpress.com/238/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/juliandyke.wordpress.com/238/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/juliandyke.wordpress.com/238/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/juliandyke.wordpress.com/238/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/juliandyke.wordpress.com/238/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/juliandyke.wordpress.com/238/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/juliandyke.wordpress.com/238/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/juliandyke.wordpress.com/238/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/juliandyke.wordpress.com/238/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/juliandyke.wordpress.com/238/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=juliandyke.wordpress.com&amp;blog=12569785&amp;post=238&amp;subd=juliandyke&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://juliandyke.wordpress.com/2011/06/19/oracle-database-registry-components/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/22a8ad7674d16428224bf7cd8d41d488?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">juliandyke</media:title>
		</media:content>
	</item>
		<item>
		<title>Multipath errors for ASM devices in /var/log/messages</title>
		<link>http://juliandyke.wordpress.com/2011/06/19/multipath-errors-for-asm-devices-in-varlogmessages/</link>
		<comments>http://juliandyke.wordpress.com/2011/06/19/multipath-errors-for-asm-devices-in-varlogmessages/#comments</comments>
		<pubDate>Sun, 19 Jun 2011 09:36:49 +0000</pubDate>
		<dc:creator>Julian Dyke</dc:creator>
				<category><![CDATA[Grid Infrastructure / RAC]]></category>
		<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://juliandyke.wordpress.com/?p=233</guid>
		<description><![CDATA[We are currently running failover tests on a three node Linux x86-84 cluster running Oracle 11.2.0.2 with the 11.2.0.2.2 PSU. As part of this process we have been examining how each failure is recorded in the log files. One of the log files we have been monitoring is /var/log/messages.  When a node is shutdown messages [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=juliandyke.wordpress.com&amp;blog=12569785&amp;post=233&amp;subd=juliandyke&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>We are currently running failover tests on a three node Linux x86-84 cluster running Oracle 11.2.0.2 with the 11.2.0.2.2 PSU. As part of this process we have been examining how each failure is recorded in the log files. One of the log files we have been monitoring is /var/log/messages.</p>
<p> When a node is shutdown messages similar to the follow appear in /var/log/messages:</p>
<pre>Jun 14 19:19:38 server20 multipathd: uevent trigger error
Jun 14 19:19:38 server20 multipathd: asm!.asm_ctl_vbg3:
remove path (uevent)
Jun 14 19:19:38 server20 multipathd: asm!.asm_ctl_vbg3:
spurious uevent, path not in pathvec</pre>
<p> When the node is restarted further messages are written to /var/log/messages:</p>
<pre>Jun 14 19:19:38 server20 multipathd: uevent trigger error
Jun 14 19:19:38 server20 multipathd: asm!.asm_ctl_vbg0:
  add path (uevent)
Jun 14 19:19:38 server20 multipathd: asm!.asm_ctl_vbg0:
  failed to store path info </pre>
<p>Similar messages appear for a number of devices in our case including:</p>
<ul>
<li>ofsctl</li>
<li>asm_ctl_vbg0 to asm_ctl_vbg8</li>
<li>asm_ctl_vdbg</li>
<li>asm_ctl_vmb</li>
<li>asm_ctl_spec</li>
</ul>
<p>We found the ofsctl device very easily:</p>
<pre>[root@server20]# ls -l /dev/ofsctl
brw-rw-r-- 1 root oinstall 251, 0 Jun 14 19:19 /dev/ofsctl</pre>
<p>The remaining devices were harder to locate. They did not appear to be in /dev/asm.</p>
<pre>[root@server20]# ls -l /dev/asm
total 0</pre>
<p>However, on further inspection we discovered they had a &#8220;dot&#8221; prefix:</p>
<pre>[root@server20]# ls -la /dev/asm
total 0
drwxrwx---  2 root oinstall     280 Jun 14 19:19 .
drwxr-xr-x 17 root root        5680 Jun 14 19:19 ..
brwxrwx---  1 root oinstall 252,  0 Jun 14 19:19 .asm_ctl_spec
brwxrwx---  1 root oinstall 252, 10 Jun 14 19:19 .asm_ctl_vbg0
brwxrwx---  1 root oinstall 252, 11 Jun 14 19:19 .asm_ctl_vbg1
brwxrwx---  1 root oinstall 252, 12 Jun 14 19:19 .asm_ctl_vbg2
brwxrwx---  1 root oinstall 252, 13 Jun 14 19:19 .asm_ctl_vbg3
brwxrwx---  1 root oinstall 252, 14 Jun 14 19:19 .asm_ctl_vbg4
brwxrwx---  1 root oinstall 252, 15 Jun 14 19:19 .asm_ctl_vbg5
brwxrwx---  1 root oinstall 252, 16 Jun 14 19:19 .asm_ctl_vbg6
brwxrwx---  1 root oinstall 252, 17 Jun 14 19:19 .asm_ctl_vbg7
brwxrwx---  1 root oinstall 252, 18 Jun 14 19:19 .asm_ctl_vbg8
brwxrwx---  1 root oinstall 252,  1 Jun 14 19:19 .asm_ctl_vdbg
brwxrwx---  1 root oinstall 252,  2 Jun 14 19:19 .asm_ctl_vmb
brwxrwx---  1 root oinstall 252,  2 Jun 10 15:06 .asm_ctl_vmb</pre>
<p>It appears these devices were added in Oracle 11.2.0.2 and the messages in /var/log/messages are fairly benign although still annoying.</p>
<p>MOS Note 1268895.1 &#8220;Multipathd: Asm!.Asm_ctl_spec: Failed To Store Path Info&#8217; found In /var/log/messages&#8221; describes a similar error and recommends blacklisting the new devices in /etc/multipath.conf. For example:</p>
<pre> blacklist {
    devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*"
    devnode "^(hd|xvd|vd)[a-z]*"
    <strong>devnode "ofsctl"           </strong>
<strong>    devnode "^asm/*" </strong>       
}</pre>
<p>Adding the new blacklist entries (shown in bold) to /etc/multipath.conf eliminated the warnings in /var/log/messages  our environment.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/juliandyke.wordpress.com/233/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/juliandyke.wordpress.com/233/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/juliandyke.wordpress.com/233/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/juliandyke.wordpress.com/233/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/juliandyke.wordpress.com/233/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/juliandyke.wordpress.com/233/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/juliandyke.wordpress.com/233/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/juliandyke.wordpress.com/233/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/juliandyke.wordpress.com/233/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/juliandyke.wordpress.com/233/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/juliandyke.wordpress.com/233/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/juliandyke.wordpress.com/233/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/juliandyke.wordpress.com/233/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/juliandyke.wordpress.com/233/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=juliandyke.wordpress.com&amp;blog=12569785&amp;post=233&amp;subd=juliandyke&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://juliandyke.wordpress.com/2011/06/19/multipath-errors-for-asm-devices-in-varlogmessages/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/22a8ad7674d16428224bf7cd8d41d488?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">juliandyke</media:title>
		</media:content>
	</item>
		<item>
		<title>Linux Multipathing when booting from SAN</title>
		<link>http://juliandyke.wordpress.com/2011/06/14/linux-multipathing-when-booting-from-san/</link>
		<comments>http://juliandyke.wordpress.com/2011/06/14/linux-multipathing-when-booting-from-san/#comments</comments>
		<pubDate>Tue, 14 Jun 2011 11:55:25 +0000</pubDate>
		<dc:creator>Julian Dyke</dc:creator>
				<category><![CDATA[Grid Infrastructure / RAC]]></category>
		<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://juliandyke.wordpress.com/?p=226</guid>
		<description><![CDATA[We hit an interesting issue this week with multipathing. (RHEL5U6). We had configured /etc/multipath.conf to set the UID and GID for multipath devices provisioned to support ASM disk groups. [oracle@server1]$ ls -l /dev/mapper/ora_data1 brw-rw---- 1 oracle dba 253, 3 Jun 14 09:00 ora_data1 I decided that I wanted to change the GID from the dba group [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=juliandyke.wordpress.com&amp;blog=12569785&amp;post=226&amp;subd=juliandyke&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>We hit an interesting issue this week with multipathing. (RHEL5U6).</p>
<p>We had configured /etc/multipath.conf to set the UID and GID for multipath devices provisioned to support ASM disk groups.</p>
<pre>[oracle@server1]$ ls -l /dev/mapper/ora_data1
brw-rw---- 1 oracle dba 253, 3 Jun 14 09:00 ora_data1</pre>
<p>I decided that I wanted to change the GID from the dba group to the oinstall group, so I updated the entry in /etc/multipath.conf.from</p>
<pre>multipath {
  wwid 360060d40053211000000476200000a09
  alias ora_data1
  uid 500
  gid 201
}</pre>
<p>to</p>
<pre>multipath {
  wwid 360060d40053211000000476200000a09
  alias ora_data1
  uid 500
  gid 202
}</pre>
<p>The server was subsequently rebooted, but the device group was still dba</p>
<pre>[oracle@server1]$ ls -l /dev/mapper/ora_data1
brw-rw---- 1 oracle dba 253, 3 Jun 14 09:00 ora_data1</pre>
<p>The most obvious possibility was that the group was being overwritten in /etc/rc.local, but that file only contained the default entry</p>
<pre>touch /var/lock/subsys/local</pre>
<p>Suspicious of the system administrators I searched through udev on the off-chance they had figured out how to change ownerships in there &#8211; but none of the files had changed since the operating system was installed.</p>
<p>Finally we discovered that the server was configured to boot from SAN. This means that some of the files required for the initial boot process must be copied into the boot partition. Updating them in /etc has no effect as the kernel has to read the files during the boot phase at which time /etc is unavailable.</p>
<p>Having discovered that /etc/multipath.conf was being ignored, my next concern was whether this affected any other configuration files.</p>
<p>Apparently this issue affects RHEL5U5 and above. To check which files are affected first determine the current kernel using uname -r. For example:</p>
<pre>[root@server1]# uname -r 2.6.18-238.1.1.el5</pre>
<p>Identify the current kernel image in the /boot directory. In this example the kernel image will be</p>
<pre>/boot/initrd-2.6.18-238.1.1.el5.img</pre>
<p>Change to another directory e.g. /tmp/initrd. This step is important to avoid inadvertently overwriting the /boot directory (I speak from experience).</p>
<pre>[root@server1]# mkdir /tmp/initrd
[root@server1]# cd /tmp/initrd</pre>
<p>The following command extracts the contents of the kernel image</p>
<pre>zcat /boot/initrd-2.6.18-238.1.1.el5.img | cpio -id</pre>
<p>This command will create a set of directories under /tmp/initrd and extract the configuration files into them. In our example there were four files in this directory:</p>
<ul>
<li>etc/multipath.conf</li>
<li>etc/scsi_id.config</li>
<li>etc/lvm/lvm.conf</li>
<li>var/lib/multipath/bindings</li>
</ul>
<p>It is also worth noting that both /etc/multipath.conf and /var/lib/multipath/bindings file are copied to the boot partition.</p>
<p>If you update /etc/multipath.conf it will be necessary to rebuild the initrd so that the changes are applied the next time the node is rebooted.</p>
<p>Before rebuilding the initrd, ensure you have backed up the previous image. For example:</p>
<pre>cp /boot/initrd-2.6.18-238.1.1.el5.img /boot/initrd-2.6.18-238.1.1.el5.img.bak</pre>
<p>Rebuild the initrd using the following command:</p>
<pre>mkinitrd /boot/initrd-$(uname -r).img $(uname -r)</pre>
<p>So far this is the only boot from SAN configuration I have encountered so it is too early to say if these are the only configuration files that can be overridden in the boot partition. But at least I now know how to check</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/juliandyke.wordpress.com/226/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/juliandyke.wordpress.com/226/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/juliandyke.wordpress.com/226/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/juliandyke.wordpress.com/226/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/juliandyke.wordpress.com/226/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/juliandyke.wordpress.com/226/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/juliandyke.wordpress.com/226/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/juliandyke.wordpress.com/226/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/juliandyke.wordpress.com/226/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/juliandyke.wordpress.com/226/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/juliandyke.wordpress.com/226/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/juliandyke.wordpress.com/226/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/juliandyke.wordpress.com/226/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/juliandyke.wordpress.com/226/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=juliandyke.wordpress.com&amp;blog=12569785&amp;post=226&amp;subd=juliandyke&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://juliandyke.wordpress.com/2011/06/14/linux-multipathing-when-booting-from-san/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/22a8ad7674d16428224bf7cd8d41d488?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">juliandyke</media:title>
		</media:content>
	</item>
		<item>
		<title>Intel Xeon Processors &#8211; Hyper Threading and Turbo Boost</title>
		<link>http://juliandyke.wordpress.com/2011/04/08/intel-xeon-processors-hyper-threading-and-turbo-boost/</link>
		<comments>http://juliandyke.wordpress.com/2011/04/08/intel-xeon-processors-hyper-threading-and-turbo-boost/#comments</comments>
		<pubDate>Fri, 08 Apr 2011 07:02:18 +0000</pubDate>
		<dc:creator>Julian Dyke</dc:creator>
				<category><![CDATA[CPU]]></category>

		<guid isPermaLink="false">http://juliandyke.wordpress.com/?p=214</guid>
		<description><![CDATA[Over the last 18 months I have been running a standard CPU performance test on every system I encounter. The test measures the time taken by a single core to process a block of anonymous PL/SQL code. This takes anything from around nine seconds to six minutes to run depending on the CPU type. I include some of [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=juliandyke.wordpress.com&amp;blog=12569785&amp;post=214&amp;subd=juliandyke&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Over the last 18 months I have been running a standard CPU performance test on every system I encounter. The test measures the time taken by a single core to process a block of anonymous PL/SQL code. This takes anything from around nine seconds to six minutes to run depending on the CPU type. I include some of the fastest results in live presentations in order to introduce topics of interest concerning processor technology. The test results are like Top Gear Fastest Laps; they measure how fast the CPU can go, but not necessarily any other desirable features such as scalability. However the test can be run on production systems without risk and hence I have in excess of 150 results.</p>
<p>During my presentation to the UKOUG RAC &amp; HA SIG I observed that the fastest result seen to date on any platform was 8.92 seconds on an Intel Xeon X5670 six-core 2.93GHz processor. My friend and former colleague Steve Shaw from Intel Corporation was in the audience and suggested it may be possible to improve on these times using some relatively recent processor enhancements. We agreed to spend a day at the Intel lab in Winnersh testing the latest hardware.</p>
<p>The system we tested had two Intel Xeon X5680 3.33GHz six-core Westmere EP processors. The operating system was Oracle Enterprise Linux 5 Update 6. We tested with both the Red Hat Kernel (2.6-18.238.el5) and Oracle Unbreakable Linux kernel (2.6-32-100.26.2.el5).</p>
<p>The test script was as follows:</p>
<pre>SET SERVEROUTPUT ON
SET TIMING ON

DECLARE
  n NUMBER := 0;
BEGIN
  FOR f IN 1..10000000
  LOOP
    n := MOD (n,999999) + SQRT (f);
  END LOOP;
  DBMS_OUTPUT.PUT_LINE ('Res = '||TO_CHAR (n,'999999.99'));
END;
/</pre>
<p>When executed successfully this script should produce the following output:</p>
<pre>Res = 873729.72</pre>
<p>For each test we executed the script at least 10 times and took the time of the fastest execution.</p>
<p>We tested the impact of configuring Hyper Threading and Turbo Boost. For the Red Hat (2.6-18.238.el5) kernel the results (in seconds) were as follows:</p>
<pre>|-------------|---------|---------------------|
|             |         |    Hyper Threading  |
|             |         |----------|----------|
|             |         |    Off   |    On    |
|-------------|---------|----------|----------|
|Turbo Boost  |   Off   |   8.98   |   8.89   |
|             |---------|----------|----------|
|             |   On    |   8.24   |   8.38   |
|-------------|---------|----------|----------|</pre>
<p>So for the Red Hat kernel, performance was significantly improved by enabling Turbo Boost. Hyper Threading appeared to have less impact.</p>
<p>We had problems booting the Oracle Unbreakable Linux kernel (2.6-32-100.26.2.el5) when Turbo Boost was disabled. We think this may be a timing issue during the boot process as opposed to a specific problem with the kernel itself. However, we did perform some tests with Turbo Boost enabled and the results (in seconds) were as follows:</p>
<pre>|-------------|---------|---------------------|
|             |         |    Hyper Threading  |
|             |         |----------|----------|
|             |         |    Off   |    On    |
|-------------|---------|----------|----------|
|Turbo Boost  |   On    |   8.33   |  8.21    |
|-------------|---------|----------|----------|</pre>
<p>Unfortunately we do not have any results for the Oracle Unbreakable kernel with Turbo Boost disabled. However, the result of 8.21 seconds with both Hyper Threading and Turbo Boost enabled is the fastest this test has ever run.</p>
<p>Although the results for the Red Hat kernel contradict this, in our opinion Hyper Threading often has a positive impact on system throughput for more representative tests; it rarely has a negative impact and therefore on balance we would generally recommend enabling Hyper Threading on all Xeon processors.</p>
<p>However these results also suggest that the Turbo Boost feature should also be enabled. For the Red Hat kernel enabling Turbo Boost improved performance by 6% to 9%. As the Turbo Boost feature is rarely enabled in production environments, it may be worth investigating for CPU-bound systems.</p>
<p>Thanks to Steve Shaw of Intel Corporation for his assistance in the research for this post.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/juliandyke.wordpress.com/214/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/juliandyke.wordpress.com/214/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/juliandyke.wordpress.com/214/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/juliandyke.wordpress.com/214/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/juliandyke.wordpress.com/214/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/juliandyke.wordpress.com/214/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/juliandyke.wordpress.com/214/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/juliandyke.wordpress.com/214/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/juliandyke.wordpress.com/214/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/juliandyke.wordpress.com/214/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/juliandyke.wordpress.com/214/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/juliandyke.wordpress.com/214/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/juliandyke.wordpress.com/214/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/juliandyke.wordpress.com/214/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=juliandyke.wordpress.com&amp;blog=12569785&amp;post=214&amp;subd=juliandyke&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://juliandyke.wordpress.com/2011/04/08/intel-xeon-processors-hyper-threading-and-turbo-boost/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/22a8ad7674d16428224bf7cd8d41d488?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">juliandyke</media:title>
		</media:content>
	</item>
		<item>
		<title>Linux Shell Limits</title>
		<link>http://juliandyke.wordpress.com/2011/01/19/linux-shell-limits/</link>
		<comments>http://juliandyke.wordpress.com/2011/01/19/linux-shell-limits/#comments</comments>
		<pubDate>Wed, 19 Jan 2011 09:49:28 +0000</pubDate>
		<dc:creator>Julian Dyke</dc:creator>
				<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://juliandyke.wordpress.com/?p=199</guid>
		<description><![CDATA[Most Oracle database installations on Linux require configuration of shell limits. A typical configuration requires the following changes Add the following lines to /etc/security/limits.conf oracle soft nproc 2047 oracle hard nproc 16384 oracle soft nofile 1024 oracle hard nofile 65536 Additional memlock limits are required if large pages are configured. The values of these limits [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=juliandyke.wordpress.com&amp;blog=12569785&amp;post=199&amp;subd=juliandyke&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Most Oracle database installations on Linux require configuration of shell limits. A typical configuration requires the following changes</p>
<p>Add the following lines to /etc/security/limits.conf</p>
<pre>
oracle           soft    nproc           2047
oracle           hard    nproc           16384
oracle           soft    nofile          1024
oracle           hard    nofile          65536
</pre>
<p>Additional memlock limits are required if large pages are configured. The values of these limits is dependent on the number of huge pages allocated. One also occasionally sees other limits such as the stack limit increased.</p>
<p>Add the following line to /etc/pam.d/login</p>
<pre>
session    required     pam_limits.so
</pre>
<p>Add the following lines to /etc/profile</p>
<pre>
if [ $USER = "oracle" ]; then
  if [ $SHELL = "/bin/ksh" ]; then
    ulimit -u 16384
    ulimit -n 65536
  else
    ulimit -u 16384 -n 65536
  fi
fi
</pre>
<p>I normally reboot the system after configuring the above settings. </p>
<p>During an Enterprise Manager Grid Control installation this week, we made the above changes. But when we tried to login to Oracle we hit the following error:</p>
<pre>
login as: oracle
oracle@vm7's password:
Last login: Wed Jan 19 05:39:00 2011 from 192.168.5.100
-bash: ulimit: open files: cannot modify limit: Operation not permitted
</pre>
<p>This error did not occurred if we logged in as root and then entered</p>
<pre>
[root@vm7]# su - oracle
</pre>
<p>After some investigation the cause of this error was identified. In /etc/ssh/sshd_config there is a parameter called UsePAM which enables PAM authentication. If this parameter is not set then it defaults to &#8220;no&#8221;. I think it is normally overridden during installation. In order to allow user limits to be set the value should be &#8220;yes&#8221;</p>
<pre>
UsePAM=yes
</pre>
<p>The sshd service must be restarted for the parameter change to take effect:</p>
<pre>
[root@vm7]# service sshd restart
</pre>
<p>If the change is successful the new shell limits should be reported by the ulimit command. For example:</p>
<pre>
[oracle@vm7]$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 26560
max locked memory       (kbytes, -l) 1148576
max memory size         (kbytes, -m) unlimited
open files                      (-n) 65536
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 10240
cpu time               (seconds, -t) unlimited
max user processes              (-u) 16384
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
</pre>
<p>In the above example the limits of interest are <i>open files</i> (-n) which should be 65536 and <i>max user processes</i> (-u) which should be 16384.</p>
<p>In many documents (including some Oracle 11gR2 installation guides) the following lines are recommended for /etc/profile</p>
<pre>
if [ $USER = "oracle" ]; then
  if [ $SHELL = "/bin/ksh" ]; then
    ulimit -p 16384
    ulimit -n 65536
  else
    ulimit -u 16384 -n 65536
  fi
fi
</pre>
<p>Not many sites use the Korn shell with Linux but if you do the following error will occur during login:</p>
<pre>
login as: oracle
oracle@vm7's password:
Last login: Wed Oct 13 07:25:04 2010 from 192.168.5.100
/etc/profile[58]: ulimit: pipe: is read only
</pre>
<p>The error occurs because the ulimit -p parameter specifies the number of 512-byte blocks for pipe buffering. The Korn shell man page (man ksh) lists most ulimit parameters, but omits the one we need which is -u to specify the number of processes. So /etc/profile should contain: </p>
<pre>
if [ $USER = "oracle" ]; then
  if [ $SHELL = "/bin/ksh" ]; then
    ulimit -u 16384
    ulimit -n 65536
  else
    ulimit -u 16384 -n 65536
  fi
fi
</pre>
<p>In the Korn shell we can confirm this using ulimit -a</p>
<pre>
[oracle@vm7]$ ulimit -a
address space limit (kbytes)   (-M)  unlimited
core file size (blocks)        (-c)  0
cpu time (seconds)             (-t)  unlimited
data size (kbytes)             (-d)  unlimited
file size (blocks)             (-f)  unlimited
locks                          (-L)  unlimited
locked address space (kbytes)  (-l)  1148576
nofile                         (-n)  65536
nproc                          (-u)  16384
pipe buffer size (bytes)       (-p)  4096
resident set size (kbytes)     (-m)  unlimited
socket buffer size (bytes)     (-b)  4096
stack size (kbytes)            (-s)  10240
threads                        (-T)  not supported
process size (kbytes)          (-v)  unlimited
</pre>
<p>In the above example <i>nofile</i> (-n) should be 65536 and <i>nproc</i> (-u) should be 16384</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/juliandyke.wordpress.com/199/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/juliandyke.wordpress.com/199/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/juliandyke.wordpress.com/199/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/juliandyke.wordpress.com/199/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/juliandyke.wordpress.com/199/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/juliandyke.wordpress.com/199/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/juliandyke.wordpress.com/199/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/juliandyke.wordpress.com/199/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/juliandyke.wordpress.com/199/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/juliandyke.wordpress.com/199/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/juliandyke.wordpress.com/199/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/juliandyke.wordpress.com/199/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/juliandyke.wordpress.com/199/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/juliandyke.wordpress.com/199/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=juliandyke.wordpress.com&amp;blog=12569785&amp;post=199&amp;subd=juliandyke&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://juliandyke.wordpress.com/2011/01/19/linux-shell-limits/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/22a8ad7674d16428224bf7cd8d41d488?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">juliandyke</media:title>
		</media:content>
	</item>
		<item>
		<title>Oracle 11.2.0.2 Grid Infrastructure Installation Issues on Solaris</title>
		<link>http://juliandyke.wordpress.com/2010/12/04/oracle-11-2-0-2-grid-infrastructure-installation-issues-on-solaris/</link>
		<comments>http://juliandyke.wordpress.com/2010/12/04/oracle-11-2-0-2-grid-infrastructure-installation-issues-on-solaris/#comments</comments>
		<pubDate>Sat, 04 Dec 2010 02:44:33 +0000</pubDate>
		<dc:creator>Julian Dyke</dc:creator>
				<category><![CDATA[Grid Infrastructure / RAC]]></category>

		<guid isPermaLink="false">http://juliandyke.wordpress.com/?p=188</guid>
		<description><![CDATA[This post discusses some issues encountered installing Oracle 11.2.0.2 on a two-node Solaris 5.10 SPARC cluster The Grid Infrastructure installation failed when running root.sh on the second node with the following error: root@server21 # /data/app/oragrid/11.2.0/grid/root.sh Running Oracle 11g root script... The following environment variables are set as: ORACLE_OWNER= grid ORACLE_HOME= /data/app/oragrid/11.2.0/grid Enter the full pathname [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=juliandyke.wordpress.com&amp;blog=12569785&amp;post=188&amp;subd=juliandyke&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>This post discusses some issues encountered installing Oracle 11.2.0.2 on a two-node Solaris 5.10 SPARC cluster</p>
<p>The Grid Infrastructure installation failed when running root.sh on the second node with the following error:</p>
<pre>root@server21 # /data/app/oragrid/11.2.0/grid/root.sh
Running Oracle 11g root script...

The following environment variables are set as:
    ORACLE_OWNER= grid
    ORACLE_HOME=  /data/app/oragrid/11.2.0/grid

Enter the full pathname of the local bin directory:
  [/usr/local/bin]:
The contents of "dbhome" have not changed.
  No need to overwrite.
The contents of "oraenv" have not changed.
  No need to overwrite.
The contents of "coraenv" have not changed.
  No need to overwrite.

Entries will be added to the /var/opt/oracle/oratab file as
needed by Database Configuration Assistant when a database
is created
Finished running generic part of root script.
Now product-specific root actions will be performed.
Using configuration parameter file:
  /u01/app/11.2.0/grid/crs/install/crsconfig_params
Creating trace directory
LOCAL ADD MODE
Creating OCR keys for user 'root', privgrp 'root'..
Operation successful.
OLR initialization - successful
Adding daemon to inittab
ACFS-9200: Supported
ACFS-9300: ADVM/ACFS distribution files found.
ACFS-9307: Installing requested ADVM/ACFS software.
ACFS-9308: Loading installed ADVM/ACFS drivers.
ACFS-9327: Verifying ADVM/ACFS devices.
ACFS-9309: ADVM/ACFS installation correctness verified.
CRS-4402: The CSS daemon was started in exclusive mode but
found an active CSS daemon on node server21, number 1, and
is terminating
An active cluster was found during exclusive startup,
restarting to join the cluster
Failed to start Oracle Clusterware stack
Failed to start Cluster Synchorinisation Service in clustered
mode at /u01/app/11.2.0/grid/crs/install/crsconfig_lib.pm
line 1017.
/u01/app/11.2.0/grid/perl/bin/perl
-I/u01/app/11.2.0/grid/perl/lib
-I/u01/app/11.2.0/grid/crs/install
/u01/app/11.2.0/grid/crs/install/rootcrs.pl execution failed</pre>
<p>We have seen this error before on Linux so we downloaded the mcasttest utility from MOS Note 1212703.1 &#8220;Grid Infrastructure install or upgrade may fail due to Multicasting&#8221;</p>
<p>A detailed explanation of the mcasttest utility is available in another post. The servers were called server21, server22 (renamed to protect the customer&#8217;s identity). The private network used device e1000g2 so we executed mcasttest as follows:</p>
<pre><a href="mailto:grid@$">$</a> ./mcasttest.pl -n server21,server22 -i e1000g2
###########  Setup for node server21  ##########
Checking node access 'server21'
Checking node login 'server21'
Checking/Creating Directory /tmp/mcasttest for binary
  on node 'server21'
Distributing mcast2 binary to node 'server21'
###########  Setup for node server22  ##########
Checking node access 'server22'
Checking node login 'server22'
Checking/Creating Directory /tmp/mcasttest for binary
  on node 'server22'
Distributing mcast2 binary to node 'server22'
###########  testing Multicast on all nodes  ##########

Test for Multicast address 230.0.1.0

Multicast Failed for e1000g2
  using address 230.0.1.0:42000

Test for Multicast address 224.0.0.251

Multicast Failed for e1000g2
  using address 224.0.0.251:42001</pre>
<p>In this example multicasting is failing for both the default address (230.0.1.0) and the alternative address (224.0.0.251). Therefore there is no point in installing the patch for bug 9974223 &#8211; &#8220;Grid Infrastructure needs multicast communication on 230.0.1.0 address working&#8221; as it will not solve the problem.</p>
<p>The customer was using CISCO switches on which multicasting had been deliberately disabled for business reasons. Therefore we were unable to explore the possibility of enabling multicasting at switch level.</p>
<p>An alternative approach was required, so we tried replacing the CISCO switches on the private network with a cheap 100Mb switch. If this test was successful the customer was planning to order two dedicated switches to provide resilience and to use IPMP to configure the private network.</p>
<p>After the new switch was installed the output of mcasttest was:</p>
<pre>$ ./mcasttest.pl -n server21,server22 -i e1000g2
###########  Setup for node server21  ##########
Checking node access 'server21'
Checking node login 'server21'
Checking/Creating Directory /tmp/mcasttest for binary
  on node 'server21'
Distributing mcast2 binary to node 'server21'
###########  Setup for node server22  ##########
Checking node access 'server22'
Checking node login 'server22'
Checking/Creating Directory /tmp/mcasttest for binary
  on node 'server22'
Distributing mcast2 binary to node 'server22'
###########  testing Multicast on all nodes  ##########

Test for Multicast address 230.0.1.0

Multicast Succeeded for e1000g2
  using address 230.0.1.0:42000

Test for Multicast address 224.0.0.251

Multicast Succeeded for e1000g2
  using address 224.0.0.251:42001</pre>
<p>So replacing the switch had solved the multicast issue.</p>
<p>We attempted to reinstall Grid Infrastructure, but again it failed when we ran root.sh on the second node with the following error:</p>
<pre>Failed to start Oracle Clusterware stack
Failed to start Cluster Synchorinisation Service in clustered
mode at /u01/app/11.2.0/grid/crs/install/crsconfig_lib.pm
line 1017.
/u01/app/11.2.0/grid/perl/bin/perl
-I/u01/app/11.2.0/grid/perl/lib
-I/u01/app/11.2.0/grid/crs/install
/u01/app/11.2.0/grid/crs/install/rootcrs.pl
  execution failed</pre>
<p>We installed the patch for bug 9974223 &#8211; &#8220;Grid Infrastructure needs multicast communication on 230.0.1.0 address working&#8221;.</p>
<p>The README for the Solaris SPARC version of patch 9974223 was incorrect; it appeared to be a generic patch notice rather than a patch for Grid Infrastructure. We compared with the Linux x86-64 README and discovered this was completely different and appeared to be correct. So we followed the Linux x86-64 README using the Solaris patch.</p>
<p>However, following installation of this patch the error still occurred. We tried a couple more times, then resorted to digging around in the log files to try to identify exactly where root.sh was failing.</p>
<p>The root.sh script calls several other scripts and finally executes</p>
<pre>$GRID_HOME/crs/install/rootcrs.pl.</pre>
<p>The rootcrs.pl script creates a log file in</p>
<pre>$GRID_HOME/cfgtoollogs/crsconfig/rootcrs_&lt;server&gt;.log</pre>
<p>This script contains some useful output. The rootcrs.pl script attempts to start CSSD using a root agent. The log file for the root agent is:</p>
<pre>$GRID_HOME/log/&lt;server&gt;/agent/ohasd/orarootagent_root/
  orarootagent_root.log</pre>
<p>The root agent starts the CSSD daemon which logs its activities in:</p>
<pre>$GRID_HOME/log/&lt;server&gt;/cssd/ocssd.log</pre>
<p>Investigating these log files, we became convinced that the error for which we were looking was not caused by the multicasting configuration which appeared to be working correctly. However, we did notice a strange discrepancy in ifconfig for the interconnect on the first node after root.sh had been executed.</p>
<pre>e1000g2:
   flags=1001000843&lt;UP,BROADCAST,RUNNING,MULTICAST,
     IPv4,FIXEDMTU&gt; mtu 9000 index 3
   inet 172.16.0.2 netmask ffffff00
   broadcast 172.16.0.255
   ether 0:c0:dd:14:c:58
e1000g2:1: flags=1000843&lt;UP,BROADCAST,RUNNING,MULTICAST,
      IPv4&gt; mtu 9216 index 3
   inet 169.254.35.5 netmask ffff0000
   broadcast 169.254.255.255</pre>
<p>In Oracle 11.2.0.2 a new feature called the redundant interconnect has been introduced. Instead of configuring bonding at operating system level, it is theoretically possible to allow Oracle to manage multiple physical networks providing load balancing and failover capabilities. Within the Grid Infrastructure the new feature is known as HAIP. I will cover this in more detail in another post. I looked at the ifconfig from another customer where we had successfully installed Oracle 11.2.0.2. Even if the bonding has been configured for the private network, the Grid Infrastructure will automatically create a new VIP on the private network device for each node in the cluster. The IP address is automatically allocated from the 169.254.0.0 subnet, apparently using a random algorithm.</p>
<p>So in the above example the private network has been configured to use e1000g2. When Clusterware is started a new virtual IP is created called e1000g2:1. The latter address appears to be used by CSSD for inter-node communications.</p>
<p>However, take another look at the MTU sizes. For the e1000g2 the MTU size is 9000; for e1000g2:1 Oracle has used an MTU size of 9216. This anomaly had us scratching our heads. We took a look at the network device definition file</p>
<pre>grid@server21 $ cat /etc/hostname.e1000g2
server21-priv mtu 9000</pre>
<p>So we were hard coding the MTU size in /etc/hostname.e1000g2, but Oracle was ignoring this and using 9216. Which size was correct?</p>
<p>Unlike Linux, in Solaris the ping command does not have an option to specify the packet size, so I tried the traceroute command.</p>
<p>We used the following syntax for the traceroute command:</p>
<pre>traceroute -s &lt;source&gt; -r -F &lt;target&gt; &lt;MTU Size&gt;</pre>
<p>By trial and error we discovered that the traceroute command was successful at all MTU sizes up to 1518, but failed for MTU sizes of 1519 and above. We immediately suspected our cheap switch and further experimentation quickly confirmed that it did not support MTU sizes greater than 1518.</p>
<p>On the other interfaces, the customer had used the default MTU size of 1500, so we decided to revert to this. As it is the default we simply removed the MTU size from /etc/hostname.e1000g2. For example:</p>
<pre>grid@server21 $ cat /etc/hostname.e1000g2
server21-priv</pre>
<p>However, this did not have the desired effect. In fact the MTU size for e1000g2 increased from 9000 to 9216. On Solaris the MTU size is defined in two places; in the interface definition file e.g. /etc/hostname.e1000g2 and also in the kernel driver e.g. /kernel/drv/e1000g.conf.</p>
<p>Initially /kernel/drv/e1000g.conf contained the following</p>
<pre>root@server21 # cat /kernel/drv/e1000g.conf
# Driver.conf file for Intel e1000g Gigabit Ethernet Adapter
MaxFrameSize=0,0,3,0,3,0,0,0,0,0,0,0,0,0,0,0;
        # 0 is for normal ethernet frames.
        # 1 is for upto 4k size frames.
        # 2 is for upto 8k size frames.
        # 3 is for upto 16k size frames.
        # These are maximum frame limits, not the actual
        # ethernet frame size. Your actual ethernet frame
        # size would be determined by protocol stack
        # configuration (please refer to ndd command man
        # pages)
        # For Jumbo Frame Support (9k ethernet packet)
        # use 3 (upto 16k size frames)</pre>
<p>In the above file, the MaxFrameSize entry is defining the maximum frame size for each interface. The first entry is for e1000g0, the next for e1000g1 etc. In the above example the maximum frame size is set to the default (1500) for e1000g0, e1000g1 and e1000g3. It is set to 3 (up to 16k size frames) for e1000g2 and e1000g4. However as 9216 is the absolute limit for frame sizes these devices are restricted to this MTU size.</p>
<p>Therefore we updated MaxFrameSize so that all entries were set to zero and rebooted the node:</p>
<pre>MaxFrameSize=0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0;</pre>
<p>Following this change ifconfig reported the following:</p>
<pre>e1000g2:
  flags=1000843&lt;UP,BROADCAST,RUNNING,MULTICAST,
     IPv4&gt; mtu 1500 index 3
   inet 172.16.0.1 netmask ffffff00
   broadcast 172.16.0.255
   ether 0:c0:dd:14:7:80</pre>
<p>So the MTU size was now set correctly to 1500</p>
<p>After reinstalling Grid Infrastructure on the first node (including root.sh) ifconfig reported the following:</p>
<pre>e1000g2:
  flags=1000843&lt;UP,BROADCAST,RUNNING,MULTICAST,
    IPv4&gt; mtu 1500 index 3
  inet 172.16.0.1 netmask ffffff00
  broadcast 172.16.0.255
  ether 0:c0:dd:14:7:80
e1000g2:1:
  flags=1000843&lt;UP,BROADCAST,RUNNING,MULTICAST,
    IPv4&gt; mtu 1500 index 3
  inet 169.254.248.231 netmask ffff0000
  broadcast 169.254.255.255</pre>
<p>So the new HAIP VIP was also now using the MTU size of 1500.</p>
<p>We tried running root.sh on the second node and this time it succeeded.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/juliandyke.wordpress.com/188/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/juliandyke.wordpress.com/188/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/juliandyke.wordpress.com/188/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/juliandyke.wordpress.com/188/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/juliandyke.wordpress.com/188/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/juliandyke.wordpress.com/188/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/juliandyke.wordpress.com/188/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/juliandyke.wordpress.com/188/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/juliandyke.wordpress.com/188/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/juliandyke.wordpress.com/188/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/juliandyke.wordpress.com/188/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/juliandyke.wordpress.com/188/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/juliandyke.wordpress.com/188/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/juliandyke.wordpress.com/188/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=juliandyke.wordpress.com&amp;blog=12569785&amp;post=188&amp;subd=juliandyke&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://juliandyke.wordpress.com/2010/12/04/oracle-11-2-0-2-grid-infrastructure-installation-issues-on-solaris/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/22a8ad7674d16428224bf7cd8d41d488?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">juliandyke</media:title>
		</media:content>
	</item>
	</channel>
</rss>
