When an Oracle process starts executing a query and needs to do a full segment scan, it needs to make a decision if it’s going to use ‘blockmode’, which is the normal way of working on non-Exadata Oracle databases, where blocks are read from disk and processed by the Oracle foreground process, either “cached” (read from disk and put in the database buffercache) or “direct” (read from disk and put in the process’ PGA), or ‘offloaded mode’, where part of the execution is done by the cell server.
The code layer where the Oracle database process initiates the offloading is ‘kcfis’; an educated guess is Kernel Cache File Intelligent Storage. Does a “normal” alias non-Exadata database ever use the ‘kcfis’ layer? My first guess would be ‘no’, but we all know guessing takes you nowhere (right?). Let’s see if a “normal” database uses the ‘kcfis’ functions on a Linux x64 (OL 6.3) system with Oracle 18.104.22.168 64 bit using ASM.
The only way to profile kernel functions that I am aware of is using ‘gdb’ and breaking on functions in the Oracle executable:
(the process id shown below ought to be the process id of an oracle database process id you are going to execute in. Do not, I repeat: not do this with other processes, especially the ones that do important tasks!)
# gdb -p 42 GNU gdb (GDB) Red Hat Enterprise Linux (7.2-56.el6) Copyright (C) 2010 Free Software Foundation, Inc. ... (gdb) rbreak ^kcfis.* Breakpoint 1 at 0x2204094 <function, no debug info> kcfis_get_sched_delay; Breakpoint 2 at 0x220410a <function, no debug info> kcfis_capability_tab_get; Breakpoint 3 at 0x2204150 <function, no debug info> kcfis_can_session_migrate; Breakpoint 4 at 0x2204190 <function, no debug info> kcfis_fob_storage_attr; Breakpoint 5 at 0x22041d0 <function, no debug info> kcfis_init_resource_limit; Breakpoint 6 at 0x22041f2 <function, no debug info> kcfis_tablespace_is_on_sage; ... (gdb) c Continuing.
Okay, we got the debugger set, now let’s execute a simple query (doing a full scan) to see if kcfis is touched on a “normal” server or not!
TS@v11203 > select count(*) from t2;
I am on OS-X (Apple) using iTerm, and I see the tab of the gdb session turning red: gdb generated some output on the screen!
(gdb) c Continuing. Breakpoint 6, 0x00000000022041f2 in kcfis_tablespace_is_on_sage () (gdb)
So, we hit a kcfis function! Let me put upfront that I do not have sourcecode of any kind, so my statements about what a function does are actually guesses. Anyway: from the name of the function (kcfis_tablespace_is_on_sage) it looks like a function in the kcfis layer which determines if a tablespace is on an Exadata storage server (exadata’s internal name is/was ‘sage’). Nice. so we hit the layer for determination if the tablespace is on Exadata.
Let’s continue the profiling with gdb:
(gdb) c Continuing. Breakpoint 2, 0x000000000220410a in kcfis_capability_tab_get () (gdb) c Continuing. Breakpoint 6, 0x00000000022041f2 in kcfis_tablespace_is_on_sage () (gdb) c Continuing.
We see another call to the function ‘kcfis_tablespace_is_on_sage’, and a call to ‘kcfis_capability_tab_get’. The last function probably tries to probe the table (but could be tablespace) to get the capabilities. This could be the function which checks the requirements for hybrid columnar compression, I am not sure about that.
At what point during the processing of the full segment scan does the kcfis_tablespace_is_on_sage occur? One way of investigating this, is profiling some functions we know a (direct path) full scan does, and see where the kcfis_tablespace_is_on_sage kicks in. When the buffer cache is flushed prior to executing a full scan, and the SQL is made unique, so it has to be parsed, the following sequence of events happens:
- A ‘SQL*Net message to client’ wait
- A ‘db file sequential read’ wait (for reading the segment header)
- Potentially a ‘asynch descriptor resize’ wait
- The full scan is done asynchronously (potentially revealing some ‘direct path read’ waits)
So if we profile on start and end of a wait (kslwtbctx and kslwtectx), a single block read (pread64), AIO (io_submit and io_getevents_0_4) and of course kcfis_tablespace_is_on_sage, we should be able to see that:
Breakpoint 3, 0x0000000008fa1334 in kslwtectx () Breakpoint 2, 0x0000000008f9a652 in kslwtbctx () Breakpoint 3, 0x0000000008fa1334 in kslwtectx () Breakpoint 2, 0x0000000008f9a652 in kslwtbctx () Breakpoint 1, pread64 () at ../sysdeps/unix/syscall-template.S:82 82 T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS) Breakpoint 3, 0x0000000008fa1334 in kslwtectx () Breakpoint 4, 0x00000000022041f2 in kcfis_tablespace_is_on_sage () Breakpoint 4, 0x00000000022041f2 in kcfis_tablespace_is_on_sage () Breakpoint 2, 0x0000000008f9a652 in kslwtbctx () Breakpoint 3, 0x0000000008fa1334 in kslwtectx () Breakpoint 5, io_submit (ctx=0x7fb42f475000, nr=1, iocbs=0x7fffb4c5e100) at io_submit.c:23 23 io_syscall3(int, io_submit, io_submit, io_context_t, ctx, long, nr, struct iocb **, iocbs) Breakpoint 5, io_submit (ctx=0x7fb42f475000, nr=1, iocbs=0x7fffb4c5e100) at io_submit.c:23 23 io_syscall3(int, io_submit, io_submit, io_context_t, ctx, long, nr, struct iocb **, iocbs) Breakpoint 6, io_getevents_0_4 (ctx=0x7fb42f475000, min_nr=2, nr=128, events=0x7fffb4c66768, timeout=0x7fffb4c67770) at io_getevents.c:46 46 if (ring==NULL || ring->magic != AIO_RING_MAGIC)
So what do we see here?
Row 1 : Here the wait ending for ‘SQL*Net message from client’.
Row 2-3 : This is the ‘SQL*Net message to client’ wait.
Row 5-6-7 : This is begin wait, pread64 for reading the segment header and the end wait.
Row 8&9 : The session probes for Exadata.
Row 10-11 : Start and end of a wait, ‘asynch descriptor resize’ (verified by trace file).
Row 12- : Here the full scan takes off.
So…a query starts (I’ve made it unique by adding a dummy hint, so it’s parsed again) the SQL*Net roundtrip occurs, the segment header is read, then the process looks if it’s on Exadata, which it isn’t here, and starts an asynchronous full scan.
So let’s repeat this check on a database which is on Exadata! In order to do so, we need to be aware several calls are not done on exadata: pread64, io_submit and io_getevents_0_4, because we do not do local IO, but issue them over infiniband. So I breaked on kslwtbctx, kslwtectx, kcfis_tablespace_is_on_sage. This is how that looks like:
(gdb) c Continuing. Breakpoint 2, 0x000000000905cf62 in kslwtectx () Breakpoint 3, 0x0000000002230366 in kcfis_tablespace_is_on_sage ()
Mmmh, this is different. The ‘SQL*Net message from client’ wait ends, and before any other wait occurs, the existence of exadata is checked. This is different from the non-exadata case. Let’s take a look at the backtrace of the break on kcfis_tablespace_is_on_sage:
(gdb) bt #0 0x0000000002230366 in kcfis_tablespace_is_on_sage () #1 0x0000000001402eb0 in qesSageEnabled () #2 0x0000000009234d20 in kkdlgstd () #3 0x0000000001a6111d in kkmfcblo () #4 0x000000000922f76d in kkmpfcbk () #5 0x000000000942e538 in qcsprfro () #6 0x000000000942de29 in qcsprfro_tree () #7 0x000000000942de6e in qcsprfro_tree () #8 0x0000000002dd80c5 in qcspafq () #9 0x0000000002dd51d9 in qcspqbDescendents () #10 0x0000000002dd91e4 in qcspqb () #11 0x0000000001a6b2be in kkmdrv () #12 0x0000000002584c76 in opiSem () #13 0x000000000258ac8b in opiDeferredSem () #14 0x000000000257dc32 in opitca () #15 0x0000000001ec3d7d in kksFullTypeCheck () #16 0x00000000092a7256 in rpiswu2 () #17 0x0000000001eca977 in kksLoadChild () #18 0x0000000009298448 in kxsGetRuntimeLock () #19 0x000000000925aa34 in kksfbc () #20 0x000000000925556e in kkspsc0 () #21 0x0000000009254e6a in kksParseCursor () #22 0x000000000933cb25 in opiosq0 () #23 0x0000000001b82a46 in kpooprx () #24 0x0000000001b80d2c in kpoal8 () #25 0x00000000091fb8b8 in opiodr () #26 0x000000000939e696 in ttcpip () #27 0x000000000180f011 in opitsk () #28 0x0000000001813c0a in opiino () #29 0x00000000091fb8b8 in opiodr () #30 0x000000000180af4c in opidrv () #31 0x0000000001e0a77b in sou2o () #32 0x0000000000a0cc05 in opimai_real () #33 0x0000000001e106ec in ssthrdmain () #34 0x0000000000a0cb71 in main ()
What is interesting to see, is line 23, backtrace layer number 21: kksParseCursor. So actually during parsing the detection of storage servers happens already, not when it actually starts a full segment scan needs to make a decision to do a smartscan or not.