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

feat(scylladb): add scylladb provider #2919

Open
wants to merge 21 commits into
base: main
Choose a base branch
from

Conversation

danielhe4rt
Copy link

What does this PR do?

Implements the Database ScyllaDB as a new provider.

Under the ScyllaDBContainer you will have the possibility of:

  • Enable Shard Awareness port using WithShardAwareness;
  • Set Docker Start Commands with WithCommand
  • Fetch ports with ConnectionHost
  • Enable ScyllaDB Alternator Endpoint (DynamoDB Compatible API)

API Example

ctx := context.Background()

ctr, err := scylladb.Run(ctx,
   "scylladb/scylla:6.2",
   scylladb.WithConfigFile(filepath.Join("testdata", "scylla.yaml")),
   scylladb.WithShardAwareness(),
   //...
)

Why is it important?

ScyllaDB is a NoSQL Database that is being constantly used by big companies and acts as a Drop-in Replacement to CassandraDB and DynamoDB.

How to test this PR

Clone the repository and enter the scylladb module folder:

git clone https://github.com/basementdevs/testcontainers-go basement-testcontainers-go
cd basement-testcontainers-go/modules/scylladb
make test

@danielhe4rt danielhe4rt requested a review from a team as a code owner December 13, 2024 16:29
Copy link

netlify bot commented Dec 13, 2024

Deploy Preview for testcontainers-go ready!

Name Link
🔨 Latest commit aabf4d9
🔍 Latest deploy log https://app.netlify.com/sites/testcontainers-go/deploys/6781712653ac6600080fbe71
😎 Deploy Preview https://deploy-preview-2919--testcontainers-go.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link
Collaborator

@stevenh stevenh left a comment

Choose a reason for hiding this comment

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

Thanks for a PR @danielhe4rt, have done an initial review with some suggestions and questions for you.

shardAwarePort = "19042/tcp"
)

type Container struct {
Copy link
Collaborator

Choose a reason for hiding this comment

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

suggestion: Add a doc comment to describe.

cf := testcontainers.ContainerFile{
HostFilePath: configFile,
ContainerFilePath: "/etc/scylla/scylla.yaml",
FileMode: 0o755,
Copy link
Collaborator

Choose a reason for hiding this comment

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

suggestion: no need for the file to be executable

modules/scylladb/scylladb.go Show resolved Hide resolved
// WithAlternator enables the Alternator (DynamoDB Compatible API) service in the ScyllaDB container.
// It will set the "alternator-port" parameter to the specified port.
// It will also set the "alternator-write-isolation" parameter to "always" as a command line argument to the container.
func WithAlternator(alternatorPort int) testcontainers.CustomizeRequestOption {
Copy link
Collaborator

Choose a reason for hiding this comment

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

suggestion: use the in type which matches a port definition

Suggested change
func WithAlternator(alternatorPort int) testcontainers.CustomizeRequestOption {
func WithAlternator(alternatorPort uint16) testcontainers.CustomizeRequestOption {

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 having WithAlternator() would be enough, simplifying user setup. Regarding to alternator-write-isolation, is always recommended for testing?

Copy link
Author

Choose a reason for hiding this comment

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

depend on the application. for testing it's "slower" but you probably wont be doing stress testing in the cluster.

// ConnectionHost returns the host and port of the Scylladb container with the default port
// and obtaining the host and exposed port from the container
// Eg: "host:port" -> "localhost:9042" -> "localhost:19042" -> "localhost:8000"
func (c Container) ConnectionHost(ctx context.Context, port int) (string, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

suggestion: use uint16 to match port requirement.

Suggested change
func (c Container) ConnectionHost(ctx context.Context, port int) (string, error) {
func (c Container) ConnectionHost(ctx context.Context, port uint16) (string, error) {

testcontainers.CleanupContainer(t, ctr)
require.NoError(t, err)

t.Run("test without shard awareness", func(t *testing.T) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

suggestion: we're standardising on simple names without spaces to ensure they match the output

Suggested change
t.Run("test without shard awareness", func(t *testing.T) {
t.Run("without-shard-awareness", func(t *testing.T) {

More below


ctr, err := scylladb.Run(ctx,
"scylladb/scylla:6.2",
scylladb.WithConfigFile(filepath.Join("testdata", "scylla.yaml")),
Copy link
Collaborator

Choose a reason for hiding this comment

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

suggestion: with the change to reader, you can leverage embed

//go:embed testdata/scylla.yaml
var testConfig []byte
Suggested change
scylladb.WithConfigFile(filepath.Join("testdata", "scylla.yaml")),
scylladb.WithConfig(bytes.NewReader(testConfig)),

})
}

func TestScyllaWithAlternator(t *testing.T) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

question: These next two tests look like duplicates of the above, or could be combined?

return dynamodb.NewFromConfig(cfg, dynamodb.WithEndpointResolverV2(&scyllaAlternatorResolver{HostPort: hostPort})), errors.Join(errs...)
}

func createTable(client *dynamodb.Client) error {
Copy link
Collaborator

Choose a reason for hiding this comment

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

suggestion: pass in t and require.NoError as thats the only use of this.

murmur3_partitioner_ignore_msb_bits: 12
strict_is_not_null_in_views: true
maintenance_socket: ignore
enable_tablets: true
Copy link
Collaborator

Choose a reason for hiding this comment

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

suggestion: add trailing newline.

@@ -0,0 +1,194 @@
package scylladb_test
Copy link
Member

Choose a reason for hiding this comment

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

Can we include a test using ssl? I think would be nice to have.

Comment on lines 35 to 60
host, err := ctr.ConnectionHost(ctx, 19042)
require.NoError(t, err)

cluster := gocql.NewCluster(host)
session, err := cluster.CreateSession()
require.NoError(t, err)
defer session.Close()

var address string
err = session.Query("SELECT address FROM system.clients").Scan(&address)
require.NoError(t, err)
})

t.Run("test with shard awareness", func(t *testing.T) {
host, err := ctr.ConnectionHost(ctx, 19042)
require.NoError(t, err)

cluster := gocql.NewCluster(host)
session, err := cluster.CreateSession()
require.NoError(t, err)
defer session.Close()

var address string
err = session.Query("SELECT address FROM system.clients").Scan(&address)
require.NoError(t, err)
})
Copy link
Member

@eddumelendez eddumelendez Dec 17, 2024

Choose a reason for hiding this comment

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

I see both tests are the same. should one of them use 9042 instead? Is it enough testing the connection?

}

// WithShardAwareness enable shard-awareness in the ScyllaDB container so you can use the `19042` port.
func WithShardAwareness() testcontainers.CustomizeRequestOption {
Copy link
Member

Choose a reason for hiding this comment

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

scylladb starts both ports 9042 and 19042 by default. IMO we should expose a fn to get the mapped port for 19042 and omit this one.

Copy link
Member

Choose a reason for hiding this comment

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

@danielhe4rt @eddumelendez I'm taking over the PR and adding the suggestions on top of the original PR.

I'd like to have this in the upcoming release.

Thanks all for your time here

Copy link
Member

Choose a reason for hiding this comment

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

Ups, I noticed this PR is sent from the main branch of the fork, so unfortunately I cannot contribute commits to it.

@danielhe4rt could you please address the above comments 🙏 ?

Copy link
Author

Choose a reason for hiding this comment

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

Done! Sorry for the delay. Tomorrow I'll sync with @CodeLieutenant and finish it for good. Give us one more day @mdelapenya. We're also gonna stream it, so you can find us on twitch.tv/danielhe4rt.

@danielhe4rt danielhe4rt requested a review from mdelapenya January 9, 2025 19:03
Copy link
Member

@mdelapenya mdelapenya left a comment

Choose a reason for hiding this comment

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

@danielhe4rt thanks for your time in this PR, I added a few comments/suggestions in the review.

Could you address them 🙏 ?

Thank you in advance

- Not available until the next release of testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go"><span class="tc-version">:material-tag: main</span></a>

In the case you have a custom config file for ScyllaDB, it's possible to copy that file into the container before it's
started, using the `WithConfigFile(cfgPath string)` function.
Copy link
Member

Choose a reason for hiding this comment

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

suggestion: let's use the WithConfig(io.Reader) method here, as suggested in the review comments. It's also applied to the existing tests, so we should change the method call in there.

[With Config YAML](../../modules/scylladb/examples_test.go) inside_block:runScyllaDBContainerWithConfigFile
<!--/codeinclude-->
!!!warning
You should provide a valid ScyllaDB configuration file when using the function, otherwise the container will fail to
Copy link
Member

Choose a reason for hiding this comment

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

suggestion: see above

Suggested change
You should provide a valid ScyllaDB configuration file when using the function, otherwise the container will fail to
You should provide a valid ScyllaDB configuration file as an `io.Reader` when using the function, otherwise the container will fail to

!!! info
By default, we add the `--developer-mode=1` flag to the ScyllaDB container to disable the various checks Scylla
performs.
Also In scenarios in which static partitioning is not desired - like mostly-idle cluster without hard latency
Copy link
Member

Choose a reason for hiding this comment

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

nit: typo

Suggested change
Also In scenarios in which static partitioning is not desired - like mostly-idle cluster without hard latency
Also in scenarios in which static partitioning is not desired - like mostly-idle cluster without hard latency


fmt.Println(state.Running)

connectionHost, err := scyllaContainer.ConnectionHost(ctx, 8080) // Alternator port
Copy link
Member

Choose a reason for hiding this comment

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

question: in L61 we are setting the alternator port as 8000. Is this correct?

Suggested change
connectionHost, err := scyllaContainer.ConnectionHost(ctx, 8080) // Alternator port
connectionHost, err := scyllaContainer.ConnectionHost(ctx, 8000) // Alternator port

// true
}

func ExampleRun_withConfigFile() {
Copy link
Member

Choose a reason for hiding this comment

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

suggestion: as above, we probably want to use the WithConfig(io.Reader) here

Comment on lines +17 to +18
port = "9042/tcp"
shardAwarePort = "19042/tcp"
Copy link
Member

Choose a reason for hiding this comment

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

question: should we expose these ports so that client code can generate URLs based on them? I see an example test using one of them. If not, we probably want a container method returning the connection string for the given port.

testcontainers.Container
}

// WithConfig sets the YAML config file to be used for the ScyllaDB container
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// WithConfig sets the YAML config file to be used for the ScyllaDB container
// WithConfig sets the YAML config file as an [io.Reader] to be used for the ScyllaDB container

})
}

func TestScyllaWithConfigFile(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

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

suggestion: as above

Suggested change
func TestScyllaWithConfigFile(t *testing.T) {
func TestScyllaWithConfig(t *testing.T) {

err = session.Query("SELECT cluster_name FROM system.local").Scan(&cluster_name)
require.NoError(t, err)

require.Equal(t, "Amazing ScyllaDB Test", cluster_name)
Copy link
Member

Choose a reason for hiding this comment

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

suggestion: add awareness about where this constant value comes from

Suggested change
require.Equal(t, "Amazing ScyllaDB Test", cluster_name)
// the "Amazing ScyllaDB Test" cluster name is set in the scylla.yaml file
require.Equal(t, "Amazing ScyllaDB Test", cluster_name)

}, nil
}

func getDynamoAlternatorClient(t *testing.T, c *scylladb.Container, port int) (*dynamodb.Client, error) {
Copy link
Member

Choose a reason for hiding this comment

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

suggestion: if the assertions in the calling tests are all the same (create a table using the client), you may want to merge them into this helper function, and name it assertCreateTable(t *testing.T). You could also merge the createTable helper

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.

4 participants