Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add experimental support for zstd compression. #3577

Merged
merged 32 commits into from
Apr 4, 2023
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
fea4847
Add experimental support for zstd and lz4 (native) compression.
mulugetam Jun 14, 2022
8aac766
Fix license issues, add tests for zstd, zstd_no_dict, and lz4_native …
mulugetam Jun 14, 2022
33715b2
Fix DCO and and issues with CodecTests.java.
mulugetam Jun 14, 2022
df93e0a
Fix forbidden api violation error for lz4-java.
mulugetam Jun 15, 2022
b0cf47e
Fix license headers. Remove and fix unnecessary fields.
mulugetam Jun 15, 2022
975cfd7
Fix magic numbers. Use more restrictive access modifiers.
mulugetam Jun 21, 2022
bdb9287
Use protected access modifier for Zstd and LZ4 compression mode classes.
mulugetam Jun 21, 2022
1d6bbbb
Allow negative compression levels for zstd. Use more restrictive acce…
mulugetam Jun 21, 2022
c6b39fa
Use a more restrictive permission for loading zstd-jni and lz4-java l…
mulugetam Jun 23, 2022
1050738
Rename a file (follow a consistent version naming convention).
mulugetam Jun 24, 2022
cbfba87
Refactor and create a new custom-codecs sandbox module.
mulugetam Jul 6, 2022
3390e08
Remove blank lines.
mulugetam Jul 6, 2022
a168c72
Restore Lucene92CustomCodec to extend from FilterCodec.
mulugetam Jul 6, 2022
1491da7
Make use of the compressionLevel argument.
mulugetam Jul 7, 2022
f74f011
Make Lucene92CustomCodec abstract and use a package-private access mo…
mulugetam Jul 7, 2022
bdbe409
Fix missing JavaDoc issues. Remove unused field in PerFieldMappingPos…
mulugetam Jul 7, 2022
8120dae
Fix lint errors.
mulugetam Jul 7, 2022
28bd099
Fix the description for the custom-codecs plugin.
mulugetam Jul 7, 2022
51b825b
Fix wildcard import and improve documentation.
mulugetam Jul 8, 2022
6757536
Access control exception fixed. Removed lz4-java support for now.
mulugetam Feb 17, 2023
9423abe
Merge branch 'opensearch-project:main' into add-zstd-lz4-support
mulugetam Feb 22, 2023
45cd603
Upgrade plugin to use Lucene95Codec. Rename files accordingly.
mulugetam Feb 22, 2023
c22d11d
Merge branch 'opensearch-project:main' into add-zstd-lz4-support
mulugetam Mar 17, 2023
f39d093
Add test cases for compression/decompression. Other minor changes.
mulugetam Mar 17, 2023
acf186f
Merge branch 'main' into add-zstd-lz4-support
dblock Mar 21, 2023
ac1bd95
Remove ES grant in plugin-security.policy. Fix minor javadoc issue.
mulugetam Mar 22, 2023
b5ab68c
Merge branch 'add-zstd-lz4-support' of https://github.com/mulugetam/O…
mulugetam Mar 22, 2023
4d1e1a5
Merge branch 'main' into add-zstd-lz4-support
dblock Mar 27, 2023
634dfc0
Merge branch 'main' into add-zstd-lz4-support
dblock Mar 27, 2023
b48119d
Upgrade jettison version to 1.5.4.
mulugetam Mar 28, 2023
bc59912
Update SHA for jettison 1.5.4.
mulugetam Mar 28, 2023
06443ea
Merge branch 'main' of https://github.com/mulugetam/OpenSearch into a…
mulugetam Mar 31, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions sandbox/modules/custom-codecs/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*
* Modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/

apply plugin: 'opensearch.opensearchplugin'
apply plugin: 'opensearch.yaml-rest-test'

opensearchplugin {
name 'custom-codecs'
description 'A plugin that implements custom compression codecs.'
classname 'org.opensearch.index.codec.customcodec.CustomCodecPlugin'
licenseFile rootProject.file('licenses/APACHE-LICENSE-2.0.txt')
noticeFile rootProject.file('NOTICE.txt')
}

dependencies {
api "com.github.luben:zstd-jni:1.5.4-1"
}

// Ignore sun.misc.Unsafe
thirdPartyAudit.ignoreViolations(
'net.jpountz.util.UnsafeUtils'
)

yamlRestTest.enabled = false;
testingConventions.enabled = false;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
291ccaacc039e41932de877303edb6af98a91c24
29 changes: 29 additions & 0 deletions sandbox/modules/custom-codecs/licenses/zstd-jni-LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
-----------------------------------------------------------------------------
** Beginning of "BSD License" text. **

Zstd-jni: JNI bindings to Zstd Library

Copyright (c) 2015-present, Luben Karavelov/ All rights reserved.

BSD License

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The code for the JNI bindings to Zstd library was originally authored by Luben Karavelov
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.index.codec.customcodec;

import org.opensearch.plugins.Plugin;
import org.opensearch.plugins.EnginePlugin;
import org.opensearch.index.codec.CodecServiceFactory;
import org.opensearch.index.IndexSettings;

import java.util.Optional;

/**
* A plugin that implements custom codecs. Supports these codecs:
* <ul>
* <li>ZSTD
* <li>ZSTDNODICT
* </ul>
*
* @opensearch.internal
*/
public final class CustomCodecPlugin extends Plugin implements EnginePlugin {

/** Creates a new instance */
public CustomCodecPlugin() {}

/**
* @param indexSettings is the default indexSettings
* @return the engine factory
*/
@Override
public Optional<CodecServiceFactory> getCustomCodecServiceFactory(final IndexSettings indexSettings) {
return Optional.of(new CustomCodecServiceFactory());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.index.codec.customcodec;

import org.apache.logging.log4j.Logger;
import org.apache.lucene.codecs.Codec;
import org.opensearch.common.collect.MapBuilder;
import org.opensearch.index.codec.CodecService;
import org.opensearch.index.mapper.MapperService;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;

/**
* CustomCodecService provides ZSTD and ZSTDNODICT compression codecs.
*/
public class CustomCodecService extends CodecService {
private final Map<String, Codec> codecs;

/**
* Creates a new CustomCodecService.
*
* @param mapperService A mapper service.
* @param logger A logger.
*/
public CustomCodecService(MapperService mapperService, Logger logger) {
super(mapperService, logger);
final MapBuilder<String, Codec> codecs = MapBuilder.<String, Codec>newMapBuilder();
if (mapperService == null) {
codecs.put(Lucene92CustomCodec.Mode.ZSTD.name(), new ZstdCodec());
codecs.put(Lucene92CustomCodec.Mode.ZSTDNODICT.name(), new ZstdNoDictCodec());
} else {
codecs.put(
Lucene92CustomCodec.Mode.ZSTD.name(),
new PerFieldMappingPostingFormatCodec(Lucene92CustomCodec.Mode.ZSTD, mapperService)
);
codecs.put(
Lucene92CustomCodec.Mode.ZSTDNODICT.name(),
new PerFieldMappingPostingFormatCodec(Lucene92CustomCodec.Mode.ZSTDNODICT, mapperService)
);
}
this.codecs = codecs.immutableMap();
}

@Override
public Codec codec(String name) {
Codec codec = codecs.get(name);
if (codec == null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this will never happen? https://github.com/opensearch-project/OpenSearch/blob/main/server/src/main/java/org/opensearch/index/codec/CodecService.java#L80 throws IllegalArgumentException on null. It would probably be better to look up codec from this class's map and then, it is not there, fall back to base class.

Copy link
Contributor Author

@mulugetam mulugetam Feb 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

return super.codec(name);
}
return codec;
}

@Override
public String[] availableCodecs() {
ArrayList<String> ac = new ArrayList<String>(Arrays.asList(super.availableCodecs()));
ac.addAll(codecs.keySet());
return ac.toArray(new String[0]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.index.codec.customcodec;

import org.opensearch.index.codec.CodecService;
import org.opensearch.index.codec.CodecServiceConfig;
import org.opensearch.index.codec.CodecServiceFactory;

/**
* A factory for creating new {@link CodecService} instance
*/
public class CustomCodecServiceFactory implements CodecServiceFactory {

/** Creates a new instance. */
public CustomCodecServiceFactory() {}

@Override
public CodecService createCodecService(CodecServiceConfig config) {
return new CustomCodecService(config.getMapperService(), config.getLogger());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.index.codec.customcodec;

import org.apache.lucene.codecs.StoredFieldsFormat;
import org.apache.lucene.codecs.FilterCodec;
import org.apache.lucene.codecs.lucene92.Lucene92Codec;
mulugetam marked this conversation as resolved.
Show resolved Hide resolved

abstract class Lucene92CustomCodec extends FilterCodec {
public static final int DEFAULT_COMPRESSION_LEVEL = 6;

/** Each mode represents a compression algorithm. */
public enum Mode {
ZSTD,
ZSTDNODICT
}

private final StoredFieldsFormat storedFieldsFormat;

/** new codec for a given compression algorithm and default compression level */
public Lucene92CustomCodec(Mode mode) {
this(mode, DEFAULT_COMPRESSION_LEVEL);
}

public Lucene92CustomCodec(Mode mode, int compressionLevel) {
super(mode.name(), new Lucene92Codec());
this.storedFieldsFormat = new Lucene92CustomStoredFieldsFormat(mode, compressionLevel);
}

@Override
public StoredFieldsFormat storedFieldsFormat() {
return storedFieldsFormat;
}

@Override
public String toString() {
mulugetam marked this conversation as resolved.
Show resolved Hide resolved
return getClass().getSimpleName();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.index.codec.customcodec;

import java.io.IOException;
import java.util.Objects;
import org.apache.lucene.codecs.StoredFieldsFormat;
import org.apache.lucene.codecs.StoredFieldsReader;
import org.apache.lucene.codecs.StoredFieldsWriter;
import org.apache.lucene.codecs.compressing.CompressionMode;
import org.apache.lucene.codecs.lucene90.compressing.Lucene90CompressingStoredFieldsFormat;
import org.apache.lucene.index.FieldInfos;
import org.apache.lucene.index.SegmentInfo;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.IOContext;

/** Stored field format used by pluggable codec */
public class Lucene92CustomStoredFieldsFormat extends StoredFieldsFormat {

/** A key that we use to map to a mode */
public static final String MODE_KEY = Lucene92CustomStoredFieldsFormat.class.getSimpleName() + ".mode";

private static final int ZSTD_BLOCK_LENGTH = 10 * 48 * 1024;
private static final int ZSTD_MAX_DOCS_PER_BLOCK = 4096;
private static final int ZSTD_BLOCK_SHIFT = 10;

private final CompressionMode zstdCompressionMode;
private final CompressionMode zstdNoDictCompressionMode;

private final Lucene92CustomCodec.Mode mode;

/** default constructor */
public Lucene92CustomStoredFieldsFormat() {
this(Lucene92CustomCodec.Mode.ZSTD, Lucene92CustomCodec.DEFAULT_COMPRESSION_LEVEL);
}

/**
* Creates a new instance.
*
* @param mode The mode represents ZSTD or ZSTDNODICT
*/
public Lucene92CustomStoredFieldsFormat(Lucene92CustomCodec.Mode mode) {
this(mode, Lucene92CustomCodec.DEFAULT_COMPRESSION_LEVEL);
}

/**
* Creates a new instance with the specified mode and compression level.
*
* @param mode The mode represents ZSTD or ZSTDNODICT
* @param compressionLevel The compression level for the mode.
*/
public Lucene92CustomStoredFieldsFormat(Lucene92CustomCodec.Mode mode, int compressionLevel) {
this.mode = Objects.requireNonNull(mode);
zstdCompressionMode = new ZstdCompressionMode(compressionLevel);
zstdNoDictCompressionMode = new ZstdNoDictCompressionMode(compressionLevel);
}

@Override
public StoredFieldsReader fieldsReader(Directory directory, SegmentInfo si, FieldInfos fn, IOContext context) throws IOException {
String value = si.getAttribute(MODE_KEY);
if (value == null) {
throw new IllegalStateException("missing value for " + MODE_KEY + " for segment: " + si.name);
}
Lucene92CustomCodec.Mode mode = Lucene92CustomCodec.Mode.valueOf(value);
return impl(mode).fieldsReader(directory, si, fn, context);
}

@Override
public StoredFieldsWriter fieldsWriter(Directory directory, SegmentInfo si, IOContext context) throws IOException {
String previous = si.putAttribute(MODE_KEY, mode.name());
if (previous != null && previous.equals(mode.name()) == false) {
throw new IllegalStateException(
"found existing value for " + MODE_KEY + " for segment: " + si.name + " old = " + previous + ", new = " + mode.name()
);
}
return impl(mode).fieldsWriter(directory, si, context);
}

private StoredFieldsFormat impl(Lucene92CustomCodec.Mode mode) {
switch (mode) {
case ZSTD:
return new Lucene90CompressingStoredFieldsFormat(
"CustomStoredFieldsZstd",
zstdCompressionMode,
ZSTD_BLOCK_LENGTH,
ZSTD_MAX_DOCS_PER_BLOCK,
ZSTD_BLOCK_SHIFT
);
case ZSTDNODICT:
return new Lucene90CompressingStoredFieldsFormat(
"CustomStoredFieldsZstdNoDict",
zstdNoDictCompressionMode,
ZSTD_BLOCK_LENGTH,
ZSTD_MAX_DOCS_PER_BLOCK,
ZSTD_BLOCK_SHIFT
);
default:
throw new AssertionError();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.index.codec.customcodec;

import org.opensearch.index.mapper.MapperService;

/** PerFieldMappingPostingFormatCodec. {@link org.opensearch.index.codec.PerFieldMappingPostingFormatCodec} */
public class PerFieldMappingPostingFormatCodec extends Lucene92CustomCodec {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

? This seems trappy? I'm not sure I'm following why we're creating a new class w/ the same name but w/o the per field logic? Is this just to pass the build but relying on SPI for runtime?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. And I'm not sure why we have getPostingsFormatForField(args) as I don't see it being used anywhere else.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

@mulugetam mulugetam Feb 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lucene92CustomCodec extends from FilterCodec and the getPostingsFormatForField gets handled via a delegation to Lucene92Codec .


/**
* Creates a new instance.
*
* @param compressionMode The compression mode (ZSTD or ZSTDNODICT).
* @param mapperService The mapper service.
*/
public PerFieldMappingPostingFormatCodec(Lucene92CustomCodec.Mode compressionMode, MapperService mapperService) {
super(compressionMode);
}
}
Loading