Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions db/db_tunables.h
Original file line number Diff line number Diff line change
Expand Up @@ -2469,8 +2469,8 @@ REGISTER_TUNABLE("seekscan_maxsteps", "Overrides the max number of steps for a s
&gbl_seekscan_maxsteps, SIGNED, NULL, NULL, NULL, NULL);
REGISTER_TUNABLE("wal_osync", "Open WAL files using the O_SYNC flag (Default: off)", TUNABLE_BOOLEAN, &gbl_wal_osync, 0,
NULL, NULL, NULL, NULL);
REGISTER_TUNABLE("sc_headroom", "Percentage threshold for low headroom calculation. (Default: 10)", TUNABLE_DOUBLE,
&gbl_sc_headroom, INTERNAL | SIGNED, NULL, NULL, NULL, NULL);
REGISTER_TUNABLE("sc_headroom", "Minimum percent of free disk space required during schema change. (Default: 10)",
TUNABLE_INTEGER, &gbl_sc_headroom, INTERNAL | SIGNED, NULL, NULL, NULL, NULL);
REGISTER_TUNABLE("sc_protobuf", "Enable protobuf schema change object (Default: on)", TUNABLE_BOOLEAN, &gbl_sc_protobuf,
0, NULL, NULL, NULL, NULL);
REGISTER_TUNABLE("sc_current_version", "Current schema-change version (Default: " STR(SC_VERSION) ")", TUNABLE_INTEGER,
Expand Down
4 changes: 2 additions & 2 deletions schemachange/sc_records.c
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ static int report_sc_progress(struct convert_record_data *data, int now)
/* now do global sc data */
int res = print_aggregate_sc_stat(data, now, copy_sc_report_freq);
/* check headroom only if this thread printed the global stats */
if (res && check_sc_headroom(data->s, data->from, data->to)) {
if (res && check_sc_headroom(data->s, data->from)) {
if (data->s->force) {
sc_printf(data->s, "Proceeding despite low disk headroom\n");
} else {
Expand Down Expand Up @@ -1915,7 +1915,7 @@ static int upgrade_records(struct convert_record_data *data)
/* now do global sc data */
int res = print_aggregate_sc_stat(data, now, copy_sc_report_freq);
/* check headroom only if this thread printed the global stats */
if (res && check_sc_headroom(data->s, data->from, data->to)) {
if (res && check_sc_headroom(data->s, data->from)) {
if (data->s->force) {
sc_printf(data->s, "Proceeding despite low disk headroom\n");
} else {
Expand Down
98 changes: 16 additions & 82 deletions schemachange/sc_schema.c
Original file line number Diff line number Diff line change
Expand Up @@ -1245,98 +1245,32 @@ int fk_source_change(struct dbtable *newdb, FILE *out, struct schema_change_type
return 0;
}

int check_sc_headroom(struct schema_change_type *s, struct dbtable *olddb,
struct dbtable *newdb)
int check_sc_headroom(struct schema_change_type *s, struct dbtable *db)
{
uint64_t avail, wanted;
struct statvfs st;
int rc;
uint64_t headroom = gbl_sc_headroom; /* percent */
uint64_t oldsize, newsize, diff;
char b1[32], b2[32], b3[32], b4[32];

/* 1 if using the entire table size to estimate disk space needed.
0 if using the following formula:
disk_space_needed = index_width / table_width * table_size / In(2)
This assumes that the table is 100% filled and the index is randomly
inserted into and thus has a fill factor of In(2) (~0.693). So in reality
the actual disk space used should be lower than the estimate. */
int conservative = 0;
double pct = 0;
struct scplan *theplan = newdb->plan;

if (theplan == NULL /* if not using a plan */ || (theplan->dta_plan == -1) /* if rebuilding data */ ||
!theplan->plan_blobs /* if rebuilding all blobs */)
conservative = 1;
else {
/* if rebuilding any blobs (could be expanding inline portion in which case
data size increases and blob size decreases, or shrinking inline portion
in which case data size decreases and blob size increases), just estimate
disk requirement the old way. */
for (int iblb = 0, nblb = newdb->numblobs; iblb != nblb; ++iblb) {
if (theplan->blob_plan[iblb] == -1) {
conservative = 1;
break;
}
}

if (!conservative) {
int iix, nix, lrl;
struct schema *ix;
for (iix = 0, nix = newdb->nix, lrl = newdb->lrl; iix != nix; ++iix) {
if (theplan->ix_plan[iix] == -1) {
ix = newdb->ixschema[iix];
/* If an index has a blob field or is datacopy, the size
will rise based on the blob size or data size. So still
use the old way here. */
if (!ix->ix_blob && !(ix->flags & (SCHEMA_DATACOPY | SCHEMA_PARTIALDATACOPY)))
pct += get_size_of_schema(ix) / (double)lrl;
}
}

/* Neither an instant schema change, nor rebuilding data, indexes and blobs.
How can this happen? */
if (pct == 0)
conservative = 1;
}
}

oldsize = calc_table_size(olddb, 0);
newsize = calc_table_size(newdb, 0);
uint64_t headroom = gbl_sc_headroom; /* minimum percent free space required */
char b1[32], b2[32];

if (conservative) {
if (newsize > oldsize)
diff = oldsize / 3; /* newdb already larger; assume 33% growth */
else
diff = oldsize - newsize;
} else {
/* In case there's a miscalculation. */
if (newsize > oldsize)
diff = oldsize / 3;
else if ((pct * oldsize) / 0.693 > newsize)
diff = (pct * oldsize) / 0.693 - newsize;
else
diff = oldsize - newsize;
}

wanted = (diff * (uint64_t)(100 + headroom)) / 100ULL;

rc = statvfs(olddb->dbenv->basedir, &st);
rc = statvfs(db->dbenv->basedir, &st);
if (rc == -1) {
sc_errf(s, "cannot get file system data for %s: %d %s\n",
olddb->dbenv->basedir, errno, strerror(errno));
sc_errf(s, "cannot get file system data for %s: %d %s\n", db->dbenv->basedir, errno, strerror(errno));
return -1;
}

avail = (uint64_t)st.f_bavail * (uint64_t)st.f_frsize;
uint64_t total = (uint64_t)st.f_blocks * (uint64_t)st.f_frsize;
uint64_t avail = (uint64_t)st.f_bavail * (uint64_t)st.f_frsize;
uint64_t pct_free = total > 0 ? (avail * 100) / total : 0;

sc_printf(
s, "Table %s, old %s, new %s, reqd. %s, avail %s\n", olddb->tablename,
fmt_size(b1, sizeof(b1), oldsize), fmt_size(b2, sizeof(b2), newsize),
fmt_size(b3, sizeof(b3), wanted), fmt_size(b4, sizeof(b4), avail));
sc_printf(s, "total disk space %s, available disk space %s, %llu%% free (threshold %llu%%)\n",
fmt_size(b1, sizeof(b1), total), fmt_size(b2, sizeof(b2), avail), (unsigned long long)pct_free,
(unsigned long long)headroom);

if (wanted > avail) {
sc_errf(s, "DANGER low headroom for schema change\n");
if (pct_free < headroom) {
sc_errf(s,
"DANGER low disk space for schema change: "
"%llu%% free, need %llu%%\n",
(unsigned long long)pct_free, (unsigned long long)headroom);
sc_client_error(s, "server is running low on disk space");
return -1;
}
Expand Down
3 changes: 1 addition & 2 deletions schemachange/sc_schema.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,7 @@ int backout_constraint_pointers(struct dbtable *db, struct dbtable *newdb);

int fk_source_change(struct dbtable *newdb, FILE *out, struct schema_change_type *s);

int check_sc_headroom(struct schema_change_type *s, struct dbtable *olddb,
struct dbtable *newdb);
int check_sc_headroom(struct schema_change_type *s, struct dbtable *db);

int compat_chg(struct dbtable *olddb, struct schema *s2, const char *ixname);

Expand Down
3 changes: 3 additions & 0 deletions tests/sc_force.test/expected
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
schema change report frequency is now 1 seconds
tag ondisk {
int b null = yes
}
[alter table t {tag ondisk {
int b null = yes
}}] failed with rc 240 server is running low on disk space
Expand Down
1 change: 0 additions & 1 deletion tests/sc_force.test/lrl.options
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
table t t1.csc2
sc_headroom 1000000000
55 changes: 46 additions & 9 deletions tests/sc_force.test/runit
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
bash -n "$0" | exit 1
dbnm=$1

fixednode=`cdb2sql --tabs ${CDB2_OPTIONS} $dbnm default 'select comdb2_host()'`
[[ "$debug" == "1" ]] && set -x

fixednode=`cdb2sql --tabs ${CDB2_OPTIONS} $dbnm default 'select comdb2_host()'`
master=`cdb2sql --tabs ${CDB2_OPTIONS} $dbnm --host $fixednode 'exec procedure sys.cmd.send("bdb cluster")' | grep MASTER | awk '{print $1}' | cut -d':' -f1`

### create and populate the table
### Create and populate the table
cat << EOF | cdb2sql ${CDB2_OPTIONS} -s --tabs $dbnm --host $fixednode - >actual 2>&1
drop table if exists t
create table t {`cat t1.csc2`}\$\$
Expand All @@ -17,32 +18,68 @@ insert into t select * from generate_series(1,1000000)
COMMIT
EOF

### set env to delay sc and increase sc status reporting frequency
### Set env to increase sc status reporting frequency
cat << EOF | cdb2sql ${CDB2_OPTIONS} -s --tabs $dbnm --host $master - >>actual 2>&1
exec procedure sys.cmd.send('screportfreq 1')
EOF

cdb2sql --tabs ${CDB2_OPTIONS} $dbnm --host $master "exec procedure sys.cmd.send('flush')"

### try to schemachange without force
# Calculate free space percentage after populating
dbdir="${TESTDIR}/${dbnm}"
df_output=$(df -P "$dbdir" | tail -1)
total_blocks=$(echo "$df_output" | awk '{print $2}')
avail_blocks=$(echo "$df_output" | awk '{print $4}')
pct_free=$((avail_blocks * 100 / total_blocks))

### TEST 1: Set headroom to current_free% - 5 (should SUCCEED - have more space than required)
required_pct=$((pct_free - 5))
cdb2sql --tabs ${CDB2_OPTIONS} $dbnm --host $master "PUT TUNABLE sc_headroom ${required_pct}" >/dev/null 2>&1

cat << EOF | cdb2sql ${CDB2_OPTIONS} -s --tabs $dbnm --host $fixednode - >>actual 2>&1
alter table t {`cat t2.csc2`}
EOF

### verify that schemachange failed
# Verify schema changed to int b (success)
cat << EOF | cdb2sql ${CDB2_OPTIONS} -s --tabs $dbnm --host $fixednode - >>actual 2>&1
select csc2 from sqlite_master where tbl_name='t'
EOF

### try to schemachange with force
### Reset table back to int a for next test
cat << EOF | cdb2sql ${CDB2_OPTIONS} -s --tabs $dbnm --host $fixednode - >>actual 2>&1
alter table t options force {`cat t2.csc2`}
alter table t {`cat t1.csc2`}
EOF

### TEST 2: Set headroom to current_free% + 5 (should FAIL - need more space than we have)
required_pct=$((pct_free + 5))
cdb2sql --tabs ${CDB2_OPTIONS} $dbnm --host $master "PUT TUNABLE sc_headroom ${required_pct}" >/dev/null 2>&1

cat << EOF | cdb2sql ${CDB2_OPTIONS} -s --tabs $dbnm --host $fixednode - >>actual 2>&1
alter table t {`cat t2.csc2`}
EOF

# Verify schema is still int a (failure as expected)
cat << EOF | cdb2sql ${CDB2_OPTIONS} -s --tabs $dbnm --host $fixednode - >>actual 2>&1
select csc2 from sqlite_master where tbl_name='t'
EOF

### verify that schemachange succeeded
### TEST 3: Try with force option (should SUCCEED despite low disk space)
cat << EOF | cdb2sql ${CDB2_OPTIONS} -s --tabs $dbnm --host $fixednode - >>actual 2>&1
alter table t options force {`cat t2.csc2`}
EOF

# Verify schema changed to int b (force succeeded)
cat << EOF | cdb2sql ${CDB2_OPTIONS} -s --tabs $dbnm --host $fixednode - >>actual 2>&1
select csc2 from sqlite_master where tbl_name='t'
EOF

diff actual expected
if diff -q actual expected; then
echo "test passed"
else
echo "first 10 diff:"
diff actual expected | head -10
echo "test failed"
exit 1
fi

exit 0
Loading