@@ -978,212 +978,199 @@ static int stack_compact_range(struct reftable_stack *st,
978978 size_t first , size_t last ,
979979 struct reftable_log_expiry_config * expiry )
980980{
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 ;
983983 struct strbuf new_table_name = STRBUF_INIT ;
984- struct strbuf lock_file_name = STRBUF_INIT ;
985- struct strbuf ref_list_contents = STRBUF_INIT ;
986984 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 ;
992990
993991 if (first > last || (!expiry && first == last )) {
994992 err = 0 ;
995993 goto done ;
996994 }
997995
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-
1002996 st -> stats .attempts ++ ;
1003997
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 )
10121006 err = 1 ;
1013- } else {
1007+ else
10141008 err = REFTABLE_IO_ERROR ;
1015- }
10161009 goto done ;
10171010 }
1018- /* Don't want to write to the lock for now. */
1019- close (lock_file_fd );
1020- lock_file_fd = -1 ;
10211011
1022- have_lock = 1 ;
10231012 err = stack_uptodate (st );
1024- if (err != 0 )
1013+ if (err )
10251014 goto done ;
10261015
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 ]));
10381023
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 )
10451028 err = 1 ;
1046- } else {
1029+ else
10471030 err = REFTABLE_IO_ERROR ;
1048- }
1031+ goto done ;
10491032 }
10501033
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 ;
10561042 goto done ;
1043+ }
10571044 }
10581045
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 ;
10611054 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 ;
10711055 }
1072- if (err < 0 )
1073- goto done ;
10741056
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 )
10791078 err = 1 ;
1080- } else {
1079+ else
10811080 err = REFTABLE_IO_ERROR ;
1082- }
10831081 goto done ;
10841082 }
1085- have_lock = 1 ;
1083+
10861084 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 ) {
10881087 err = REFTABLE_IO_ERROR ;
10891088 goto done ;
10901089 }
10911090 }
10921091
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+ */
10991096 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 );
11021103 if (err < 0 ) {
11031104 err = REFTABLE_IO_ERROR ;
11041105 goto done ;
11051106 }
11061107 }
11071108
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 );
11291123 if (err < 0 ) {
11301124 err = REFTABLE_IO_ERROR ;
11311125 unlink (new_table_path .buf );
11321126 goto done ;
11331127 }
11341128
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 ));
11371130 if (err < 0 ) {
11381131 err = REFTABLE_IO_ERROR ;
11391132 unlink (new_table_path .buf );
11401133 goto done ;
11411134 }
11421135
1143- err = rename ( lock_file_name . buf , st -> list_file );
1136+ err = commit_lock_file ( & tables_list_lock );
11441137 if (err < 0 ) {
11451138 err = REFTABLE_IO_ERROR ;
11461139 unlink (new_table_path .buf );
11471140 goto done ;
11481141 }
1149- have_lock = 0 ;
11501142
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+ */
11541148 err = reftable_stack_reload_maybe_reuse (st , first < last );
1149+ if (err < 0 )
1150+ goto done ;
11551151
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 );
11621161 }
11631162
11641163done :
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 );
11661168
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- }
11821169 strbuf_release (& new_table_name );
11831170 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 );
11871174 return err ;
11881175}
11891176
0 commit comments