Skip to content

Commit e33416a

Browse files
authored
Merge pull request #1535 from petersmythe/fix/configurable-seeder-pool-size
Make seeder thread pool sizes configurable (fixes #1429)
2 parents 4f3bec7 + ebbf01e commit e33416a

9 files changed

Lines changed: 450 additions & 4 deletions

File tree

documentation/en/user/source/production/index.rst

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,43 @@ These applicaiton properties can be established by any of the following ways, in
9797
- As a System environment variable: `export GWC_SEED_ABORT_LIMIT=2000; <your usual command to run GWC here>` (or for Tomcat, use the Tomcat's `CATALINA_OPTS` in Tomcat's `bin/catalina.sh` as this: `CATALINA_OPTS="GWC_SEED_ABORT_LIMIT=2000 GWC_SEED_RETRY_COUNT=2`
9898

9999

100+
Seeder Thread Pool Size
101+
+++++++++++++++++++++++
102+
103+
The seeder thread pool controls how many seeding threads can run concurrently. By default, the core pool size is ``16`` and the maximum pool size is ``32``. These can be configured in ``geowebcache.xml``:
104+
105+
.. code-block:: xml
106+
107+
<gwcConfiguration>
108+
...
109+
<seederCorePoolSize>24</seederCorePoolSize>
110+
<seederMaxPoolSize>64</seederMaxPoolSize>
111+
...
112+
</gwcConfiguration>
113+
114+
* ``seederCorePoolSize`` : the number of threads to keep in the pool, even if they are idle. Defaults to ``16``.
115+
* ``seederMaxPoolSize`` : the maximum number of threads allowed in the pool. Defaults to ``32``.
116+
117+
The configuration can be overridden at runtime using Java system properties or OS environment variables (useful for Docker and cloud deployments):
118+
119+
* ``GWC_SEEDER_CORE_POOL_SIZE`` : overrides ``seederCorePoolSize`` from the XML configuration.
120+
* ``GWC_SEEDER_MAX_POOL_SIZE`` : overrides ``seederMaxPoolSize`` from the XML configuration.
121+
122+
These overrides are resolved in the following order of precedence:
123+
124+
- As a Java system property: for example ``java -DGWC_SEEDER_CORE_POOL_SIZE=24 -DGWC_SEEDER_MAX_POOL_SIZE=64 ...``
125+
- As a System environment variable: ``export GWC_SEEDER_CORE_POOL_SIZE=24``
126+
127+
If the value is not set or is not a valid positive integer, the default is used. Invalid values are logged as warnings.
128+
129+
If ``seederCorePoolSize`` (or its override) is set to a value greater than ``seederMaxPoolSize``, the maximum pool size will be automatically adjusted upward to match the core pool size.
130+
131+
.. note::
132+
Unlike the seed failure tolerance settings above, the environment variable overrides are resolved directly by the Java code and do not support servlet context parameters in ``WEB-INF/web.xml``. Only Java system properties (``-D``) and OS environment variables are supported as overrides.
133+
134+
Increasing these values is useful when seeding many layers in parallel, especially in combination with a larger HTTP connection pool to the backend WMS.
135+
136+
100137
Resource Allocation
101138
-------------------
102139

geowebcache/core/src/main/java/org/geowebcache/config/GeoWebCacheConfiguration.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ public class GeoWebCacheConfiguration {
4646
/* Default values */
4747
private Integer backendTimeout;
4848

49+
private Integer seederCorePoolSize;
50+
51+
private Integer seederMaxPoolSize;
52+
4953
private String lockProvider;
5054

5155
private transient LockProvider lockProviderInstance;
@@ -128,6 +132,26 @@ public void setBackendTimeout(Integer backendTimeout) {
128132
this.backendTimeout = backendTimeout;
129133
}
130134

135+
/** @see ServerConfiguration#getSeederCorePoolSize() */
136+
public Integer getSeederCorePoolSize() {
137+
return seederCorePoolSize;
138+
}
139+
140+
/** @param seederCorePoolSize the core pool size for the seeder thread pool */
141+
public void setSeederCorePoolSize(Integer seederCorePoolSize) {
142+
this.seederCorePoolSize = seederCorePoolSize;
143+
}
144+
145+
/** @see ServerConfiguration#getSeederMaxPoolSize() */
146+
public Integer getSeederMaxPoolSize() {
147+
return seederMaxPoolSize;
148+
}
149+
150+
/** @param seederMaxPoolSize the maximum pool size for the seeder thread pool */
151+
public void setSeederMaxPoolSize(Integer seederMaxPoolSize) {
152+
this.seederMaxPoolSize = seederMaxPoolSize;
153+
}
154+
131155
/** @return the cacheBypassAllowed */
132156
public Boolean getCacheBypassAllowed() {
133157
return cacheBypassAllowed;

geowebcache/core/src/main/java/org/geowebcache/config/ServerConfiguration.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,23 @@ public interface ServerConfiguration extends BaseConfiguration {
103103

104104
/** The version number should match the XSD namespace and the version of GWC */
105105
String getVersion();
106+
107+
/**
108+
* The core pool size for the seeder thread pool. This is the number of threads to keep in the pool, even if they
109+
* are idle.
110+
*
111+
* @return the configured core pool size, or {@code null} if not set (defaults to 16)
112+
*/
113+
default Integer getSeederCorePoolSize() {
114+
return null;
115+
}
116+
117+
/**
118+
* The maximum pool size for the seeder thread pool. This is the maximum number of threads allowed in the pool.
119+
*
120+
* @return the configured max pool size, or {@code null} if not set (defaults to 32)
121+
*/
122+
default Integer getSeederMaxPoolSize() {
123+
return null;
124+
}
106125
}

geowebcache/core/src/main/java/org/geowebcache/config/XMLConfiguration.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1318,6 +1318,18 @@ public void setBackendTimeout(Integer backendTimeout) throws IOException {
13181318
save();
13191319
}
13201320

1321+
/** @see ServerConfiguration#getSeederCorePoolSize() */
1322+
@Override
1323+
public Integer getSeederCorePoolSize() {
1324+
return gwcConfig.getSeederCorePoolSize();
1325+
}
1326+
1327+
/** @see ServerConfiguration#getSeederMaxPoolSize() */
1328+
@Override
1329+
public Integer getSeederMaxPoolSize() {
1330+
return gwcConfig.getSeederMaxPoolSize();
1331+
}
1332+
13211333
/** @see ServerConfiguration#isCacheBypassAllowed() */
13221334
@Override
13231335
public Boolean isCacheBypassAllowed() {

geowebcache/core/src/main/java/org/geowebcache/seed/SeederThreadPoolExecutor.java

Lines changed: 121 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,136 @@
1919
import java.util.concurrent.TimeUnit;
2020
import java.util.logging.Logger;
2121
import org.geotools.util.logging.Logging;
22+
import org.geowebcache.config.ServerConfiguration;
2223
import org.springframework.beans.factory.DisposableBean;
2324
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
25+
import org.springframework.util.StringUtils;
2426

2527
public class SeederThreadPoolExecutor extends ThreadPoolExecutor implements DisposableBean {
2628

2729
private static final Logger log = Logging.getLogger(SeederThreadPoolExecutor.class.getName());
2830

2931
private static final ThreadFactory tf = new CustomizableThreadFactory("GWC Seeder Thread-");
3032

31-
public SeederThreadPoolExecutor(int corePoolSize, int maxPoolSize) {
32-
super(corePoolSize, maxPoolSize, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), tf);
33+
protected static final int DEFAULT_CORE_POOL_SIZE = 16;
34+
35+
protected static final int DEFAULT_MAX_POOL_SIZE = 32;
36+
37+
/**
38+
* Environment variable / system property name for configuring the core pool size. Looked up from Java system
39+
* properties first, then from OS environment variables. If neither is set or the value is not a valid positive
40+
* integer, the configuration value (or hardcoded default) is used.
41+
*/
42+
public static final String GWC_SEEDER_CORE_POOL_SIZE = "GWC_SEEDER_CORE_POOL_SIZE";
43+
44+
/**
45+
* Environment variable / system property name for configuring the maximum pool size. Looked up from Java system
46+
* properties first, then from OS environment variables. If neither is set or the value is not a valid positive
47+
* integer, the configuration value (or hardcoded default) is used.
48+
*/
49+
public static final String GWC_SEEDER_MAX_POOL_SIZE = "GWC_SEEDER_MAX_POOL_SIZE";
50+
51+
/**
52+
* Creates the seeder thread pool, reading pool sizes from the given {@link ServerConfiguration}. The configuration
53+
* values can be overridden by system properties or environment variables.
54+
*
55+
* <p>Precedence: environment variable / system property → ServerConfiguration → hardcoded default (16/32).
56+
*
57+
* @param config the server configuration providing pool size settings from geowebcache.xml
58+
*/
59+
public SeederThreadPoolExecutor(ServerConfiguration config) {
60+
this(configuredCorePoolSize(config), configuredMaxPoolSize(config));
61+
}
62+
63+
/**
64+
* Internal constructor that resolves env var overrides and validates sizes. Subclasses (e.g. GeoServer's
65+
* SeederThreadLocalTransferExecutor) use this to pass in pool sizes read from their own configuration.
66+
*/
67+
protected SeederThreadPoolExecutor(int defaultCore, int defaultMax) {
68+
this(resolvedSizes(defaultCore, defaultMax));
69+
}
70+
71+
private SeederThreadPoolExecutor(int[] sizes) {
72+
super(sizes[0], sizes[1], 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), tf);
73+
log.info("Seeder thread pool initialized with corePoolSize="
74+
+ getCorePoolSize()
75+
+ ", maxPoolSize="
76+
+ getMaximumPoolSize());
77+
}
78+
79+
private static int configuredCorePoolSize(ServerConfiguration config) {
80+
if (config != null) {
81+
Integer value = config.getSeederCorePoolSize();
82+
if (value != null && value > 0) {
83+
return value;
84+
}
85+
}
86+
return DEFAULT_CORE_POOL_SIZE;
87+
}
88+
89+
private static int configuredMaxPoolSize(ServerConfiguration config) {
90+
if (config != null) {
91+
Integer value = config.getSeederMaxPoolSize();
92+
if (value != null && value > 0) {
93+
return value;
94+
}
95+
}
96+
return DEFAULT_MAX_POOL_SIZE;
97+
}
98+
99+
/** Resolves both pool sizes applying env var overrides and the core <= max constraint. Returns [core, max]. */
100+
private static int[] resolvedSizes(int defaultCore, int defaultMax) {
101+
int core = resolvePoolSize(GWC_SEEDER_CORE_POOL_SIZE, defaultCore);
102+
int max = resolvePoolSize(GWC_SEEDER_MAX_POOL_SIZE, defaultMax);
103+
if (core > max) {
104+
log.warning("Configured corePoolSize ("
105+
+ core
106+
+ ") is greater than maxPoolSize ("
107+
+ max
108+
+ "), adjusting maxPoolSize to match corePoolSize");
109+
max = core;
110+
}
111+
return new int[] {core, max};
112+
}
113+
114+
/**
115+
* Resolves a pool size configuration value by looking up the given property name first as a Java system property,
116+
* then as an OS environment variable. Falls back to the provided default if neither is set or the value is not a
117+
* valid positive integer.
118+
*
119+
* @param propertyName the system property / environment variable name to look up
120+
* @param defaultValue the fallback value if the property is not set or invalid
121+
* @return the resolved pool size
122+
*/
123+
static int resolvePoolSize(String propertyName, int defaultValue) {
124+
String value = System.getProperty(propertyName);
125+
if (!StringUtils.hasText(value)) {
126+
value = System.getenv(propertyName);
127+
}
128+
if (StringUtils.hasText(value)) {
129+
try {
130+
int parsed = Integer.parseInt(value.trim());
131+
if (parsed > 0) {
132+
log.info("Using configured value for " + propertyName + "=" + parsed);
133+
return parsed;
134+
} else {
135+
log.warning("Invalid value for "
136+
+ propertyName
137+
+ "="
138+
+ value
139+
+ " (must be a positive integer), using default "
140+
+ defaultValue);
141+
}
142+
} catch (NumberFormatException e) {
143+
log.warning("Invalid value for "
144+
+ propertyName
145+
+ "="
146+
+ value
147+
+ " (not a valid integer), using default "
148+
+ defaultValue);
149+
}
150+
}
151+
return defaultValue;
33152
}
34153

35154
/**

geowebcache/core/src/main/resources/geowebcache.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
xsi:schemaLocation="http://geowebcache.org/schema/1.28.0 http://geowebcache.org/schema/1.28.0/geowebcache.xsd">
55
<version>1.8.0</version>
66
<backendTimeout>120</backendTimeout>
7+
<!-- Seeder thread pool configuration (optional, can be overridden by
8+
GWC_SEEDER_CORE_POOL_SIZE and GWC_SEEDER_MAX_POOL_SIZE env vars) -->
9+
<!-- <seederCorePoolSize>16</seederCorePoolSize> -->
10+
<!-- <seederMaxPoolSize>32</seederMaxPoolSize> -->
711
<serviceInformation>
812
<title>GeoWebCache</title>
913
<description>GeoWebCache is an advanced tile cache for WMS servers. It supports a large variety of protocols and

geowebcache/core/src/main/resources/org/geowebcache/config/geowebcache.xsd

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,26 @@
3838
</xs:documentation>
3939
</xs:annotation>
4040
</xs:element>
41+
<xs:element name="seederCorePoolSize" type="xs:positiveInteger" minOccurs="0">
42+
<xs:annotation>
43+
<xs:documentation xml:lang="en">
44+
The number of threads to keep in the seeder thread pool,
45+
even if they are idle. Defaults to 16.
46+
Can be overridden by the GWC_SEEDER_CORE_POOL_SIZE
47+
system property or environment variable.
48+
</xs:documentation>
49+
</xs:annotation>
50+
</xs:element>
51+
<xs:element name="seederMaxPoolSize" type="xs:positiveInteger" minOccurs="0">
52+
<xs:annotation>
53+
<xs:documentation xml:lang="en">
54+
The maximum number of threads allowed in the seeder
55+
thread pool. Defaults to 32.
56+
Can be overridden by the GWC_SEEDER_MAX_POOL_SIZE
57+
system property or environment variable.
58+
</xs:documentation>
59+
</xs:annotation>
60+
</xs:element>
4161
<xs:element name="lockProvider" type="xs:string" minOccurs="0">
4262
<xs:annotation>
4363
<xs:documentation xml:lang="en">

0 commit comments

Comments
 (0)