@@ -978,212 +978,199 @@ static int stack_compact_range(struct reftable_stack *st,
978
978
size_t first , size_t last ,
979
979
struct reftable_log_expiry_config * expiry )
980
980
{
981
- char * * delete_on_success = NULL , * * subtable_locks = NULL , * * listp = NULL ;
982
- struct strbuf temp_tab_file_name = STRBUF_INIT ;
981
+ struct strbuf tables_list_buf = STRBUF_INIT ;
982
+ struct strbuf new_table_temp_path = STRBUF_INIT ;
983
983
struct strbuf new_table_name = STRBUF_INIT ;
984
- struct strbuf lock_file_name = STRBUF_INIT ;
985
- struct strbuf ref_list_contents = STRBUF_INIT ;
986
984
struct strbuf new_table_path = STRBUF_INIT ;
987
- size_t i , j , compact_count ;
988
- int err = 0 ;
989
- int have_lock = 0 ;
990
- int lock_file_fd = -1 ;
991
- int is_empty_table = 0 ;
985
+ struct strbuf table_name = STRBUF_INIT ;
986
+ struct lock_file tables_list_lock = LOCK_INIT ;
987
+ struct lock_file * table_locks = NULL ;
988
+ int is_empty_table = 0 , err = 0 ;
989
+ size_t i ;
992
990
993
991
if (first > last || (!expiry && first == last )) {
994
992
err = 0 ;
995
993
goto done ;
996
994
}
997
995
998
- compact_count = last - first + 1 ;
999
- REFTABLE_CALLOC_ARRAY (delete_on_success , compact_count + 1 );
1000
- REFTABLE_CALLOC_ARRAY (subtable_locks , compact_count + 1 );
1001
-
1002
996
st -> stats .attempts ++ ;
1003
997
1004
- strbuf_reset ( & lock_file_name );
1005
- strbuf_addstr ( & lock_file_name , st -> list_file );
1006
- strbuf_addstr ( & lock_file_name , ".lock" );
1007
-
1008
- lock_file_fd =
1009
- open ( lock_file_name . buf , O_EXCL | O_CREAT | O_WRONLY , 0666 );
1010
- if (lock_file_fd < 0 ) {
1011
- if (errno == EEXIST ) {
998
+ /*
999
+ * Hold the lock so that we can read "tables.list" and lock all tables
1000
+ * which are part of the user-specified range.
1001
+ */
1002
+ err = hold_lock_file_for_update ( & tables_list_lock , st -> list_file ,
1003
+ LOCK_NO_DEREF );
1004
+ if (err < 0 ) {
1005
+ if (errno == EEXIST )
1012
1006
err = 1 ;
1013
- } else {
1007
+ else
1014
1008
err = REFTABLE_IO_ERROR ;
1015
- }
1016
1009
goto done ;
1017
1010
}
1018
- /* Don't want to write to the lock for now. */
1019
- close (lock_file_fd );
1020
- lock_file_fd = -1 ;
1021
1011
1022
- have_lock = 1 ;
1023
1012
err = stack_uptodate (st );
1024
- if (err != 0 )
1013
+ if (err )
1025
1014
goto done ;
1026
1015
1027
- for (i = first , j = 0 ; i <= last ; i ++ ) {
1028
- struct strbuf subtab_file_name = STRBUF_INIT ;
1029
- struct strbuf subtab_lock = STRBUF_INIT ;
1030
- int sublock_file_fd = -1 ;
1031
-
1032
- stack_filename (& subtab_file_name , st ,
1033
- reader_name (st -> readers [i ]));
1034
-
1035
- strbuf_reset (& subtab_lock );
1036
- strbuf_addbuf (& subtab_lock , & subtab_file_name );
1037
- strbuf_addstr (& subtab_lock , ".lock" );
1016
+ /*
1017
+ * Lock all tables in the user-provided range. This is the slice of our
1018
+ * stack which we'll compact.
1019
+ */
1020
+ REFTABLE_CALLOC_ARRAY (table_locks , last - first + 1 );
1021
+ for (i = first ; i <= last ; i ++ ) {
1022
+ stack_filename (& table_name , st , reader_name (st -> readers [i ]));
1038
1023
1039
- sublock_file_fd = open (subtab_lock .buf ,
1040
- O_EXCL | O_CREAT | O_WRONLY , 0666 );
1041
- if (sublock_file_fd >= 0 ) {
1042
- close (sublock_file_fd );
1043
- } else if (sublock_file_fd < 0 ) {
1044
- if (errno == EEXIST ) {
1024
+ err = hold_lock_file_for_update (& table_locks [i - first ],
1025
+ table_name .buf , LOCK_NO_DEREF );
1026
+ if (err < 0 ) {
1027
+ if (errno == EEXIST )
1045
1028
err = 1 ;
1046
- } else {
1029
+ else
1047
1030
err = REFTABLE_IO_ERROR ;
1048
- }
1031
+ goto done ;
1049
1032
}
1050
1033
1051
- subtable_locks [j ] = subtab_lock .buf ;
1052
- delete_on_success [j ] = subtab_file_name .buf ;
1053
- j ++ ;
1054
-
1055
- if (err != 0 )
1034
+ /*
1035
+ * We need to close the lockfiles as we might otherwise easily
1036
+ * run into file descriptor exhaustion when we compress a lot
1037
+ * of tables.
1038
+ */
1039
+ err = close_lock_file_gently (& table_locks [i - first ]);
1040
+ if (err < 0 ) {
1041
+ err = REFTABLE_IO_ERROR ;
1056
1042
goto done ;
1043
+ }
1057
1044
}
1058
1045
1059
- err = unlink (lock_file_name .buf );
1060
- if (err < 0 )
1046
+ /*
1047
+ * We have locked all tables in our range and can thus release the
1048
+ * "tables.list" lock while compacting the locked tables. This allows
1049
+ * concurrent updates to the stack to proceed.
1050
+ */
1051
+ err = rollback_lock_file (& tables_list_lock );
1052
+ if (err < 0 ) {
1053
+ err = REFTABLE_IO_ERROR ;
1061
1054
goto done ;
1062
- have_lock = 0 ;
1063
-
1064
- err = stack_compact_locked (st , first , last , & temp_tab_file_name ,
1065
- expiry );
1066
- /* Compaction + tombstones can create an empty table out of non-empty
1067
- * tables. */
1068
- is_empty_table = (err == REFTABLE_EMPTY_TABLE_ERROR );
1069
- if (is_empty_table ) {
1070
- err = 0 ;
1071
1055
}
1072
- if (err < 0 )
1073
- goto done ;
1074
1056
1075
- lock_file_fd =
1076
- open (lock_file_name .buf , O_EXCL | O_CREAT | O_WRONLY , 0666 );
1077
- if (lock_file_fd < 0 ) {
1078
- if (errno == EEXIST ) {
1057
+ /*
1058
+ * Compact the now-locked tables into a new table. Note that compacting
1059
+ * these tables may end up with an empty new table in case tombstones
1060
+ * end up cancelling out all refs in that range.
1061
+ */
1062
+ err = stack_compact_locked (st , first , last , & new_table_temp_path , expiry );
1063
+ if (err < 0 ) {
1064
+ if (err != REFTABLE_EMPTY_TABLE_ERROR )
1065
+ goto done ;
1066
+ is_empty_table = 1 ;
1067
+ }
1068
+
1069
+ /*
1070
+ * Now that we have written the new, compacted table we need to re-lock
1071
+ * "tables.list". We'll then replace the compacted range of tables with
1072
+ * the new table.
1073
+ */
1074
+ err = hold_lock_file_for_update (& tables_list_lock , st -> list_file ,
1075
+ LOCK_NO_DEREF );
1076
+ if (err < 0 ) {
1077
+ if (errno == EEXIST )
1079
1078
err = 1 ;
1080
- } else {
1079
+ else
1081
1080
err = REFTABLE_IO_ERROR ;
1082
- }
1083
1081
goto done ;
1084
1082
}
1085
- have_lock = 1 ;
1083
+
1086
1084
if (st -> config .default_permissions ) {
1087
- if (chmod (lock_file_name .buf , st -> config .default_permissions ) < 0 ) {
1085
+ if (chmod (get_lock_file_path (& tables_list_lock ),
1086
+ st -> config .default_permissions ) < 0 ) {
1088
1087
err = REFTABLE_IO_ERROR ;
1089
1088
goto done ;
1090
1089
}
1091
1090
}
1092
1091
1093
- format_name (& new_table_name , st -> readers [first ]-> min_update_index ,
1094
- st -> readers [last ]-> max_update_index );
1095
- strbuf_addstr (& new_table_name , ".ref" );
1096
-
1097
- stack_filename (& new_table_path , st , new_table_name .buf );
1098
-
1092
+ /*
1093
+ * If the resulting compacted table is not empty, then we need to move
1094
+ * it into place now.
1095
+ */
1099
1096
if (!is_empty_table ) {
1100
- /* retry? */
1101
- err = rename (temp_tab_file_name .buf , new_table_path .buf );
1097
+ format_name (& new_table_name , st -> readers [first ]-> min_update_index ,
1098
+ st -> readers [last ]-> max_update_index );
1099
+ strbuf_addstr (& new_table_name , ".ref" );
1100
+ stack_filename (& new_table_path , st , new_table_name .buf );
1101
+
1102
+ err = rename (new_table_temp_path .buf , new_table_path .buf );
1102
1103
if (err < 0 ) {
1103
1104
err = REFTABLE_IO_ERROR ;
1104
1105
goto done ;
1105
1106
}
1106
1107
}
1107
1108
1108
- for (i = 0 ; i < first ; i ++ ) {
1109
- strbuf_addstr (& ref_list_contents , st -> readers [i ]-> name );
1110
- strbuf_addstr (& ref_list_contents , "\n" );
1111
- }
1112
- if (!is_empty_table ) {
1113
- strbuf_addbuf (& ref_list_contents , & new_table_name );
1114
- strbuf_addstr (& ref_list_contents , "\n" );
1115
- }
1116
- for (i = last + 1 ; i < st -> merged -> stack_len ; i ++ ) {
1117
- strbuf_addstr (& ref_list_contents , st -> readers [i ]-> name );
1118
- strbuf_addstr (& ref_list_contents , "\n" );
1119
- }
1120
-
1121
- err = write_in_full (lock_file_fd , ref_list_contents .buf , ref_list_contents .len );
1122
- if (err < 0 ) {
1123
- err = REFTABLE_IO_ERROR ;
1124
- unlink (new_table_path .buf );
1125
- goto done ;
1126
- }
1127
-
1128
- err = fsync_component (FSYNC_COMPONENT_REFERENCE , lock_file_fd );
1109
+ /*
1110
+ * Write the new "tables.list" contents with the compacted table we
1111
+ * have just written. In case the compacted table became empty we
1112
+ * simply skip writing it.
1113
+ */
1114
+ for (i = 0 ; i < first ; i ++ )
1115
+ strbuf_addf (& tables_list_buf , "%s\n" , st -> readers [i ]-> name );
1116
+ if (!is_empty_table )
1117
+ strbuf_addf (& tables_list_buf , "%s\n" , new_table_name .buf );
1118
+ for (i = last + 1 ; i < st -> merged -> stack_len ; i ++ )
1119
+ strbuf_addf (& tables_list_buf , "%s\n" , st -> readers [i ]-> name );
1120
+
1121
+ err = write_in_full (get_lock_file_fd (& tables_list_lock ),
1122
+ tables_list_buf .buf , tables_list_buf .len );
1129
1123
if (err < 0 ) {
1130
1124
err = REFTABLE_IO_ERROR ;
1131
1125
unlink (new_table_path .buf );
1132
1126
goto done ;
1133
1127
}
1134
1128
1135
- err = close (lock_file_fd );
1136
- lock_file_fd = -1 ;
1129
+ err = fsync_component (FSYNC_COMPONENT_REFERENCE , get_lock_file_fd (& tables_list_lock ));
1137
1130
if (err < 0 ) {
1138
1131
err = REFTABLE_IO_ERROR ;
1139
1132
unlink (new_table_path .buf );
1140
1133
goto done ;
1141
1134
}
1142
1135
1143
- err = rename ( lock_file_name . buf , st -> list_file );
1136
+ err = commit_lock_file ( & tables_list_lock );
1144
1137
if (err < 0 ) {
1145
1138
err = REFTABLE_IO_ERROR ;
1146
1139
unlink (new_table_path .buf );
1147
1140
goto done ;
1148
1141
}
1149
- have_lock = 0 ;
1150
1142
1151
- /* Reload the stack before deleting. On windows, we can only delete the
1152
- files after we closed them.
1153
- */
1143
+ /*
1144
+ * Reload the stack before deleting the compacted tables. We can only
1145
+ * delete the files after we closed them on Windows, so this needs to
1146
+ * happen first.
1147
+ */
1154
1148
err = reftable_stack_reload_maybe_reuse (st , first < last );
1149
+ if (err < 0 )
1150
+ goto done ;
1155
1151
1156
- listp = delete_on_success ;
1157
- while (* listp ) {
1158
- if (strcmp (* listp , new_table_path .buf )) {
1159
- unlink (* listp );
1160
- }
1161
- listp ++ ;
1152
+ /*
1153
+ * Delete the old tables. They may still be in use by concurrent
1154
+ * readers, so it is expected that unlinking tables may fail.
1155
+ */
1156
+ for (i = first ; i <= last ; i ++ ) {
1157
+ struct lock_file * table_lock = & table_locks [i - first ];
1158
+ char * table_path = get_locked_file_path (table_lock );
1159
+ unlink (table_path );
1160
+ free (table_path );
1162
1161
}
1163
1162
1164
1163
done :
1165
- free_names (delete_on_success );
1164
+ rollback_lock_file (& tables_list_lock );
1165
+ for (i = first ; table_locks && i <= last ; i ++ )
1166
+ rollback_lock_file (& table_locks [i - first ]);
1167
+ reftable_free (table_locks );
1166
1168
1167
- if (subtable_locks ) {
1168
- listp = subtable_locks ;
1169
- while (* listp ) {
1170
- unlink (* listp );
1171
- listp ++ ;
1172
- }
1173
- free_names (subtable_locks );
1174
- }
1175
- if (lock_file_fd >= 0 ) {
1176
- close (lock_file_fd );
1177
- lock_file_fd = -1 ;
1178
- }
1179
- if (have_lock ) {
1180
- unlink (lock_file_name .buf );
1181
- }
1182
1169
strbuf_release (& new_table_name );
1183
1170
strbuf_release (& new_table_path );
1184
- strbuf_release (& ref_list_contents );
1185
- strbuf_release (& temp_tab_file_name );
1186
- strbuf_release (& lock_file_name );
1171
+ strbuf_release (& new_table_temp_path );
1172
+ strbuf_release (& tables_list_buf );
1173
+ strbuf_release (& table_name );
1187
1174
return err ;
1188
1175
}
1189
1176
0 commit comments