Caching with RWDBInserter
With
RWDBInserter, inserted rows may be cached locally and sent to the database in one operation. This procedure allows more optimal use of network packet sizes and server resources than sending individual rows separately.
When the caching feature of
RWDBInserter is enabled, individual rows are stored in the
RWDBInserter object’s internal buffers and then sent all at once to the database, typically by using the database vendor’s array binding interface. To enable the caching features when producing an
RWDBInserter, we pass a cache size of
n > 1 to its producer method. The following example shows how to use an inserter with a cache size of
5.
Example 15 – Caching with RWDBInserter
RWDBConnection aConn = aDb.connection();
RWDBInserter inserter = myTable.inserter(5); // 1
inserter << 0; // 2
inserter.execute(aConn); // 3
inserter << 1; // 4
inserter.execute(aConn); // 5
inserter << 2; // 6
inserter.execute(aConn); // 7
inserter << 3; // 8
inserter.execute(aConn); // 9
inserter << 4; //10
inserter.execute(aConn); //11
inserter << 5; //12
inserter.execute(aConn); //13
inserter.flush(); //14
In
//1, we produce an inserter with a cache size of
5, from an existing
RWDBTable object. In
//2 -
//13, we execute the inserter, inserting a new value between each execution as we would with an inserter that does no caching. There are only two differences between coding this example and coding an example that does not use a cached inserter: in
//1, we set the cache size, and in
//14, we flush the contents of the cache.
Now let’s see what happens to the inserter internally. In //1, when we create the inserter with a cache size of 5, the inserter creates an internal buffer big enough to hold five rows of data. In //2, we shift a row of data into the inserter. This row is stored in the first location in the inserter's internal buffers, and the inserter notes that it has cached one row.
When we execute in //3, the inserter doesn't send the row that was just shifted into itself to the database. Instead, the inserter compares the number of rows already cached to the cache size it was assigned at construction. Since the cache is not at capacity — in fact, it holds only one row at this point — the inserter returns immediately to the application.
In //4 - //5, the value of 1 is shifted into the inserter, and the inserter is executed again. The inserter notes that it has now cached two rows, and returns to the application. This pattern is repeated in //6 - //11. By the time we reach //11, the inserter has cached five rows. At this point, the number of rows cached is equal to the cache size, and the inserter sends all five cached rows to the database server at once. By using the inserter in this way, the application saves the cost of four network round trips. In our example, one more line of data is shifted into the cache and then we are done.
As we mentioned previously, the inserter's flush() method is invoked in //14. Actually, the flush operation was first invoked in //11 because the method is invoked automatically whenever the cache reaches its assigned capacity. In this example, the cache reached capacity in //11. But what about the final piece of data? We invoke flush() in //14 to ensure that all data is sent to the server. This is good programming practice, although another implicit invocation of flush() occurs when the inserter goes out of scope and its destructor is called.