Skip to content

Commit b795f6f

Browse files
HADOOP-18094. Disable S3A auditing by default.
See HADOOP-18091. S3A auditing leaks memory through ThreadLocal references * Adds a new option fs.s3a.audit.enabled to controls whether or not auditing is enabled. This is false by default. * When false, the S3A auditing manager is NoopAuditManagerS3A, which was formerly only used for unit tests and during filsystem initialization. * When true, ActiveAuditManagerS3A is used for managing auditing, allowing auditing events to be reported. * updates documentation and tests. This patch does not fix the underlying leak. When auditing is enabled, long-lived threads will retain references to the audit managers of S3A filesystem instances which have already been closed. Contributed by Steve Loughran.
1 parent 795a5ef commit b795f6f

File tree

9 files changed

+213
-31
lines changed

9 files changed

+213
-31
lines changed

hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/audit/AuditIntegration.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
import org.apache.hadoop.fs.statistics.impl.IOStatisticsStore;
3434

3535
import static java.util.Objects.requireNonNull;
36+
import static org.apache.hadoop.fs.s3a.audit.S3AAuditConstants.AUDIT_ENABLED;
37+
import static org.apache.hadoop.fs.s3a.audit.S3AAuditConstants.AUDIT_ENABLED_DEFAULT;
3638
import static org.apache.hadoop.fs.s3a.audit.impl.S3AInternalAuditConstants.AUDIT_SPAN_HANDLER_CONTEXT;
3739

3840
/**
@@ -58,8 +60,14 @@ private AuditIntegration() {
5860
public static AuditManagerS3A createAndStartAuditManager(
5961
Configuration conf,
6062
IOStatisticsStore iostatistics) {
61-
ActiveAuditManagerS3A auditManager = new ActiveAuditManagerS3A(
62-
requireNonNull(iostatistics));
63+
AuditManagerS3A auditManager;
64+
if (conf.getBoolean(AUDIT_ENABLED, AUDIT_ENABLED_DEFAULT)) {
65+
auditManager = new ActiveAuditManagerS3A(
66+
requireNonNull(iostatistics));
67+
} else {
68+
LOG.debug("auditing is disabled");
69+
auditManager = stubAuditManager();
70+
}
6371
auditManager.init(conf);
6472
auditManager.start();
6573
LOG.debug("Started Audit Manager {}", auditManager);

hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/audit/S3AAuditConstants.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,19 @@ private S3AAuditConstants() {
3434
*/
3535
public static final String UNAUDITED_OPERATION = "unaudited operation";
3636

37+
/**
38+
* Is auditing enabled?
39+
* Value: {@value}.
40+
*/
41+
public static final String AUDIT_ENABLED = "fs.s3a.audit.enabled";
42+
43+
/**
44+
* Default auditing flag.
45+
* Value: {@value}.
46+
*/
47+
public static final boolean AUDIT_ENABLED_DEFAULT = false;
48+
49+
3750
/**
3851
* Name of class used for audit logs: {@value}.
3952
*/

hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/audit/impl/NoopAuditManagerS3A.java

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,6 @@
4444
/**
4545
* Simple No-op audit manager for use before a real
4646
* audit chain is set up, and for testing.
47-
* Audit spans always have a unique ID and the activation/deactivation
48-
* operations on them will update this audit manager's active span.
4947
* It does have the service lifecycle, so do
5048
* create a unique instance whenever used.
5149
*/
@@ -59,14 +57,7 @@ public class NoopAuditManagerS3A extends CompositeService
5957
/**
6058
* The inner auditor.
6159
*/
62-
private NoopAuditor auditor = NOOP_AUDITOR;
63-
64-
/**
65-
* Thread local span. This defaults to being
66-
* the unbonded span.
67-
*/
68-
private final ThreadLocal<AuditSpanS3A> activeSpan =
69-
ThreadLocal.withInitial(this::getUnbondedSpan);
60+
private final NoopAuditor auditor = NOOP_AUDITOR;
7061

7162
/**
7263
* ID which is returned as a span ID in the audit event
@@ -160,7 +151,7 @@ public boolean checkAccess(final Path path,
160151

161152
@Override
162153
public void activate(final AuditSpanS3A span) {
163-
activeSpan.set(span);
154+
/* no-op */
164155
}
165156

166157
@Override
@@ -180,6 +171,6 @@ public static AuditSpanS3A createNewSpan(
180171
final String name,
181172
final String path1,
182173
final String path2) {
183-
return NOOP_AUDITOR.createSpan(name, path1, path2);
174+
return NoopSpan.INSTANCE;
184175
}
185176
}

hadoop-tools/hadoop-aws/src/site/markdown/tools/hadoop-aws/auditing.md

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,18 @@ and inside the AWS S3 SDK, immediately before the request is executed.
2222
The full architecture is covered in [Auditing Architecture](auditing_architecture.html);
2323
this document covers its use.
2424

25+
## Important: Auditing is disabled by default
26+
27+
Due to a memory leak from the use of `ThreadLocal` fields, this auditing feature leaks memory as S3A filesystem
28+
instances are created and deleted.
29+
This causes problems in long-lived processes which either do not re-use filesystem
30+
instances, or attempt to delete all instances belonging to specific users.
31+
See [HADOOP-18091](https://issues.apache.org/jira/browse/HADOOP-18091) _S3A auditing leaks memory through ThreadLocal references_.
32+
33+
To avoid these memory leaks, auditing is disabled by default.
34+
35+
To turn auditing on, set `fs.s3a.audit.enabled` to `true`.
36+
2537
## Auditing workflow
2638

2739
1. An _Auditor Service_ can be instantiated for each S3A FileSystem instance,
@@ -63,27 +75,43 @@ ideally even identifying the process/job generating load.
6375

6476
## Using Auditing
6577

66-
The Logging Auditor is enabled by default; it annotates the S3 logs.
78+
Auditing is disabled by default.
79+
When auditing enabled, a Logging Auditor will annotate the S3 logs through a custom
80+
HTTP Referrer header in requests made to S3.
81+
Other auditor classes may be used instead.
6782

6883
### Auditor Options
6984

7085
| Option | Meaning | Default Value |
7186
|--------|---------|---------------|
87+
| `fs.s3a.audit.enabled` | Is auditing enabled | `false` |
7288
| `fs.s3a.audit.service.classname` | Auditor classname | `org.apache.hadoop.fs.s3a.audit.impl.LoggingAuditor` |
7389
| `fs.s3a.audit.request.handlers` | List of extra subclasses of AWS SDK RequestHandler2 to include in handler chain | `""` |
7490
| `fs.s3a.audit.referrer.enabled` | Logging auditor to publish the audit information in the HTTP Referrer header | `true` |
7591
| `fs.s3a.audit.referrer.filter` | List of audit fields to filter | `""` |
7692
| `fs.s3a.audit.reject.out.of.span.operations` | Auditor to reject operations "outside of a span" | `false` |
7793

7894

79-
### Disabling Auditing with the No-op Auditor
95+
### Disabling Auditing.
96+
97+
In this release of Hadoop, auditing is disabled.
8098

81-
The No-op auditor does not perform any logging of audit events.
99+
This can be explicitly set globally or for specific buckets
82100

83101
```xml
84102
<property>
85-
<name>fs.s3a.audit.service.classname</name>
86-
<value>org.apache.hadoop.fs.s3a.audit.impl.NoopAuditor</value>
103+
<name>fs.s3a.audit.enabled</name>
104+
<value>false</value>
105+
</property>
106+
```
107+
108+
Specific buckets can have auditing disabled, even when it is enabled globally.
109+
110+
```xml
111+
<property>
112+
<name>fs.s3a.bucket.landsat-pds.audit.enabled</name>
113+
<value>false</value>
114+
<description>Do not audit landsat bucket operations</description>
87115
</property>
88116
```
89117

@@ -92,13 +120,18 @@ The No-op auditor does not perform any logging of audit events.
92120
The "Logging Auditor" is the default auditor.
93121
It provides two forms of logging
94122

95-
1. Logging of operations in the client via Log4J.
123+
1. Logging of operations in the client via the active SLF4J imolementation.
96124
1. Dynamic generation of the HTTP Referrer header for S3 requests.
97125

98126
The Logging Auditor is enabled by providing its classname in the option
99127
`fs.s3a.audit.service.classname`.
100128

101129
```xml
130+
<property>
131+
<name>fs.s3a.audit.enabled</name>
132+
<value>true</value>
133+
</property>
134+
102135
<property>
103136
<name>fs.s3a.audit.service.classname</name>
104137
<value>org.apache.hadoop.fs.s3a.audit.impl.LoggingAuditor</value>

hadoop-tools/hadoop-aws/src/site/markdown/tools/hadoop-aws/auditing_architecture.md

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,18 @@ the auditor is bound to.
117117

118118
The auditor then creates and returns a span for the specific operation.
119119
The AuditManagerS3A will automatically activate the span returned by the auditor
120-
(i.e. assign it the thread local variable tracking the active span in each thread)
120+
(i.e. assign it the thread local variable tracking the active span in each thread).
121+
122+
### Memory Leakage through `ThreadLocal` use
123+
124+
This architecture contains a critical defect,
125+
[HADOOP-18091](https://issues.apache.org/jira/browse/HADOOP-18091) _S3A auditing leaks memory through ThreadLocal references_.
126+
127+
The code was written assuming that when the `ActiveAuditManagerS3A` service is
128+
stopped, it's `ThreadLocal` fields would be freed.
129+
In fact, they are retained until the threads with references are terminated.
130+
131+
This is why auditing is now disabled by default until a fix is implemented.
121132

122133
### Class `org.apache.hadoop.fs.audit.CommonAuditContext`
123134

@@ -141,8 +152,19 @@ thread.
141152

142153
### class `NoopAuditor`
143154

144-
This auditor creates spans which perform no auditing.
145-
It is very efficient and reliable.
155+
This auditor creates spans which doesn't do anything with the events.
156+
157+
```xml
158+
<property>
159+
<name>fs.s3a.audit.service.classname</name>
160+
<value>org.apache.hadoop.fs.s3a.audit.impl.NoopAuditor</value>
161+
</property>
162+
```
163+
164+
This is *not* the same as disabling auditing, as it still uses the `ActiveAuditManagerS3A` class
165+
which is the source of memory leaks.
166+
167+
Avoid using it except in tests as there is no benefit -simply significant cost.
146168

147169
### class `LoggingAuditor`
148170

hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/audit/AuditTestSupport.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import static org.apache.hadoop.fs.s3a.Statistic.AUDIT_FAILURE;
3030
import static org.apache.hadoop.fs.s3a.Statistic.AUDIT_REQUEST_EXECUTION;
3131
import static org.apache.hadoop.fs.s3a.Statistic.AUDIT_SPAN_CREATION;
32+
import static org.apache.hadoop.fs.s3a.audit.S3AAuditConstants.AUDIT_ENABLED;
3233
import static org.apache.hadoop.fs.s3a.audit.S3AAuditConstants.AUDIT_REQUEST_HANDLERS;
3334
import static org.apache.hadoop.fs.s3a.audit.S3AAuditConstants.AUDIT_SERVICE_CLASSNAME;
3435
import static org.apache.hadoop.fs.s3a.audit.S3AAuditConstants.LOGGING_AUDIT_SERVICE;
@@ -68,6 +69,7 @@ public static Configuration noopAuditConfig() {
6869
final Configuration conf = new Configuration(false);
6970
conf.set(
7071
AUDIT_SERVICE_CLASSNAME, NOOP_AUDIT_SERVICE);
72+
conf.setBoolean(AUDIT_ENABLED, true);
7173
return conf;
7274
}
7375

@@ -88,6 +90,7 @@ public static Configuration loggingAuditConfig() {
8890
*/
8991
public static Configuration enableLoggingAuditor(final Configuration conf) {
9092
conf.set(AUDIT_SERVICE_CLASSNAME, LOGGING_AUDIT_SERVICE);
93+
conf.setBoolean(AUDIT_ENABLED, true);
9194
conf.setBoolean(REJECT_OUT_OF_SPAN_OPERATIONS, true);
9295
return conf;
9396
}
@@ -117,7 +120,8 @@ public static Configuration resetAuditOptions(Configuration conf) {
117120
REFERRER_HEADER_ENABLED,
118121
REJECT_OUT_OF_SPAN_OPERATIONS,
119122
AUDIT_REQUEST_HANDLERS,
120-
AUDIT_SERVICE_CLASSNAME);
123+
AUDIT_SERVICE_CLASSNAME,
124+
AUDIT_ENABLED);
121125
return conf;
122126
}
123127
}

hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/audit/ITestAuditAccessChecks.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@
3737
import static org.apache.hadoop.fs.s3a.Statistic.AUDIT_REQUEST_EXECUTION;
3838
import static org.apache.hadoop.fs.s3a.Statistic.INVOCATION_ACCESS;
3939
import static org.apache.hadoop.fs.s3a.Statistic.STORE_IO_REQUEST;
40-
import static org.apache.hadoop.fs.s3a.audit.S3AAuditConstants.AUDIT_SERVICE_CLASSNAME;
4140
import static org.apache.hadoop.fs.s3a.audit.AuditTestSupport.resetAuditOptions;
41+
import static org.apache.hadoop.fs.s3a.audit.S3AAuditConstants.AUDIT_ENABLED;
42+
import static org.apache.hadoop.fs.s3a.audit.S3AAuditConstants.AUDIT_SERVICE_CLASSNAME;
4243
import static org.apache.hadoop.fs.s3a.performance.OperationCost.FILE_STATUS_ALL_PROBES;
4344
import static org.apache.hadoop.fs.s3a.performance.OperationCost.FILE_STATUS_FILE_PROBE;
4445
import static org.apache.hadoop.fs.s3a.performance.OperationCost.ROOT_FILE_STATUS_PROBE;
@@ -67,6 +68,7 @@ public Configuration createConfiguration() {
6768
Configuration conf = super.createConfiguration();
6869
resetAuditOptions(conf);
6970
conf.set(AUDIT_SERVICE_CLASSNAME, AccessCheckingAuditor.CLASS);
71+
conf.setBoolean(AUDIT_ENABLED, true);
7072
return conf;
7173
}
7274

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.apache.hadoop.fs.s3a.audit;
20+
21+
import org.assertj.core.api.Assertions;
22+
import org.junit.Test;
23+
24+
import org.apache.hadoop.conf.Configuration;
25+
import org.apache.hadoop.fs.s3a.S3AFileSystem;
26+
import org.apache.hadoop.fs.s3a.audit.impl.NoopAuditManagerS3A;
27+
import org.apache.hadoop.fs.s3a.performance.AbstractS3ACostTest;
28+
29+
import static org.apache.hadoop.fs.s3a.audit.AuditTestSupport.NOOP_SPAN;
30+
import static org.apache.hadoop.fs.s3a.audit.AuditTestSupport.resetAuditOptions;
31+
32+
/**
33+
* Verify that by default audit managers are disabled.
34+
*/
35+
public class ITestAuditManagerDisabled extends AbstractS3ACostTest {
36+
37+
public ITestAuditManagerDisabled() {
38+
super(true);
39+
}
40+
41+
@Override
42+
public Configuration createConfiguration() {
43+
Configuration conf = super.createConfiguration();
44+
resetAuditOptions(conf);
45+
return conf;
46+
}
47+
48+
/**
49+
* The default auditor is the no-op auditor.
50+
*/
51+
@Test
52+
public void testAuditorDisabled() {
53+
54+
final S3AFileSystem fs = getFileSystem();
55+
final AuditManagerS3A auditManager = fs.getAuditManager();
56+
57+
Assertions.assertThat(auditManager)
58+
.isInstanceOf(NoopAuditManagerS3A.class);
59+
}
60+
61+
/**
62+
* All the audit spans are the no-op span.
63+
*/
64+
@Test
65+
public void testAuditSpansAreAllTheSame() throws Throwable {
66+
67+
final S3AFileSystem fs = getFileSystem();
68+
final AuditSpanS3A span1 = fs.createSpan("span1", null, null);
69+
final AuditSpanS3A span2 = fs.createSpan("span2", null, null);
70+
Assertions.assertThat(span1)
71+
.describedAs("audit span 1")
72+
.isSameAs(NOOP_SPAN);
73+
Assertions.assertThat(span2)
74+
.describedAs("audit span 2")
75+
.isSameAs(span1);
76+
}
77+
}

0 commit comments

Comments
 (0)