Skip to content

Conversation

@shyla226
Copy link
Contributor

@shyla226 shyla226 commented Dec 4, 2025

Updated the code based on feedback from #4420.

  1. Code now does not need any changes in cassandra.yaml file
  2. Added a new api in CompressionParams, getDecoratedSstableCompressor so that plugin compressor does not change the compression metadata

* @param options compression options of baseCompressor
* @return returns a decorated compressor, if service available, otherwise baseCompressor
*/
private static ICompressor decorateCompressor(ICompressor baseCompressor, Map<String, String> options)
Copy link
Contributor

@smiklosovic smiklosovic Dec 15, 2025

Choose a reason for hiding this comment

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

I think this might be way simplified

    private static ICompressor decorateCompressor(ICompressor baseCompressor, Map<String, String> options)
    {
        return Optional.ofNullable(baseCompressor)
                       .flatMap(c -> ServiceLoader.load(ICompressorFactory.class)
                                                  .stream()
                                                  .map(ServiceLoader.Provider::get)
                                                  .filter(factory-> c.getClass().getSimpleName()
                                                                              .equals(factory.getSupportedCompressorName()))
                                                  .findFirst())
                       .flatMap(factory -> factory.createCompressor(options))
                       .map(pluginCompressor -> new CompressorDecorator(baseCompressor, pluginCompressor))
                       .orElse(null);
    }

We can get rid of getServiceProviderFactory completely. Just return Optional<ICompressor> from factory's createCompressor. You are just catching that exception here and logging, you can do same in the implementation and return empty optional instead if not possible to instantiate.

this.otherOptions = ImmutableMap.copyOf(otherOptions);
this.minCompressRatio = minCompressRatio;
this.maxCompressedLength = maxCompressedLength;
this.decoratedSstableCompressor = decorateCompressor(sstableCompressor, otherOptions);
Copy link
Contributor

Choose a reason for hiding this comment

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

alignement


public final class CompressionParams
{
private static final Logger logger = LoggerFactory.getLogger(CompressionParams.class);
Copy link
Contributor

Choose a reason for hiding this comment

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

if you log in the implementation as suggested then this might go away (with imports too)


public interface ICompressorFactory
{
public static final ImmutableMap<String, String> COMPRESSOR_NAME_MAP = ImmutableMap.of(
Copy link
Contributor

Choose a reason for hiding this comment

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

this is unused and can be removed, also not correct as such, we would need to be way more robust than this in general.

Copy link
Contributor

@smiklosovic smiklosovic Dec 15, 2025

Choose a reason for hiding this comment

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

public static final redundant too. Would you mind to shed some light at the logic behind this?

We would need to use some reflection to scan classes in org.apache.cassandra.io.compress package which are not abstract and implement ICompressor otherwise we would need to update this every time we add a new compressor implementation. (you are already missing ZstdDictionaryCompressor here)

I am also not completely sure if all such compression algorithms support your hardware speedup. In that case we would need to some filter them more. Not sure, waiting on your feedback.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Intention behind using the map was to have a simple name to represent the algorithm like, deflate, lz4 etc. and map them to concrete classes in Cassandra. Another way I can think of is make the plugin factory return concrete class names like DeflateCompressor when getSupportedCompressorName is invoked, to specify which compressor it is accelerating. Will that be alright?

/**
* This class uses a plugin compressor to perform compress/decompress, if available, otherwise reverts to default SstableCompressor.
*/
public class CompressorDecorator extends AbstractCompressorDecorator
Copy link
Contributor

Choose a reason for hiding this comment

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

I do not think that controlling the execution flow with try/catch in below methods is a good idea. Perhaps you might decide if it is possible to use your plugin as you are getting it from the factory? This is very uncomfortable and probably also not performance friendly when we do uncompress on plugin every single time and then falling back to base compressor.

Copy link
Contributor

Choose a reason for hiding this comment

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

Based on the discussion on ML it seems we will go with the fallback logic after all. In that case we need to log (in a non-spamming manner) + introduce metrics and update them on each failure.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sounds good. I will look into adding the metrics

.bufferType(parameters.getSstableCompressor().preferredBufferType())
.finishOnClose(option.finishOnClose())
.build());
ICompressor compressor = parameters.getSstableCompressor();
Copy link
Contributor

@smiklosovic smiklosovic Dec 15, 2025

Choose a reason for hiding this comment

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

alignement, plus we should most probably not use it like this. We should still use getSstableCompressor() as it was before. It is implementation detail if the compressor is decorated or not.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agree. Reasoning for doing it this way, sstableCompressor being final it can be changed only in the createCompressor function. But if I change it there, CompressionMetadata also gets updated and stored.
Any thoughts how I could overcome this issue?

Copy link
Contributor

@smiklosovic smiklosovic left a comment

Choose a reason for hiding this comment

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

i think we need to iron out some design issues

if (result == null)
{
result = resolveCompressor(parameters.getSstableCompressor(), compressionDictionary);
result = resolveCompressor(parameters.getDecoratedSstableCompressor(), compressionDictionary);
Copy link
Contributor

Choose a reason for hiding this comment

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

same as above

this.otherOptions = ImmutableMap.copyOf(otherOptions);
this.minCompressRatio = minCompressRatio;
this.maxCompressedLength = maxCompressedLength;
this.decoratedSstableCompressor = decorateCompressor(sstableCompressor, otherOptions);
Copy link
Contributor

@smiklosovic smiklosovic Dec 16, 2025

Choose a reason for hiding this comment

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

do we actually need decoratedSstableCompressor? I propose to do something like

this.sstableCompressor = maybeDecorateCompressor(sstableCompressor, otherOptions);

Then sstableCompressor would be either decorated or not.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah..I will try this. I think you answered one of my questions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants