Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 23e5b16

Browse files
committed
Add temp_file_limit GUC parameter to constrain temporary file space usage.
The limit is enforced against the total amount of temp file space used by each session. Mark Kirkwood, reviewed by Cédric Villemain and Tatsuo Ishii
1 parent 1bc16a9 commit 23e5b16

File tree

7 files changed

+122
-14
lines changed

7 files changed

+122
-14
lines changed

doc/src/sgml/config.sgml

+32-1
Original file line numberDiff line numberDiff line change
@@ -1025,10 +1025,41 @@ SET ENABLE_SEQSCAN TO OFF;
10251025
</variablelist>
10261026
</sect2>
10271027

1028+
<sect2 id="runtime-config-resource-disk">
1029+
<title>Disk</title>
1030+
1031+
<variablelist>
1032+
<varlistentry id="guc-temp-file-limit" xreflabel="temp_file_limit">
1033+
<term><varname>temp_file_limit</varname> (<type>integer</type>)</term>
1034+
<indexterm>
1035+
<primary><varname>temp_file_limit</> configuration parameter</primary>
1036+
</indexterm>
1037+
<listitem>
1038+
<para>
1039+
Specifies the maximum amount of disk space that a session can use
1040+
for temporary files, such as sort and hash temporary files, or the
1041+
storage file for a held cursor.
1042+
The value is specified in kilobytes, and <literal>-1</> (the
1043+
default) means no limit.
1044+
Only superusers can change this setting.
1045+
</para>
1046+
<para>
1047+
This setting constrains the total space used at any instant by all
1048+
temporary files used by a given <productname>PostgreSQL</> session.
1049+
It should be noted that disk space used for explicit temporary
1050+
tables, as opposed to temporary files used behind-the-scenes in query
1051+
execution, does <emphasis>not</emphasis> count against this limit.
1052+
</para>
1053+
</listitem>
1054+
</varlistentry>
1055+
1056+
</variablelist>
1057+
</sect2>
1058+
10281059
<sect2 id="runtime-config-resource-kernel">
10291060
<title>Kernel Resource Usage</title>
1030-
<variablelist>
10311061

1062+
<variablelist>
10321063
<varlistentry id="guc-max-files-per-process" xreflabel="max_files_per_process">
10331064
<term><varname>max_files_per_process</varname> (<type>integer</type>)</term>
10341065
<indexterm>

src/backend/storage/file/fd.c

+61-8
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,6 @@ static int max_safe_fds = 32; /* default if not changed */
128128
#define FD_XACT_TRANSIENT (1 << 2) /* T = close (not delete) at aoXact,
129129
* but keep VFD */
130130

131-
/* Flag to tell whether there are files to close/delete at end of transaction */
132-
static bool have_pending_fd_cleanup = false;
133-
134131
typedef struct vfd
135132
{
136133
int fd; /* current FD, or VFD_CLOSED if none */
@@ -140,6 +137,7 @@ typedef struct vfd
140137
File lruMoreRecently; /* doubly linked recency-of-use list */
141138
File lruLessRecently;
142139
off_t seekPos; /* current logical file position */
140+
off_t fileSize; /* current size of file (0 if not temporary) */
143141
char *fileName; /* name of file, or NULL for unused VFD */
144142
/* NB: fileName is malloc'd, and must be free'd when closing the VFD */
145143
int fileFlags; /* open(2) flags for (re)opening the file */
@@ -159,6 +157,17 @@ static Size SizeVfdCache = 0;
159157
*/
160158
static int nfile = 0;
161159

160+
/* True if there are files to close/delete at end of transaction */
161+
static bool have_pending_fd_cleanup = false;
162+
163+
/*
164+
* Tracks the total size of all temporary files. Note: when temp_file_limit
165+
* is being enforced, this cannot overflow since the limit cannot be more
166+
* than INT_MAX kilobytes. When not enforcing, it could theoretically
167+
* overflow, but we don't care.
168+
*/
169+
static uint64 temporary_files_size = 0;
170+
162171
/*
163172
* List of stdio FILEs and <dirent.h> DIRs opened with AllocateFile
164173
* and AllocateDir.
@@ -887,6 +896,7 @@ PathNameOpenFile(FileName fileName, int fileFlags, int fileMode)
887896
vfdP->fileFlags = fileFlags & ~(O_CREAT | O_TRUNC | O_EXCL);
888897
vfdP->fileMode = fileMode;
889898
vfdP->seekPos = 0;
899+
vfdP->fileSize = 0;
890900
vfdP->fdstate = 0x0;
891901
vfdP->resowner = NULL;
892902

@@ -1123,6 +1133,10 @@ FileClose(File file)
11231133
if (unlink(vfdP->fileName))
11241134
elog(LOG, "could not unlink file \"%s\": %m", vfdP->fileName);
11251135
}
1136+
1137+
/* Subtract its size from current usage */
1138+
temporary_files_size -= vfdP->fileSize;
1139+
vfdP->fileSize = 0;
11261140
}
11271141

11281142
/* Unregister it from the resource owner */
@@ -1242,6 +1256,31 @@ FileWrite(File file, char *buffer, int amount)
12421256
if (returnCode < 0)
12431257
return returnCode;
12441258

1259+
/*
1260+
* If enforcing temp_file_limit and it's a temp file, check to see if the
1261+
* write would overrun temp_file_limit, and throw error if so. Note: it's
1262+
* really a modularity violation to throw error here; we should set errno
1263+
* and return -1. However, there's no way to report a suitable error
1264+
* message if we do that. All current callers would just throw error
1265+
* immediately anyway, so this is safe at present.
1266+
*/
1267+
if (temp_file_limit >= 0 && (VfdCache[file].fdstate & FD_TEMPORARY))
1268+
{
1269+
off_t newPos = VfdCache[file].seekPos + amount;
1270+
1271+
if (newPos > VfdCache[file].fileSize)
1272+
{
1273+
uint64 newTotal = temporary_files_size;
1274+
1275+
newTotal += newPos - VfdCache[file].fileSize;
1276+
if (newTotal > (uint64) temp_file_limit * (uint64) 1024)
1277+
ereport(ERROR,
1278+
(errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
1279+
errmsg("temporary file size exceeds temp_file_limit (%dkB)",
1280+
temp_file_limit)));
1281+
}
1282+
}
1283+
12451284
retry:
12461285
errno = 0;
12471286
returnCode = write(VfdCache[file].fd, buffer, amount);
@@ -1251,7 +1290,21 @@ FileWrite(File file, char *buffer, int amount)
12511290
errno = ENOSPC;
12521291

12531292
if (returnCode >= 0)
1293+
{
12541294
VfdCache[file].seekPos += returnCode;
1295+
1296+
/* maintain fileSize and temporary_files_size if it's a temp file */
1297+
if (VfdCache[file].fdstate & FD_TEMPORARY)
1298+
{
1299+
off_t newPos = VfdCache[file].seekPos;
1300+
1301+
if (newPos > VfdCache[file].fileSize)
1302+
{
1303+
temporary_files_size += newPos - VfdCache[file].fileSize;
1304+
VfdCache[file].fileSize = newPos;
1305+
}
1306+
}
1307+
}
12551308
else
12561309
{
12571310
/*
@@ -1854,11 +1907,11 @@ CleanupTempFiles(bool isProcExit)
18541907
if (fdstate & FD_TEMPORARY)
18551908
{
18561909
/*
1857-
* If we're in the process of exiting a backend process, close
1858-
* all temporary files. Otherwise, only close temporary files
1859-
* local to the current transaction. They should be closed by
1860-
* the ResourceOwner mechanism already, so this is just a
1861-
* debugging cross-check.
1910+
* If we're in the process of exiting a backend process,
1911+
* close all temporary files. Otherwise, only close
1912+
* temporary files local to the current transaction.
1913+
* They should be closed by the ResourceOwner mechanism
1914+
* already, so this is just a debugging cross-check.
18621915
*/
18631916
if (isProcExit)
18641917
FileClose(i);

src/backend/utils/errcodes.txt

+6-5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#
55
# Copyright (c) 2003-2011, PostgreSQL Global Development Group
66
#
7-
# This list serves a basis for generating source files containing error
7+
# This list serves as the basis for generating source files containing error
88
# codes. It is kept in a common format to make sure all these source files have
99
# the same contents.
1010
# The files generated from this one are:
@@ -24,14 +24,14 @@
2424
# sqlstate E/W/S errcode_macro_name spec_name
2525
#
2626
# where sqlstate is a five-character string following the SQLSTATE conventions,
27-
# the second field determines if the code means an error, a warning or success,
27+
# the second field indicates if the code means an error, a warning or success,
2828
# errcode_macro_name is the C macro name starting with ERRCODE that will be put
29-
# in errcodes.h and spec_name is a lowercase, underscore-separated name that
29+
# in errcodes.h, and spec_name is a lowercase, underscore-separated name that
3030
# will be used as the PL/pgSQL condition name and will also be included in the
3131
# SGML list. The last field is optional, if not present the PL/pgSQL condition
3232
# and the SGML entry will not be generated.
3333
#
34-
# Empty lines and ones starting with a hash are comments.
34+
# Empty lines and lines starting with a hash are comments.
3535
#
3636
# There are also special lines in the format of:
3737
#
@@ -368,6 +368,7 @@ Section: Class 53 - Insufficient Resources
368368
53100 E ERRCODE_DISK_FULL disk_full
369369
53200 E ERRCODE_OUT_OF_MEMORY out_of_memory
370370
53300 E ERRCODE_TOO_MANY_CONNECTIONS too_many_connections
371+
53400 E ERRCODE_CONFIGURATION_LIMIT_EXCEEDED configuration_limit_exceeded
371372

372373
Section: Class 54 - Program Limit Exceeded
373374

@@ -393,7 +394,7 @@ Section: Class 57 - Operator Intervention
393394
57P01 E ERRCODE_ADMIN_SHUTDOWN admin_shutdown
394395
57P02 E ERRCODE_CRASH_SHUTDOWN crash_shutdown
395396
57P03 E ERRCODE_CANNOT_CONNECT_NOW cannot_connect_now
396-
57P04 E ERRCODE_DATABASE_DROPPED database_dropped
397+
57P04 E ERRCODE_DATABASE_DROPPED database_dropped
397398

398399
Section: Class 58 - System Error (errors external to PostgreSQL itself)
399400

src/backend/utils/misc/guc.c

+15
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,8 @@ int log_min_duration_statement = -1;
425425
int log_temp_files = -1;
426426
int trace_recovery_messages = LOG;
427427

428+
int temp_file_limit = -1;
429+
428430
int num_temp_buffers = 1024;
429431

430432
char *data_directory;
@@ -535,6 +537,8 @@ const char *const config_group_names[] =
535537
gettext_noop("Resource Usage"),
536538
/* RESOURCES_MEM */
537539
gettext_noop("Resource Usage / Memory"),
540+
/* RESOURCES_DISK */
541+
gettext_noop("Resource Usage / Disk"),
538542
/* RESOURCES_KERNEL */
539543
gettext_noop("Resource Usage / Kernel Resources"),
540544
/* RESOURCES_VACUUM_DELAY */
@@ -1693,6 +1697,17 @@ static struct config_int ConfigureNamesInt[] =
16931697
check_max_stack_depth, assign_max_stack_depth, NULL
16941698
},
16951699

1700+
{
1701+
{"temp_file_limit", PGC_SUSET, RESOURCES_DISK,
1702+
gettext_noop("Limits the total size of all temp files used by each session."),
1703+
gettext_noop("-1 means no limit."),
1704+
GUC_UNIT_KB
1705+
},
1706+
&temp_file_limit,
1707+
-1, -1, INT_MAX,
1708+
NULL, NULL, NULL
1709+
},
1710+
16961711
{
16971712
{"vacuum_cost_page_hit", PGC_USERSET, RESOURCES_VACUUM_DELAY,
16981713
gettext_noop("Vacuum cost for a page found in the buffer cache."),

src/backend/utils/misc/postgresql.conf.sample

+5
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,11 @@
119119
#maintenance_work_mem = 16MB # min 1MB
120120
#max_stack_depth = 2MB # min 100kB
121121

122+
# - Disk -
123+
124+
#temp_file_limit = -1 # limits per-session temp file space
125+
# in kB, or -1 for no limit
126+
122127
# - Kernel Resource Usage -
123128

124129
#max_files_per_process = 1000 # min 25

src/include/utils/guc.h

+2
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,8 @@ extern int client_min_messages;
209209
extern int log_min_duration_statement;
210210
extern int log_temp_files;
211211

212+
extern int temp_file_limit;
213+
212214
extern int num_temp_buffers;
213215

214216
extern char *data_directory;

src/include/utils/guc_tables.h

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ enum config_group
5959
CONN_AUTH_SECURITY,
6060
RESOURCES,
6161
RESOURCES_MEM,
62+
RESOURCES_DISK,
6263
RESOURCES_KERNEL,
6364
RESOURCES_VACUUM_DELAY,
6465
RESOURCES_BGWRITER,

0 commit comments

Comments
 (0)