@@ -158,6 +158,13 @@ int max_files_per_process = 1000;
158
158
*/
159
159
int max_safe_fds = FD_MINFREE ; /* default if not changed */
160
160
161
+ #ifdef HAVE_GETRLIMIT
162
+ static bool saved_original_max_open_files ;
163
+ static struct rlimit original_max_open_files ;
164
+ static struct rlimit custom_max_open_files ;
165
+ #endif
166
+
167
+
161
168
/* Whether it is safe to continue running after fsync() fails. */
162
169
bool data_sync_retry = false;
163
170
@@ -946,6 +953,152 @@ InitTemporaryFileAccess(void)
946
953
#endif
947
954
}
948
955
956
+ /*
957
+ * Returns true if the passed in highestfd is the last one that we're allowed
958
+ * to open based on our. This should only be called if
959
+ */
960
+ static bool
961
+ IsOpenFileLimit (int highestfd )
962
+ {
963
+ #ifdef HAVE_GETRLIMIT
964
+ if (!saved_original_max_open_files )
965
+ {
966
+ return false;
967
+ }
968
+
969
+ return highestfd >= custom_max_open_files .rlim_cur - 1 ;
970
+ #else
971
+ return false;
972
+ #endif
973
+ }
974
+
975
+ /*
976
+ * Increases the open file limit (RLIMIT_NOFILE) by the requested amount.
977
+ * Returns true if successful, false otherwise.
978
+ */
979
+ static bool
980
+ IncreaseOpenFileLimit (int extra_files )
981
+ {
982
+ #ifdef HAVE_GETRLIMIT
983
+ struct rlimit rlim ;
984
+
985
+ if (!saved_original_max_open_files )
986
+ {
987
+ return false;
988
+ }
989
+
990
+ rlim = custom_max_open_files ;
991
+
992
+ /* If we're already at the max we reached our limit */
993
+ if (rlim .rlim_cur == original_max_open_files .rlim_max )
994
+ return false;
995
+
996
+ /* Otherwise try to increase the soft limit to what we need */
997
+ rlim .rlim_cur = Min (rlim .rlim_cur + extra_files , rlim .rlim_max );
998
+
999
+ if (setrlimit (RLIMIT_NOFILE , & rlim ) != 0 )
1000
+ {
1001
+ /* We made sure not to exceed the hard limit, so this shouldn't fail */
1002
+ ereport (WARNING , (errmsg ("setrlimit failed: %m" )));
1003
+ return false;
1004
+ }
1005
+
1006
+ custom_max_open_files = rlim ;
1007
+
1008
+ elog (LOG , "increased open file limit to %ld" , (long ) rlim .rlim_cur );
1009
+
1010
+ return true;
1011
+ #else
1012
+ return false;
1013
+ #endif
1014
+ }
1015
+
1016
+ /*
1017
+ * Saves the original open file limit (RLIMIT_NOFILE) the first time when this
1018
+ * is called. If called again it's a no-op.
1019
+ *
1020
+ * Returns true if successful, false otherwise.
1021
+ */
1022
+ static void
1023
+ SaveOriginalOpenFileLimit (void )
1024
+ {
1025
+ #ifdef HAVE_GETRLIMIT
1026
+ int status ;
1027
+
1028
+ if (saved_original_max_open_files )
1029
+ {
1030
+ /* Already saved, no need to do it again */
1031
+ return ;
1032
+ }
1033
+
1034
+ status = getrlimit (RLIMIT_NOFILE , & original_max_open_files );
1035
+ if (status != 0 )
1036
+ {
1037
+ ereport (WARNING , (errmsg ("getrlimit failed: %m" )));
1038
+ return ;
1039
+ }
1040
+
1041
+ custom_max_open_files = original_max_open_files ;
1042
+ saved_original_max_open_files = true;
1043
+ return ;
1044
+ #endif
1045
+ }
1046
+
1047
+ /*
1048
+ * UseOriginalOpenFileLimit --- Makes the process use the original open file
1049
+ * limit that was present at postmaster start.
1050
+ *
1051
+ * This should be called before spawning subprocesses that might use select(2)
1052
+ * which can only handle file descriptors up to 1024.
1053
+ */
1054
+ static void
1055
+ UseOriginalOpenFileLimit (void )
1056
+ {
1057
+ #ifdef HAVE_GETRLIMIT
1058
+ if (!saved_original_max_open_files )
1059
+ {
1060
+ return ;
1061
+ }
1062
+
1063
+ if (custom_max_open_files .rlim_cur == original_max_open_files .rlim_cur )
1064
+ {
1065
+ /* Not changed, so no need to call setrlimit at all */
1066
+ return ;
1067
+ }
1068
+
1069
+ if (setrlimit (RLIMIT_NOFILE , & original_max_open_files ) != 0 )
1070
+ {
1071
+ ereport (WARNING , (errmsg ("setrlimit failed: %m" )));
1072
+ }
1073
+ #endif
1074
+ }
1075
+
1076
+ /*
1077
+ * UseCustomOpenFileLimit --- Makes the process use our custom open file limit
1078
+ * after that we configured based on the max_files_per_process GUC.
1079
+ */
1080
+ static void
1081
+ UseCustomOpenFileLimit (void )
1082
+ {
1083
+ #ifdef HAVE_GETRLIMIT
1084
+ if (!saved_original_max_open_files )
1085
+ {
1086
+ return ;
1087
+ }
1088
+
1089
+ if (custom_max_open_files .rlim_cur == original_max_open_files .rlim_cur )
1090
+ {
1091
+ /* Not changed, so no need to call setrlimit at all */
1092
+ return ;
1093
+ }
1094
+
1095
+ if (setrlimit (RLIMIT_NOFILE , & custom_max_open_files ) != 0 )
1096
+ {
1097
+ ereport (WARNING , (errmsg ("setrlimit failed: %m" )));
1098
+ }
1099
+ #endif
1100
+ }
1101
+
949
1102
/*
950
1103
* count_usable_fds --- count how many FDs the system will let us open,
951
1104
* and estimate how many are already open.
@@ -969,38 +1122,39 @@ count_usable_fds(int max_to_probe, int *usable_fds, int *already_open)
969
1122
int highestfd = 0 ;
970
1123
int j ;
971
1124
972
- #ifdef HAVE_GETRLIMIT
973
- struct rlimit rlim ;
974
- int getrlimit_status ;
975
- #endif
976
-
977
1125
size = 1024 ;
978
1126
fd = (int * ) palloc (size * sizeof (int ));
979
1127
980
- #ifdef HAVE_GETRLIMIT
981
- getrlimit_status = getrlimit (RLIMIT_NOFILE , & rlim );
982
- if (getrlimit_status != 0 )
983
- ereport (WARNING , (errmsg ("getrlimit failed: %m" )));
984
- #endif /* HAVE_GETRLIMIT */
1128
+ SaveOriginalOpenFileLimit ();
985
1129
986
1130
/* dup until failure or probe limit reached */
987
1131
for (;;)
988
1132
{
989
1133
int thisfd ;
990
1134
991
- #ifdef HAVE_GETRLIMIT
992
-
993
1135
/*
994
1136
* don't go beyond RLIMIT_NOFILE; causes irritating kernel logs on
995
1137
* some platforms
996
1138
*/
997
- if (getrlimit_status == 0 && highestfd >= rlim .rlim_cur - 1 )
998
- break ;
999
- #endif
1139
+ if (IsOpenFileLimit (highestfd ))
1140
+ {
1141
+ if (!IncreaseOpenFileLimit (max_to_probe - used ))
1142
+ break ;
1143
+ }
1000
1144
1001
1145
thisfd = dup (2 );
1002
1146
if (thisfd < 0 )
1003
1147
{
1148
+ /*
1149
+ * Eventhough we do the pre-check above, it's still possible that
1150
+ * the call to dup fails with EMFILE. This can happen if the last
1151
+ * file descriptor was already assigned to an "already open" file.
1152
+ * One example of this happening, is if we're already at the soft
1153
+ * limit when we call count_usable_fds.
1154
+ */
1155
+ if (errno == EMFILE && IncreaseOpenFileLimit (max_to_probe - used ))
1156
+ continue ;
1157
+
1004
1158
/* Expect EMFILE or ENFILE, else it's fishy */
1005
1159
if (errno != EMFILE && errno != ENFILE )
1006
1160
elog (WARNING , "duplicating stderr file descriptor failed after %d successes: %m" , used );
@@ -2750,6 +2904,7 @@ pg_system(const char *command, uint32 wait_event_info)
2750
2904
{
2751
2905
int rc ;
2752
2906
2907
+ UseOriginalOpenFileLimit ();
2753
2908
fflush (NULL );
2754
2909
pgstat_report_wait_start (wait_event_info );
2755
2910
@@ -2772,6 +2927,7 @@ pg_system(const char *command, uint32 wait_event_info)
2772
2927
PostRestoreCommand ();
2773
2928
2774
2929
pgstat_report_wait_end ();
2930
+ UseCustomOpenFileLimit ();
2775
2931
return rc ;
2776
2932
}
2777
2933
@@ -2805,6 +2961,19 @@ OpenPipeStream(const char *command, const char *mode)
2805
2961
ReleaseLruFiles ();
2806
2962
2807
2963
TryAgain :
2964
+
2965
+ /*
2966
+ * It would be great if we could call UseOriginalOpenFileLimit here, but
2967
+ * since popen() also opens a file in the current process (this side of the
2968
+ * pipe) we cannot do so safely. Because we might already have many more
2969
+ * files open than the original limit.
2970
+ *
2971
+ * The only way to address this would be implementing a custom popen() that
2972
+ * calls UseOriginalOpenFileLimit only around the actual fork call, but
2973
+ * that seems too much effort to handle the corner case where this external
2974
+ * command uses both select() and tries to open more files than select()
2975
+ * allows for.
2976
+ */
2808
2977
fflush (NULL );
2809
2978
pqsignal (SIGPIPE , SIG_DFL );
2810
2979
errno = 0 ;
0 commit comments