Tales From A Lazy Fat DBA

Its all about Databases, their performance, troubleshooting & much more …. ¯\_(ツ)_/¯

Posts Tagged ‘trouble’

Saving Oracle unified audit volume with ONLY TOPLEVEL

Posted by FatDBA on August 17, 2026

Unified auditing is one of those features that looks simple when we enable it, but its impact can become very visible very quickly on a busy production system.

I remember once instance where we’d enabled a unified audit policy for a limited set of users. At first glance, the scope looked controlled. It was not enabled for the whole database. It was not auditing every schema. It was not auditing every user. The policy was enabled only for ten named users and only for SELECT activity on a small set of application tables. So, from a distance, it looked safe. But within a short period, the audit trail started growing heavily. In around one day, we saw millions of audit records. That immediately raised the question every DBA asks in this situation —-> “Is something wrong, or is the audit policy doing exactly what we asked it to do?”

In this case, the answer was clear after reviewing the data. Nothing was technically broken. The audit policy was behaving as configured. The issue was that the policy was too broad for the way the application was actually using these tables. The audit policy was defined like this:

CREATE AUDIT POLICY sensitive_read_audit_policy ACTIONS 
  SELECT ON app_schema.sensitive_table_1, 
  SELECT ON app_schema.sensitive_table_2, 
  SELECT ON app_schema.sensitive_table_3, 
  SELECT ON app_schema.sensitive_table_4, 
  SELECT ON app_schema.high_volume_sensitive_table 
  WHEN '1=1' EVALUATE PER STATEMENT;

The important part is this ---> WHEN '1=1' EVALUATE PER STATEMENT

Means that condition is always true. So every qualifying SELECT statement against these audited objects was getting recorded. Since the policy was evaluated per statement, this was not one audit row per user or one audit row per session. It was one audit event per qualifying statement activity. That distinction matters a lot.

On a quiet database, this may not be a concern. But on a busy application system, especially where front-end screens, packages, reports, and repeated lookups are involved, this can quickly generate a very large audit trail. In our case, the audit was enabled only for ten users, but those users were active through the application. The main client program seen in the audit records was TEST.EXE, which appeared to be the primary application front-end client. That was the first clue that the activity was coming from normal application usage, not from random ad hoc access.

The next step was to break down the audit records by object. That changed the whole direction of the analysis … A one-day audit summary showed something like this

OBJECT_SCHEMA   OBJECT_NAME                   ACTION_NAME    AUDIT_RECORDS
-------------   ---------------------------   -----------    -------------
APP_SCHEMA      HIGH_VOLUME_SENSITIVE_TABLE   SELECT           4,425,238
APP_SCHEMA      SENSITIVE_TABLE_1             SELECT              87,785
APP_SCHEMA      SENSITIVE_TABLE_2             SELECT               8,419
APP_SCHEMA      SENSITIVE_TABLE_3             SELECT               2,835
APP_SCHEMA      SENSITIVE_TABLE_4             SELECT               1,263

This changed the direction of the analysis immediately. The issue was not evenly distributed across all audited objects. Almost all of the audit records were coming from one high-volume business table. The high-volume table alone was responsible for roughly 98% of the audit records. That is a very important finding. When audit growth becomes a problem, we should not treat all audited objects equally. One table, one screen, one package, one report, or one application flow may be generating most of the activity.

In this example, the high-volume table had millions of rows and was frequently queried by the application. But table size alone was not the reason for the audit growth. A large table does not generate audit rows by itself. Audit rows are generated when audited activity happens. So the real issue was repeated read activity against that object. From the database side, we can check which client programs or hosts are generating records … something like this

SELECT dbusername,
       client_program_name,
       userhost,
       COUNT(*) AS audit_records
FROM unified_audit_trail
WHERE unified_audit_policies LIKE '%SENSITIVE_READ_AUDIT_POLICY%'
  AND event_timestamp >= SYSTIMESTAMP - INTERVAL '1' DAY
GROUP BY dbusername,
         client_program_name,
         userhost
ORDER BY audit_records DESC;

This helps confirm whether the audit records are coming from normal application usage, reporting tools, ad hoc clients, batch jobs, or unexpected access paths.

In the case behind this post, the majority of audit records were generated by the primary application client. So filtering by that client program would not solve the problem. It would only confirm what we already knew: the main application was the source of the audit volume. We had a few options, FGA clicked instantly, but there are multiple columns out of those tables where they want to enforce auditing, meaning auditing a full table or with FGA will be almost like same, with very less gains as they are looing to audit a wide/huge range of columns and FGA wasn’t a good choice … I mean FGA is more targeted, but If every application query selects the sensitive columns, then FGA may still generate a lot of records. It may make the audit trail more meaningful, but not necessarily small.

Now, next I thought was about one of the option which I see very less DBA discuss about, the ‘ONLY TOPLEVEL’ … Okay, so what this “ONLY TOPLEVEL” does ? … It is an option for unified audit policies that limits audit records to top-level SQL operations. In simple terms, it helps avoid recording the internal or recursive SQL activity that happens underneath a top-level operation. For example, a user may perform one action in the application. That action may call a stored procedure. The procedure may query several tables. The query may access views. The views may access base tables. Oracle may also perform recursive SQL internally. Without top-level filtering, the audit policy may record many of those lower-level operations if they match the audited action. With ONLY TOPLEVEL, the policy focuses on top-level operations instead of recording every indirect SQL statement generated underneath the user action.

ALTER AUDIT POLICY sensitive_read_audit_policy ADD ONLY TOPLEVEL;

This does not change which objects are part of the policy. It changes how much internal activity gets recorded for that policy.

That is why ONLY TOPLEVEL can be very useful when an audit policy is generating a large number of rows because of recursive or package driven SQL. It can reduce audit volume by a great extent. It can reduce pressure on audit storage. It can reduce purge workload. It can also reduce the operational overhead of managing a very large audit trail.

I mean retention is handled by purge. Generation is handled by audit policy design. ONLY TOPLEVEL helps with generation. If it reduces unnecessary internal audit rows, then fewer rows are inserted into the unified audit trail in the first place. It helps in less audit data growth, less pressure on storage, small retentions, less work for purge jobs etc. .. And this is especially important when unified audit data is growing inside system-managed areas such as the audit schema and SYSAUX-related storage. Even if old audit records are purged, physical datafiles may not automatically shrink at the filesystem level. Deleted space may become reusable inside the tablespace, but actual file shrink depends on the datafile high water mark. You can do something like this to implement this change…

-- Check current policy enablement before change

SELECT policy_name,
       enabled_option,
       entity_name,
       entity_type,
       success,
       failure
FROM audit_unified_enabled_policies
WHERE policy_name = 'SENSITIVE_READ_AUDIT_POLICY' ORDER BY entity_name;

NOAUDIT POLICY sensitive_read_audit_policy BY user_1, user_2, user_3;
ALTER AUDIT POLICY sensitive_read_audit_policy ADD ONLY TOPLEVEL;

-- Reverify the policy
SELECT policy_name,
       enabled_option,
       entity_name,
       entity_type,
       success,
       failure
FROM audit_unified_enabled_policies
WHERE policy_name = 'SENSITIVE_READ_AUDIT_POLICY' ORDER BY entity_name;

Soon after we implemented the ONLY TOPLEVEL approach, we saw huge drop in audit records generation. It was more than 90% reduction in records 🙂 And all top level information about underlying sensitive obejcts were well captured by the auditing after ignoring all recursive statements.

The biggest lesson from this exercise is that audit volume should be understood before it becomes a storage or performance issue. A policy can be logically correct and still operationally expensive. Auditing a few users and a few objects may sound small, but if one high-volume table is read millions of times through normal application activity, the unified audit trail can grow very quickly. ONLY TOPLEVEL is a very useful option when the audit policy is capturing too much internal or recursive SQL activity. WHENEVER SUCCESSFUL helps align the policy with a successful-read requirement. FGA can make the design more targeted when specific columns or conditions matter.

But the best audit design starts with a simple question –> What exact evidence do we need? … If the requirement is database object access, unified auditing may be enough. If the requirement is business record access, such as which account, transaction, investment, or bank record was viewed, then application or package-level audit logging may be needed as well.

A good audit trail is not the biggest audit trail. A good audit trail is the one that captures the right evidence, at the right level, with a volume the system can safely manage.

Hope It Helped!
Prashant Dixit

Posted in Uncategorized | Tagged: , , , , | Leave a Comment »

File is DELETED but didn’t reclaim space in filesystem – LINUX

Posted by FatDBA on March 9, 2015

There are times when even after you deleted a file in Linux but that didn’t reclaim space in filesystem.
Below command shows that there are few of the files which are deleted from the system but are still active and still taking the space on the disk.

[oracle@dixitdb053 /proc]# /usr/sbin/lsof |grep -i deleted
oracle 11441 oracle 19w REG 253,2 1810 3129788 /opt/oracle/diag/rdbms/dixitdb/dixitdb/trace/dixitdb_diag_11441.trc (deleted)
oracle 11441 oracle 20w REG 253,2 126 3129795 /opt/oracle/diag/rdbms/dixitdb/dixitdb/trace/dixitdb_diag_11441.trm (deleted)
oracle 30157 oracle 19w REG 253,2 14182 3129684 /opt/oracle/diag/rdbms/dixitdb/dixitdb/trace/dixitdb_vkrm_30157.trc (deleted)
dd 32592 oracle 1w REG 253,2 24238593024 3129587 /opt/oracle/diag/rdbms/dixitdb/dixitdb/trace/dixitdb_ora_31778.trm (deleted)

Here the file is available under mount point — /opt/oracle and is still 86% full.

[oracle@dixitdb053 /proc/11441/fd]# df -hk
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/mapper/VolGroup00-LogVol00
31614888 6127104 23856156 21% /
/dev/mapper/VolGroup02-LogVol05
36124288 29165480 5123800 86% /opt/oracle
/dev/sda1 101086 15711 80156 17% /boot
tmpfs 1956756 0 1956756 0% /dev/shm
113.11.88.199:/vol/dixitdb053_BO
110100480 58354656 51745824 54% /db01
113.11.88.199:/vol/vol_dixitdb053_backup
104857600 55011648 49845952 53% /backup

Well, the space will be automatically reclaimed depending on the size of the file deleted.
Below are the steps in order to remove it instantly from the file system


Steps:
=============

1. Go to /proc directory which contains PID of all active processes and files.
2. login to respective PID

[oracle@dixitdb053 /proc/11441/fd]# cd /proc/32592
[oracle@dixitdb053 /proc/32592]# cd fd
[oracle@dixitdb053 /proc/32592/fd]# pwd
/proc/32592/fd

3. Check if there is a link and in the end it says deleted.

[oracle@dixitdb053 /proc/32592/fd]# ls -ltrh
total 0
lrwx—— 1 oracle oinstall 64 Mar 7 07:57 2 -> /dev/pts/1
l-wx—— 1 oracle oinstall 64 Mar 7 08:25 1 -> /opt/oracle/diag/rdbms/dixitdb/dixitdb/trace/dixitdb_ora_31778.trm (deleted)
lr-x—— 1 oracle oinstall 64 Mar 7 08:25 0 -> /dev/zero

4. Type > in the number that was shown in that line and it will release the space instantly.

[oracle@dixitdb053 /proc/32592/fd]# > 1

[oracle@dixitdb053 /proc/32592/fd]# ls -ltrh
total 0
lrwx—— 1 oracle oinstall 64 Mar 7 07:57 2 -> /dev/pts/1
l-wx—— 1 oracle oinstall 64 Mar 7 08:25 1 -> /opt/oracle/diag/rdbms/dixitdb/dixitdb/trace/dixitdb_ora_31778.trm (deleted)
lr-x—— 1 oracle oinstall 64 Mar 7 08:25 0 -> /dev/zero

5. verify it once again by listing the open files in the filesystem to see if there is still an open file with the deleted status.

Now the space is reclaimed and has been brought down to 0.

[oracle@dixitdb053 /proc/32592/fd]# /usr/sbin/lsof |grep -i deleted
oracle 11441 oracle 19w REG 253,2 0 3129788 /opt/oracle/diag/rdbms/dixitdb/dixitdb/trace/dixitdb_diag_11441.trc (deleted)
oracle 11441 oracle 20w REG 253,2 0 3129795 /opt/oracle/diag/rdbms/dixitdb/dixitdb/trace/dixitdb_diag_11441.trm (deleted)
oracle 30157 oracle 19w REG 253,2 14792 3129684 /opt/oracle/diag/rdbms/dixitdb/dixitdb/trace/dixitdb_vkrm_30157.trc (deleted)
dd 32592 oracle 1w REG 253,2 0 3129587 /opt/oracle/diag/rdbms/dixitdb/dixitdb/trace/dixitdb_ora_31778.trm (deleted)

And the mount point /opt/oracle usage went down too and reached to 17%.

[oracle@dixitdb053 /proc/32592/fd]# df -kh
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/VolGroup00-LogVol00
31G 5.9G 23G 21% /
/dev/mapper/VolGroup02-LogVol05
35G 5.3G 28G 17% /opt/oracle
/dev/sda1 99M 16M 79M 17% /boot
tmpfs 1.9G 0 1.9G 0% /dev/shm
113.11.88.199:/vol/dixitdb053_BO
105G 56G 50G 54% /db01
113.11.88.199:/vol/vol_dixitdb053_backup
100G 53G 48G 53% /backup

Thanks
Prashant Dixit

Posted in Advanced | Tagged: , | Leave a Comment »