Insert into Sqlite in WAL mode generating huge db-WAL file

Viewed 29

My C application uses SQLite 3.36. I am using WAL mode for my DB. But on inserting ~50K records with 7 columns, the WAL file grows up to 1.5 GB. I have disabled auto checkpointing and doing manual checkpointing after all insertions. I already went through many articles which blame checkpointing for the huge WAL size. But,I guess checkpointing is not the issue here because other tables exist in the same DB where the record count is more than 50k but the WAL file size remains in MBs on insertion. Before insertion, I am doing deletion but in that case, table contains hardly 2-3 records so WAL size is not impacted much (Confirmed by debugging the code). During debugging I observed, the WAL size increasing very fast in for loop used for insertion (See below code). Loop completed within seconds.

Below is the code - (Added comments for a few local methods and macros ) Note- Code is running fine without any error and on checkpointing, I am getting desired records in the original DB. No other read operation going on this table during the execution of this routine.

Kindly, help me to know what I am doing wrong here which is causing the WAL file to grow up to 1.5GB. Is insert after delete causing some issues?

int generatefMap(struct ixdb* xdb)
{
    struct fmap* fmap;
    uint64_t max_id, size;
    int status = 0;
    sqlite3_stmt* stmt = NULL;
    int stmt_id = 0;
    int retry_count = 0;
    char* sql = NULL;
    max_id = 0;
    fmap = NULL;

    status = ixdb_largest_id(xdb, &max_id);
    if (status != 0 || max_id == 0) {
        if (status == E_NOTFOUND) {
            status = 0;
        }
        else {
            return E_INVALID;
        }
    }

    sql = "DELETE FROM fmap; ";
    status = sqlite3_exec(xdb->dbh->db, sql, NULL, NULL, NULL);
    if (status != 0) {
        return status;
    }

    size = (max_id + 1) * sizeof(struct fnode);

    fmap = (struct fmap*)mem_alloc(sizeof(struct fmap)); // #define mem_alloc(size) calloc(1, (size))
    if (fmap == NULL) {
        status = E_MEM;
        return status;
    }

    fmap->hash = (struct fnode*)mem_alloc((size_t)size);// #define mem_alloc(size)  calloc(1, (size))
    if (fmap->hash == NULL) { 
        free(fmap);
        status = E_MEM;
        return status;
    }

    fmap->size = size;
    
    fmap->max_id = max_id;
    status = __fmap_gen(fmap, xdb); //Gives fmap by selecting/querying data from other table in same DB
    if (status != 0) {
        free(fmap->hash);
        free(fmap);
        return status;
    }
    //Insert into DB
    stmt_id = XDB_FMAP_INSERT; //INSERT OR REPLACE INTO fmap(id,pid,xt,size,xtime,ytime,selected) values (:id,:pid,:xt,:size,:xtime,:ytime,:selected);
    stmt = get_prepared_stmt(xdb, stmt_id); // This will get me prepared statement for above query
    for (uint64_t i = 0; i < max_id + 1; ++i) {
        sqlite3_reset(stmt);
        sqlite3_clear_bindings(stmt);
        sqlite3_bind_int64(stmt, 1, (uint64_t)(i + 1));
        sqlite3_bind_int64(stmt, 2, fmap->hash[i].pid);
        sqlite3_bind_int64(stmt, 3, fmap->hash[i].xtid);
        sqlite3_bind_int64(stmt, 4, fmap->hash[i].size);
        sqlite3_bind_int64(stmt, 5, fmap->hash[i].xtime);
        sqlite3_bind_int64(stmt, 6, fmap->hash[i].ytime);
        sqlite3_bind_int64(stmt, 7, fmap->hash[i].selected);
    retry:
        status = sqlite3_step(stmt);
        if (status == SQLITE_BUSY || status == SQLITE_IOERR_BLOCKED) {
            printf("sqlite_step returned: %d . Hence retrying ", status);
            ++retry_count;
            if (retry_count <= ATTEMPTS) { //#define ATTEMPTS 5
                sleep(SLEEP); //#define SLEEP 1
                goto retry;
            }
            printf("Maximum number of retries(%d) exhausted|stmt=%s|err=%d", ATTEMPTS, sqlite3_expanded_sql(stmt), status);
            goto err;
        }
        else if (status != SQLITE_DONE) {
            printf("\nsqlite3_step() failed with error[%d].\n", status);
            status = ERR_SQLITE;
            goto err;
        }
        status = 0;
    }
    if (stmt) {
        sqlite3_reset(stmt);
        sqlite3_clear_bindings(stmt);
    }
err:
    if (fmap->hash) {
        free(fmap->hash);
        fmap->hash = NULL;
    }
    if (fmap) {
        free(fmap);
        fmap = NULL;
    }
    return status;
}
0 Answers
Related