Showing posts with label lobsegment. Show all posts
Showing posts with label lobsegment. Show all posts

Tuesday, September 2, 2014

LOB Chunk and Tablespace Block Size

Chunk value corresponds to the data size used by oracle when reading or writing a lob value. Once set chunk size cannot be changed. Though it doesn't matter for lobs stored in row, for out of row lobs the space is used in multiples of the chunk size.
From Oracle documentation (for basicfile lobs) A chunk is one or more Oracle blocks. You can specify the chunk size for the BasicFiles LOB when creating the table that contains the LOB. This corresponds to the data size used by Oracle Database when accessing or modifying the LOB value. Part of the chunk is used to store system-related information and the rest stores the LOB value. If the tablespace block size is the same as the database block size, then CHUNK is also a multiple of the database block size. The default CHUNK size is equal to the size of one tablespace block, and the maximum value is 32K. Once the value of CHUNK is chosen (when the LOB column is created), it cannot be changed. Hence, it is important that you choose a value which optimizes your storage and performance requirements.
For securefile CHUNK is an advisory size and is provided for backward compatibility purposes.
From performance perspective it is considered that accessing lobs in big chunks is more efficient. You can set CHUNK to the data size most frequently accessed or written. For example, if only one block of LOB data is accessed at a time, then set CHUNK to the size of one block. If you have big LOBs, and read or write big amounts of data, then choose a large value for CHUNK.
This post shows the result of a test case carried out to compare the performance benefits of using a large chunk size along with a tablespace with a large block size (8k vs 32k). The blob used for the test case is 800KB. The java code used for the test case is given at the end of the post. For each chunk size (8k vs 32k) the caching option was also changed (nocache vs cache) as they also have direct impact on the IO usage. The lob is stored out of row in a separate tablespace than the table.
The table creation DDL used for basicfile test is shown below (comment and uncomment each option based on the test case). The LOB32KTBS is a tablespace of 32k block size while LOB8KTBS is a tabelspace of 8k block size.
create table ses(sesid varchar2(100), sesob blob) SEGMENT CREATION IMMEDIATE TABLESPACE users
 LOB
  (
    sesob
  )
  STORE AS object_lob_seg (
  TABLESPACE LOB32KTBS
  --TABLESPACE LOB8KTBS
  DISABLE STORAGE IN ROW
  CHUNK 32K
  --CHUNK 8K
                CACHE
  --NOCACHE
  PCTVERSION 0
        STORAGE (MAXEXTENTS UNLIMITED)
        INDEX object_lob_idx (
            TABLESPACE LOB32KTBS
     --TABLESPACE LOB8KTBS
            STORAGE (MAXEXTENTS UNLIMITED)
        )
    )
/
Test cases comprised of reading a lob column for a row and inserting lobs. The IO related statistics comparison for select test case is given below. Based on the graphs it could be seen that 32K chunk size on a tablespace with a block size of 32K requires less number of logical or physical reads compared to having a 8k chunk and lob segment on a 8k block size tablespace. Though not shown on the graphs, on a separate test where using a 32k chunk size and placing the lob segment on a 8K block size tablespace had the same performance characteristics of having a 8k chunk on a 8k block size tablespace. On the other hand having a chunk of 8k and placing the lob segment on a 32k block size tablespace had the same performance characteristics of having a 32k chunk on a 32k block size tablespace. This means that chunk size alone is not going to reduce the amount of IO but the tablespace block size where the lob segment is stored has an influence as well.

The next test was the inserting of lob. The results are shown on the following two graphs. Similar to the read test, having a large chunk size and tablespace block size for lob reduces the IO.




The same test was carried out for securefile lob segments. The table creation DDL is given below. Only difference to the DDL compared to basicfile is the "retention none". All other parameters/options are the same.
create table ses(sesid varchar2(100), sesob blob) SEGMENT CREATION IMMEDIATE TABLESPACE users
 LOB
  (
    sesob
  )
  STORE AS  securefile object_lob_seg (
  TABLESPACE LOB32KTBS
  --TABLESPACE LOB8KTBS
  DISABLE STORAGE IN ROW
  CHUNK 32K
  --CHUNK 8K
                CACHE
  --NOCACHE
  RETENTION NONE
        STORAGE (MAXEXTENTS UNLIMITED)
        INDEX object_lob_idx (
            TABLESPACE LOB32KTBS
   --TABLESPACE LOB8KTBS
            STORAGE (MAXEXTENTS UNLIMITED)
        )
    )
/
The results of the select test is shown on the graphs below. Similar to basicfile the larger chunk size advisory and tablespace block size combination out perform the smaller chunk/block size combination. In all cases the securefile out perform basicfile for the amount of logical or physical reads.

The outcome for the insert test also same as that of basicfile insert test where larger chunk/block size combination out performs the smaller chunk/block size combination. Also between basicfile and secfile the secfile out performs the basicfile.

This tests have shown that it's better to use large chunk/tablespace block sizes for larger LOBs to reduce logical/physical IO related to LOBs.

Useful White papers
SecureFile Performance
Oracle 11g: SecureFiles

Related Post
Nologging vs Logging for LOBs and enq: CF - contention

Java code used for the test. For the code of LobStat class refer the earlier post
public class LobChunkTest {

    final String URL = "jdbc:oracle:thin:@192.168.0.66:1521:ent11g2";
    final String USERNAME = "asanga";
    final String PASSWORD = "asa";

    public static void main(String[] args) {

        LobChunkTest test = new LobChunkTest();
        //Insert test
        test.insertTest();

        System.out.println("\n\n************* end of insert test **************\n\n");

        //select test
        test.selectTest();

    }

    public void insertTest() {
        try {

            OracleDataSource pool = new OracleDataSource();
            pool.setURL(URL);
            pool.setUser(USERNAME);
            pool.setPassword(PASSWORD);

            Connection con = pool.getConnection();
            con.setAutoCommit(false);

            long t1 = System.currentTimeMillis();

            LobStat.displayStats(con);

            byte[] x = new byte[800 * 1024];
            x[1] = 10;
            x[798 * 1024] = 20;

            for (int i = 0; i < 100; i++) {

                OraclePreparedStatement pr = (OraclePreparedStatement) con.prepareStatement("insert into ses values(?,?)");

                String sesid = "abcdefghijklmnopqrstuvwxy" + Math.random();
                pr.setString(1, sesid);
                pr.setBytes(2, x);

                pr.execute();
                con.commit();
                pr.close();

            }

            long t2 = System.currentTimeMillis();

            LobStat.displayStats(con);

            con.close();

            System.out.println("time taken " + (t2 - t1));

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public  void selectTest() {
        try {

            OracleDataSource pool = new OracleDataSource();
            pool.setURL("jdbc:oracle:thin:@192.168.0.66:1521:ent11g2");
            pool.setUser("asanga");
            pool.setPassword("asa");

            Connection con = pool.getConnection();
            con.setAutoCommit(false);

            String[] sesids = new String[100];

            PreparedStatement pr1 = con.prepareStatement("select sesid from ses");
            ResultSet rs1 = pr1.executeQuery();
            int i = 0;
            while (rs1.next()) {

                sesids[i] = rs1.getString(1);

                i++;

            }
            rs1.close();
            pr1.close();
            con.close();

            con = pool.getConnection();
            LobStat.displayStats(con);

            OraclePreparedStatement pr = (OraclePreparedStatement) con.prepareStatement("select SESOB from ses where sesid=?");

            long t1 = System.currentTimeMillis();
            for (String x : sesids) {


                pr.setString(1, x);
                ResultSet rs = pr.executeQuery();

                while (rs.next()) {

                    byte[] blob = rs.getBytes(1);

                }

                rs.close();
            }

            long t2 = System.currentTimeMillis();
            System.out.println("time taken " + (t2 - t1));

            LobStat.displayStats(con);

            pr.close();

            con.close();

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

Tuesday, August 19, 2014

Nologging vs Logging for LOBs and enq: CF - contention

Changing the logging option is one of the possible performance tuning tasks when dealing with LOBs. However use of nologging will make recovery of these lob segments impossible as such use of this will also depends on the nature of the application data. For transient data where recovery is not expected in case of database failure nologging option would be suitable. When the logging option is not explicitly mentioned this results in the tablespace's logging option being inherited by the lob segment. Refer Oracle documentation for more logging related information on basicfile and securefile.
This post presents the result of a simple test carried out comparing the nologging vs logging for both basicfile and securefile LOB segments. The java code used for the test is given at the end of the post. First case is the basicfile. Nologging is not possible if the cache is enable on the lob segment. Therefore nocache option is chosen for both logging and nologging. The table definition is given below.
create table ses(sesid varchar2(100), sesob blob) SEGMENT CREATION IMMEDIATE TABLESPACE users
 LOB
  (
    sesob
  )
  STORE AS object_lob_seg (
  TABLESPACE lob32ktbs
  DISABLE STORAGE IN ROW
        CHUNK 32K
        NOCACHE NOLOGGING
        --NOCACHE LOGGING
        PCTVERSION 0
        STORAGE (MAXEXTENTS UNLIMITED)
        INDEX object_lob_idx (
            TABLESPACE lob32ktbs
            STORAGE (MAXEXTENTS UNLIMITED)
        )
    )
/
The test involves inserting an 800KB LOB into the table 100 times and then later updating it with similar size LOB. Since the LOB is greater than 4K, the table is created with disable in row. Chunk size is set to 32k and the lob segment is stored in a tablespace of 32K block size (lob32ktbs) while the table resides in a different (users) tablespace of 8K block size. Pctversion is set to 0 as no consistent reads are expected. Only option that is changed is the logging option. The test database version is 11.2.0.3
Redo size statistic and the log file sync wait event times are compared for the two test runs as these are directly related to the logging option. Graphs below show the comparison of these for each test run.

There's no surprise that nologging option generates the lowest amount of redo. The logging test case generated around 83-84MB of redo for insert and update which is roughly the same size as the LOB inserted/updated (800KB x 100). There's minimal logging during the nologging test. Since redo is counted for the entire test, the redo seen could be the redo generated for the table data insert (as oppose to lobs) which still is on a tablespace with logging. Nevertheless a clear difference could be observed in the amount of redo generated when nologging is used. This is also reflected in the log file sync time for the two test cases which got reduced from several minutes to under a minute.
Next the same test was executed but this time the lob segment was stored as securefile. The table DDL is given below. Only difference apart from the securefile is Pctversion 0 has been replaced with retention none. All other settings are same as the basic file (tablespaces, database and etc). Chunk size is depreciated in securefile, and when specified is considered only as a guidance.
create table ses(sesid varchar2(100), sesob blob) SEGMENT CREATION IMMEDIATE TABLESPACE users
 LOB
  (
    sesob
  )
  STORE AS securefile object_lob_seg (
  TABLESPACE lob32ktbs
  DISABLE STORAGE IN ROW
        CHUNK 32K
        NOCACHE NOLOGGING
        --NOCACHE LOGGING
        RETENTION NONE
        STORAGE (MAXEXTENTS UNLIMITED)
        INDEX object_lob_idx (
            TABLESPACE lob32ktbs
            STORAGE (MAXEXTENTS UNLIMITED)
        )
    )
/
Similar to the earlier test the nologging generated low amount of redo compared to logging and resulted in short wait on log file sync event.

Comparing redo size and log file sync time for nologging option between the basicfile and securefile shows a mix bag of results. Basicfile has performed well for inserts in reducing redo generated and log file sync while securefile performed well for updates.

Comparing the IO types on OEM console during the nologging test it was noted that securefile uses predominantly large writes while the basicfile uses small writes.
Comparing the test result with logging enabled the securefile out performance basic file for insert and updates in terms of log file sync wait time. Both securefile and basicfile generates roughly the same amount of redo. It must be noted nocache logging is the default for secure file.
The table below shows all the test results.




It seems that when application permits it's best to use nologging for lobs which reduce the amount of redo generated and log file sync waits. However there are some drawbacks to using nologging on LOBs which only comes to light when there are multiple sessions doing LOB related DMLS. Following is an AWR snippet from a load test on pre-production system.
After CPU the top wait event is log file sync and high portion of this wait event is due an basicfile LOB related insert statement that store some transient data. Changing the lob segment option to nologging resulted in lower log file sync time but it also introduced high enq: CF - contention wait times.
According to 1072417.1 enq: CF - contention is normal and expected when doing DML on LOBs with nocache nologging. CF contention occurs as oracle records the unrecoverable system change number (SCN) in the control file. From the application perspective the overall response time degraded after changing to nologging and had to be reverted back to cache logging.

Useful metalink notes
Performance Degradation as a Result of 'enq: CF - contention' [ID 1072417.1]
LOB Performance Guideline [ID 268476.1]
LOBS - Storage, Redo and Performance Issues [ID 66431.1]
LOBS - Storage, Read-consistency and Rollback [ID 162345.1]
Master Note - RDBMS Large Objects (LOBs) [ID 1268771.1]
Performance problems on a table that has hundreds of columns including LOBs [ID 1292685.1]
POOR PERFORMANCE WITH LOB INSERTS [ID 978045.1]
Securefiles Performance Appears Slower Than Basicfile LOB [ID 1323933.1]


Java Code Used for Testing
public class LobLoggingTest {

    final String URL = "jdbc:oracle:thin:@192.168.0.66:1521:ent11g2";
    final String USERNAME = "asanga";
    final String PASSWORD = "asa";

    public static void main(String[] args) {

        LobLoggingTest test = new LobLoggingTest();
        //Insert test
        test.insertTest();

        System.out.println("\n\n************* end of insert test **************\n\n");

        //Update test
        test.updateTest();

    }

    public void insertTest() {
        try {

            OracleDataSource pool = new OracleDataSource();
            pool.setURL(URL);
            pool.setUser(USERNAME);
            pool.setPassword(PASSWORD);

            Connection con = pool.getConnection();
            con.setAutoCommit(false);

            long t1 = System.currentTimeMillis();

            LobStat.displayStats(con);
            LobStat.displayWaits(con);

            byte[] x = new byte[800 * 1024];
            x[1] = 10;
            x[798 * 1024] = 20;

            for (int i = 0; i < 100; i++) {

                OraclePreparedStatement pr = (OraclePreparedStatement) con.prepareStatement("insert into ses values(?,?)");

                String sesid = "abcdefghijklmnopqrstuvwxy" + Math.random();
                pr.setString(1, sesid);
                pr.setBytes(2, x);

                pr.execute();
                con.commit();
                pr.close();

            }

            long t2 = System.currentTimeMillis();

            LobStat.displayStats(con);
            LobStat.displayWaits(con);

            con.close();

            System.out.println("time taken " + (t2 - t1));

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

public void updateTest() {
        try {

            OracleDataSource pool = new OracleDataSource();
            pool.setURL(URL);
            pool.setUser(USERNAME);
            pool.setPassword(PASSWORD);

            Connection con = pool.getConnection();
            con.setAutoCommit(false);

            String[] sesids = new String[100];

            PreparedStatement pr1 = con.prepareStatement("select sesid from ses");
            ResultSet rs1 = pr1.executeQuery();
            int i = 0;
            while (rs1.next()) {

                sesids[i] = rs1.getString(1);
                i++;
            }
            rs1.close();
            pr1.close();
            con.close();

            con = pool.getConnection();
            LobStat.displayStats(con);
            LobStat.displayWaits(con);

            OraclePreparedStatement pr = (OraclePreparedStatement) con.prepareStatement("update ses set SESOB=? where sesid=?");

            byte[] xx = new byte[800 * 1024];
            xx[1] = 10;
            xx[798 * 1024] = 20;

            long t1 = System.currentTimeMillis();
            for (String x : sesids) {

                pr.setBytes(1, xx);
                pr.setString(2, x);
                pr.execute();
                con.commit();
            }

            long t2 = System.currentTimeMillis();
            System.out.println("time taken " + (t2 - t1));

            LobStat.displayStats(con);
            LobStat.displayWaits(con);

            pr.close();
            con.close();

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}


public class LobStat {

    public static void displayStats(Connection con) {
        try {
            PreparedStatement pr = con.prepareStatement("select name,value from v$mystat,v$statname where v$mystat.statistic#=v$statname.statistic# "
                    + " and v$statname.name in ('CPU used when call started',"
                    + " 'CPU used by this session','db block gets','db block gets from cache','db block gets from cache (fastpath)',"
                    + " 'db block gets direct','consistent gets','consistent gets from cache','consistent gets from cache (fastpath)',"
                    + " 'consistent gets - examination','consistent gets direct','physical reads','physical reads direct',"
                    + " 'physical read IO requests','physical read bytes','consistent changes','redo size','redo writes',"
                    + " 'lob writes','lob writes unaligned','physical writes direct (lob)','physical writes','physical writes direct','physical writes from cache','physical writes direct temporary tablespace'"
                    + " ,'physical writes direct temporary tablespace','securefile direct read bytes','securefile direct write bytes','securefile direct read ops'"
                    + " ,'securefile direct write ops') order by 1");

            ResultSet rs = pr.executeQuery();


            while(rs.next()){

                System.out.println(rs.getString(1)+" : "+rs.getDouble(2));
            }

            rs.close();
            pr.close();
        } catch (SQLException ex) {
            ex.printStackTrace();
        }

    }

    public static void displayWaits(Connection con){
        try {
            PreparedStatement pr = con.prepareStatement("select event,total_waits,TIME_WAITED_MICRO from  V$SESSION_EVENT where sid=SYS_CONTEXT ('USERENV', 'SID') and event in ('log file sync','enq: CF - contention')");
            ResultSet rs = pr.executeQuery();

            System.out.println("event : total waits : time waited micro");
            while(rs.next()){

                System.out.println(rs.getString(1)+" : "+rs.getLong(2)+" : "+rs.getLong(3));

            }

            rs.close();
            pr.close();
        } catch (SQLException ex) {
            ex.printStackTrace();
        }

    }
}

Monday, August 18, 2014

enq: HW - contention and latch: enqueue hash chains

An application uses serialized java objects to stored in the database to overcome the application server failovers. The java objects are stored as BLOB. Application has been using the same session storing mechanism for number of years (7+ years) and gone through the upgrades of Oracle versions 10.2 -> 11.1 -> 11.2 without any performance regress. During the inception (with a 10.2) high wait times on enq: HW - contention were encountered which was remedied with the use of the event 44951 (refer 740075.1)
However during a test, involving large number of concurrent application sessions (x10 baseline) the test system encountered high enq: HW - contention even though event 44951 has been set with level 1024.

Following actions were tried in order to reducing the number of and time spent on HW - contention event.
1. Large initial extent size for the lob segment.
  STORE AS "LOB_SEG"
  (
    ...(INITIAL 5368709120 NEXT 134217728 ...
  );
2. Large value for next extent, ideally this should be greater than the (average size of the LOB inserted x number of concurrent inserts).
STORE AS "LOB_SEG"
  (
    ...INITIAL 5368709120 NEXT 134217728 ...
  );
3. Tablespace with uniform extent allocation and extent size is greater than the (average size of the LOB inserted x number of concurrent inserts).
4. Large chunk size for lob segment and tablespace block size equal to the chunk size. Lob segment was placed in a tablespace with a block size of 32K and chunk size 32K was used for the lob semgment.
  STORE AS SECUREFILE "LOB_SEG"
  (
    TABLESPACE "TBS32K" CHUNK 32768
  );
However these actions only slightly reduced the HW - contention wait events and the performance was not satisfactory. At this point it was decided to use the secure file for the lob segments as it was considered better in performance compared to basic file.



Investigating secure file related issues came across the following MOS note (1532311.1) which mentioned high waits on buffer busy waits and enq: TX - contention when there are frequent updates on secure file lobs. Solution for this was to increase the secure file concurrency estimate hidden parameter (_securefiles_concurrency_estimate). However instead of relying on a parameter that depends on the concurrency, the application was modified by replacing the statement sequence of (insert/update) with an (insert/delete/insert).
Running the same test as earlier resulted in high latch: enqueue hash chain waits.

This wait event was as a result of bug 13775960 which results high enqueue hash chains for concurrent inserts on secure files (refer 13775960.8). Luckily there's patch for the bug (13775960 which supersede the patch 13395403) and once applied the enqueue hash chain wait events were resolved.
Even though Oracle documents says that secure file out perform the basic files as in this case the secure file themselves pose some issues of their own which must be tested against.

Useful metalink notes
Bug 13775960 - "enqueue hash chains" latch contention for delete/insert Securefile workload [ID 13775960.8]
Securefiles DMLs cause high 'buffer busy waits' & 'enq: TX - contention' wait events leading to whole database performance degradation [ID 1532311.1]
Bug 2530125 - Hang possible with "enqueue hash chains" latch held during deadlock detection [ID 2530125.8]
Bug 13395403 - "enqueue hash chains" latch contention on Securefile blob DMLs - superseded [ID 13395403.8]
'enq HW - contention' For Busy LOB Segment [ID 740075.1]
How To Analyze the Wait Statistic: 'enq: HW - contention' [ID 419348.1]

Thursday, November 14, 2013

ORA-1691: unable to extend lobsegment Message Only on Alert Log When Inserting to a Table with SecureFile

Similar to Basic LOB situation mentioned in the earlier post a server side (or alert log only) ora-1691 message appears when inserting to table with a securefile. The test was created using the same infrastructure as before so the tablespace names, table names and the java code are identical to the ones mentioned in earlier post. Therefore the test case used for the previous post could also be used here. Only difference is that lob segment is now a securefile.
CREATE TABLE lobtest ( ID number,  "OBJECT" BLOB ) SEGMENT CREATION IMMEDIATE TABLESPACE datatbs
   LOB
  (
    "OBJECT"
  )
  STORE AS securefile object_lob_seg (
                TABLESPACE lobtbs
                DISABLE STORAGE IN ROW
        CACHE
                RETENTION NONE
        STORAGE (MAXEXTENTS UNLIMITED)
        INDEX object_lob_idx (
            TABLESPACE lobtbs
            STORAGE (MAXEXTENTS UNLIMITED)
        )
    )
/
In the previous case the ora-1691 was logged on the alert log during the subsequent inserts. But with the securefile table ora-1691 is raised in the initial insert itself and well before the tablespace is exhausted.
Using the tablespace from the previous test case (each with maximum of 10M) 143 rows could be inserted (different to maximum value in the earlier case. This could be due to use of securefile) before the client side ora-1691 is shown. But at the point of inserting the 133rd row and forward ora-1691 is logged on the alert log but all rows are inserted successfully. Use of retention none has no effect in removing or reducing this logging. Similar to previous case, unless number of the rows inserted somehow known by other means it would be difficult to know any row insertion failed due to a space issue.
Few SR updates later issue is being investigated as a possible bug. Post will be updated with the outcome.

Related Post
ORA-1691: unable to extend lobsegment is expected behavior?




Update 06 December 2013
SR is being inactivated and issue is to be tracked with
Bug 17463217 : ORA-1691: UNABLE TO EXTEND LOBSEGMENT SHOWN ON ALERT LOG WHEN INSERTING TO TABLE

Update 02 January 2014
Oracle's reply to the SR was that this is not a bug but expected behavior. The explanation is when inserting around 133rd row mark oracle process realize there's not enough in the table so it allocate some extents to the table and insert is successful, as such no error on the client side. However at this time oracle process also identifies that space pressure exits for this table and starts background process to preallocate further extents to the table. This is done by the Space Management Slave Process (Wnnn) which "performs various background space management tasks, including proactive space allocation and space reclamation". When this slave process tries to preallocate it gets the ora-1691 as there's not enough free space in the data file, because pre-allocation of extents goes beyond the maximum size for the data file. Therefore slave process logs ora-1691 on alert log. End of explanation.
So if there's any ora-1691 on alert log it's worth while to check the application logs as well to check if any row insertion failed, since as shown here it is possible to get ora-1691 on alert log but all rows to successfully get inserted as well.

Tuesday, September 3, 2013

ORA-1691: unable to extend lobsegment is expected behavior?

ORA-1691 happens when a lob segment cannot extend itself to accommodate the growth due to new data being inserted. Usually remedies are extending the data file and adding a data file. However there's a special situation where this is the expected behavior. ORA-1691 is reported on alert log (only on alert log no client side error) when space taken by the lob segment is allocated for reuse, before the undo retention period is expired.
From the Admin Guide "Automatic tuning of undo retention is not supported for LOBs. This is because undo information for LOBs is stored in the segment itself and not in the undo tablespace. For LOBs, the database attempts to honor the minimum undo retention period specified by UNDO_RETENTION. However, if space becomes low, unexpired LOB undo information may be overwritten."
From Database Concept "The database manages read consistency for LOB segments differently from other data. Instead of using undo data to record changes, the database stores the before images in the segment itself. When a transaction changes a LOB, the database allocates a new chunk and leaves the old data in place. If the transaction rolls back, then the database rolls back the changes to the index, which points to the old chunk."
Below is the test case that illustrate the reporting of ora-1691 on the alert log. The test case create the LOB as BasicFile. But the observed behavior is same for SecureFile as well. The test was done on 11.2.0.3.7 and 11.1.0.16 on Linux.
1. Create two tablespaces one for data and one for lob segments
SQL> create tablespace datatbs datafile '+data(datafile)' size 5m autoextend on next 1m maxsize 10m;
SQL>  create tablespace lobtbs datafile '+data(datafile)' size 5m autoextend on next 1m maxsize 10m;
2. Create the table with a LOB column and make sure data segments and lob segments are created in the correct tablespaces
CREATE TABLE lobtest ( ID number,  "OBJECT" BLOB ) SEGMENT CREATION IMMEDIATE TABLESPACE datatbs
  LOB ( "OBJECT" )
  STORE AS object_lob_seg ( TABLESPACE lobtbs
        DISABLE STORAGE IN ROW
        CHUNK 8K
        CACHE
        INDEX object_lob_idx (TABLESPACE lobtbs)
    );
3. The java code given at the end of the post is used to populate the table. The java code will insert a integer and a 50K BLOB.
java LobReuse 192.168.0.66 1521 ent11g2 asanga asa 141
Here (192.168.0.66 1521 ent11g2) is the (DB Server name or IP, listener port, SID) and next two parameters (asanga asa) is (username, password) and last parameter 141 is number of inserts. Verify 141 rows are inserted
SQL> select count(*) from lobtest;

  COUNT(*)
----------
       141
141 is the maximum number of inserts that could be made before the 10m maximum size is reached on the LOBTBS tablespace. Trying to insert any more rows would result in ora-1691 on client side which is the expected behavior (this is not the reason for the post).
java LobReuse 192.168.0.66 1521 ent11g2 asanga asa 1

java.sql.SQLException: ORA-01691: unable to extend lob segment ASANGA.OBJECT_LOB_SEG by 128 in tablespace LOBTBS

        at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:445)
        at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:396)
        at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:879)
        at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:450)
        at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:192)
and following lines will be reported on the alert log
ORA-1691: unable to extend lobsegment ASANGA.OBJECT_LOB_SEG by 128 in tablespace              LOBTBS
ORA-1691: unable to extend lobsegment ASANGA.OBJECT_LOB_SEG by 128 in tablespace              LOBTBS
ORA-1691: unable to extend lobsegment ASANGA.OBJECT_LOB_SEG by 128 in tablespace              LOBTBS
4. Delete all the inserted rows and verify no rows are in the table.
SQL> delete from lobtest;
141 rows deleted.

SQL> commit;
Commit complete.

SQL> select count(*) from lobtest;

  COUNT(*)
----------
         0
Deleting rows will not reduce the segment size (unless shrink space other space reclaimable mechanism are used to reduce the size). Once space is allocated to a segment it will remain allocated and reused if become free due to row deletion. At this stage the segment is occupying the full tablespace and there's no place to grow and for new inserts the log segment space must be reused.
5. Run the java code to insert another 141 rows as before.
java LobReuse 192.168.0.66 1521 ent11g2 asanga asa 141
This will execute without any errors and at the end of the execution 141 rows would have been inserted to the table

SQL> select count(*) from lobtest;

  COUNT(*)
----------
       141
However if alert log was monitored during this time following could be seen
ORA-1691: unable to extend lobsegment ASANGA.OBJECT_LOB_SEG by 128 in tablespace              LOBTBS
ORA-1691: unable to extend lobsegment ASANGA.OBJECT_LOB_SEG by 128 in tablespace              LOBTBS
ORA-1691: unable to extend lobsegment ASANGA.OBJECT_LOB_SEG by 128 in tablespace              LOBTBS
ORA-1691: unable to extend lobsegment ASANGA.OBJECT_LOB_SEG by 128 in tablespace              LOBTBS
ORA-1691: unable to extend lobsegment ASANGA.OBJECT_LOB_SEG by 128 in tablespace              LOBTBS
ORA-1691: unable to extend lobsegment ASANGA.OBJECT_LOB_SEG by 128 in tablespace              LOBTBS
This is misleading as no error was shown on the client side and 141 rows got inserted successful. But there's no way to verify this looking only at the alert log which looks like the segment is unable to expand and no rows got inserted. Having the Event 44951 set seem to reduce the number of lines reporting ora-1691 on alert log but still it does get reported. (Event 44951 sets number of chunks to clean up). Beside the error it enq: HW - contention waits were also observed.



According to Oracle this is expected behavior and not a bug. For system where automated alert log monitoring is in place it would be difficult to distinguish if there's a space issue or not without looking into free space within the lobsegment.
One way to overcome this observed behavior is to change the undo related parameters of the LOB segment, which are pctversion and retention. Creating the table with pctversion 0 eliminate the reporting of ora-1691 on the alert log when log segment space is reused.
CREATE TABLE lobtest ( ID number,  "OBJECT" BLOB ) SEGMENT CREATION IMMEDIATE TABLESPACE datatbs
  LOB ( "OBJECT" )
  STORE AS object_lob_seg ( TABLESPACE lobtbs
        DISABLE STORAGE IN ROW
        CHUNK 8K
        CACHE
        PCTVERSION 0
        INDEX object_lob_idx (TABLESPACE lobtbs)
    );
However this may not suite every application and could lead to ora-1555 snapshot too old errors. Oracle documentation provide some guide lines on how to set pctversion value based on update/read patterns of the application.
If space is not reused within the the undo_retention then again the ora-1691 is not observed on the alert log. There's no explicit way to change the retention value on LOB segment. It's taken from the undo_retention parameter.

Related Post
ORA-1691: unable to extend lobsegment Message Only on Alert Log When Inserting to a Table with SecureFile

Java code used for LOB insert
import java.sql.Connection;
import oracle.jdbc.OraclePreparedStatement;
import oracle.jdbc.pool.OracleDataSource;

/**
 *
 * @author Asanga
 */
public class LobReuse {

    public static void main(String[] args) {

      try {

            OracleDataSource pool = new OracleDataSource();
            pool.setURL("jdbc:oracle:thin:@"+args[0]+":"+args[1]+":"+args[2]);
            pool.setUser(args[3]);
            pool.setPassword(args[4]);

            int inserts = Integer.parseInt(args[5]);
            for(int i = 0 ; i < inserts ; i++){
            Connection con = pool.getConnection();

            con.setAutoCommit(false);
            byte[] x = new byte[50 * 1024];
            x[1]=10; x[40 * 1024] = 20;

            OraclePreparedStatement pr = (OraclePreparedStatement) con.prepareStatement("insert into lobtest values(?,?)");
            pr.setInt(1, i );
            pr.setBytes(2, x);
            pr.execute();
            con.commit();
            pr.close();
            con.close();
            }

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }}