Chapter 3. libjf basics

3.1. Many journaled files, one journal

In the previous chapter we introduced a bit of libjf using only one journaled file at a time: that usage may help you in writing easier programs because you don't have to implement the "back-out" logic, it's a gift of libjf.

Now it's time to investigate how libjf can solve "data integrity issue" at its root; the following examples will exploit only two journaled files, but the principle can be extended to any number.

Example 3-1. two_files.c

     1	#include <jf_file.h>
       
     2	int main()
     3	{
     4	        int rc;
     5	        size_t write;
     6	        jf_journal_t j;
     7	        jf_file_t jf1, jf2;
     8	        struct jf_journal_opts_s jopts;
     9	        struct jf_file_open_opts_s fopts;
    10	        const char *file1_data1 = "First string for first file\n";
    11	        const char *file1_data2 = "Second string for first file\n";
    12	        const char *file2_data1 = "First string for second file\n";
    13	        const char *file2_data2 = "Second string for second file\n";
       
    14	        jf_set_default_journal_opts(&jopts);
    15	        jopts.flags |= JF_JOURNAL_PROP_OPEN_O_CREAT |
    16	                JF_JOURNAL_PROP_OPEN_O_EXCL;
    17	        rc = jf_journal_open(&j, "jf_tut_foo-journal", 2, &jopts);
    18	        if (JF_RC_OK != rc) {
    19	                printf("%d/%s\n", rc, jf_strerror(rc));
    20	                return 1;
    21	        }
       
    22	        jf_set_default_file_open_opts(&fopts);
    23	        fopts.join_the_journal = TRUE;
    24	        rc = jf_file_open(&jf1, &j, "jf_tut_foo-data1", "w", &fopts);
    25	        if (JF_RC_OK != rc) {
    26	                printf("%d/%s\n", rc, jf_strerror(rc));
    27	                return 1;
    28	        }
    29	        rc = jf_file_open(&jf2, &j, "jf_tut_foo-data2", "w", &fopts);
    30	        if (JF_RC_OK != rc) {
    31	                printf("%d/%s\n", rc, jf_strerror(rc));
    32	                return 1;
    33	        }
       
    34	        rc = jf_file_write(&jf1, file1_data1, strlen(file1_data1),
    35	                           &write);
    36	        if (JF_RC_OK != rc) {
    37	                printf("%d/%s\n", rc, jf_strerror(rc));
    38	                return 1;
    39	        }
    40	        rc = jf_file_write(&jf2, file2_data1, strlen(file2_data1),
    41	                           &write);
    42	        if (JF_RC_OK != rc) {
    43	                printf("%d/%s\n", rc, jf_strerror(rc));
    44	                return 1;
    45	        }
    46	        rc = jf_journal_rollback(&j);
    47	        if (JF_RC_OK != rc) {
    48	                printf("%d/%s\n", rc, jf_strerror(rc));
    49	                return 1;
    50	        }
       
    51	        rc = jf_file_write(&jf1, file1_data2, strlen(file1_data2),
    52	                           &write);
    53	        if (JF_RC_OK != rc) {
    54	                printf("%d/%s\n", rc, jf_strerror(rc));
    55	                return 1;
    56	        }
    57	        rc = jf_file_write(&jf2, file2_data2, strlen(file2_data2),
    58	                           &write);
    59	        if (JF_RC_OK != rc) {
    60	                printf("%d/%s\n", rc, jf_strerror(rc));
    61	                return 1;
    62	        }
    63	        rc = jf_journal_commit(&j);
    64	        if (JF_RC_OK != rc) {
    65	                printf("%d/%s\n", rc, jf_strerror(rc));
    66	                return 1;
    67	        }
       
    68	        rc = jf_file_close(&jf1);
    69	        if (JF_RC_OK != rc) {
    70	                printf("%d/%s\n", rc, jf_strerror(rc));
    71	                return 1;
    72	        }
    73	        rc = jf_file_close(&jf2);
    74	        if (JF_RC_OK != rc) {
    75	                printf("%d/%s\n", rc, jf_strerror(rc));
    76	                return 1;
    77	        }
    78	        rc = jf_journal_close(&j);
    79	        if (JF_RC_OK != rc) {
    80	                printf("%d/%s\n", rc, jf_strerror(rc));
    81	                return 1;
    82	        }
       
    83	        printf("two_files program ended OK!\n");
    84	        return 0;
    85	}
      

two_files.c code explanation

Row 1

to use libjf a program must include at least jf_file.h header file

Row 6

declare object j of type jf_journal_t: j is a "journal object"

Row 7

declare object jf1, jf2 of type jf_file_t: jf1 and jf2 are a "journaled file objects"

Row 8

declare a struct will be used to store options related to journal

Row 9

declare a struct will be used to store options related to journaled files

Row 14

set default value for options related to journal

Rows 15-16

add some flags to journal related options: the journal must be created and the journal must not exist

Row 17

open the journal file jf_tut_foo-journal and associate it to journal object j, the journal will handle 2 journaled files and use options specified by struct jopts

Row 18

check previous operation return code: print the return code and its human readable description if something goes wrong

Row 22

set default value for options related to journaled files

Row 23

specify the journaled file must "join the journal"; joining a journal means the journal will store all transactional information about the journaled file

Row 24

open journaled file jf_tut_foo-data1, associate to object jf1, use journal j, data will be written ("w") and additional options must be picked-up from fopts

Row 29

open journaled file jf_tut_foo-data2, associate to object jf2, use journal j, data will be written ("w") and additional options must be picked-up from fopts

Row 34

write string file1_data1 to journaled file jf1

Row 40

write string file2_data1 to journaled file jf2

Row 46

back out all the changes operated on all the journaled files managed by journal object j

Row 51

write string file1_data2 to journaled file jf1

Row 57

write string file2_data2 to journaled file jf2

Row 63

commit all the changes operated on all the journaled files managed by journal object j

Row 68

close journaled file jf1

Row 73

close journaled file jf2

Row 78

close journal j

3.1.1. two_files.c compile and run

To compile two_files.c you may use this libtool command:

libtool --mode=link gcc -Wall -I/opt/libjf/include -L/opt/libjf/lib \
        -ljf -o two_files two_files.c
      
run two_files program:
tiian@linux:~/tutorial> ./two_files
two_files program ended OK!
      
take a look to the files produced by two_files execution:
tiian@linux:~/tutorial> ls -la jf_tut_foo*
-rw-r--r--  1 tiian users    29 2005-08-11 16:50 jf_tut_foo-data1
-rw-r--r--  1 tiian users    30 2005-08-11 16:50 jf_tut_foo-data2
-rw-r--r--  1 tiian users 16607 2005-08-11 16:50 jf_tut_foo-journal
      
inspect first and second journaled file:
tiian@linux:~/tutorial> cat jf_tut_foo-data1
Second string for first file
tiian@linux:~/tutorial> cat jf_tut_foo-data2
Second string for second file
      
take a look to journal content with command
tiian@linux:~/tutorial> jf_report -j jf_tut_foo-journal -dt
      
the first strings, on all journaled files, were backed out by jf_journal_rollback at line 46; the second strings, on all journaled files, were committed by jf_journal_commit at line 63.

3.1.2. two_files.c interesting aspects

You should note these aspects in two_files.c source code:

  • in libjf API some data types are not "typedefed" and they must be used with the explicit reserved word "struct" (rows 8-9); this is a desired behavior because some structs should not be interpreted as "classes". This simple rule can help you understanding the idea:

    • "classes" are types with name terminating in "_t" and methods to create/destroy/manage: you must not access "properties" of an object with "methods" (functions) out of the scope of the "class"

    • "structs" are commodity data aggregation with name terminating in "_s" and no methods to manipulate them: you have to set the values of the interesting fields when necessary

  • it's a good programming practice to close journaled files before closing journal: leaving objects in open status will cause a useless, time consuming, automatic recovery at next open time; forgetting object closure may lead to memory leak bugs.

The illustrated schema is really simple: two global transactions on two journaled files, first transaction was backed out, second transaction was committed.

What happens in the event of an application crash?