-
Notifications
You must be signed in to change notification settings - Fork 333
Add NATS replica client #596
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
Conversation
benbjohnson
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a few points of feedback. Can you wire this up to cmd/litestream and add some instructions on how to test it?
| @@ -0,0 +1,402 @@ | |||
| package nats | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't seem to be imported or usable by the cmd/litestream binary. Am I missing something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had hooked it up (i thought) in cmd/litestream/replicate.go around line 125. Apparently this code wasn't commited
|
Big NATS fan here (oh, and Litestream too of course). Just stumbled across this PR accidentally and looked through it briefly. Some thoughts:
Apologies for stepping in unannounced. Let me know if I can help out any other way. |
|
@betamos thanks for taking the time
I can see it both ways, if it's a blocker I'll remove it.
This is a good point. In general you want to set limits on buckets but this goes to your previous point.
I teach NATS usage at Synadia to customers. We highly recommend JWT only. User/pass, while supported currently support it really shouldn't be used. It's very easy to create JWT programmatically and have far superior control available.
Not at all, I think Litestream is an amazing tool and should be available in a NATS centric pipeline. |
|
I was looking through the litestream docs to see if it supported NATS then came across this PR. Would be great to see this get over the line. I agree with the previous comment about the bucket configuration. I would expect to configure my own bucket and just tell litestream which one to use. There is a number of bucket configurations that will be set depending on my use case at the time. I suggest just taking in a bucket name and expecting it to exist. JWT authentication is my preferred approach but again I wouldn't have litestream be so opinionated. It should support any NATS auth mechanism |
|
I have been slammed with other projects but I agree with the comments above and will remove the bucket config from this sorry and look for a review |
bruth
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@delaneyj Took some time to review given our conversation this past week.
| } | ||
|
|
||
| func (c *ReplicaClient) Cleanup(ctx context.Context) (err error) { | ||
| if c.nc == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| if c.nc == nil { | |
| if c.nc != nil { |
|
|
||
| func (c *ReplicaClient) Cleanup(ctx context.Context) (err error) { | ||
| if c.nc == nil { | ||
| c.nc.Close() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| c.nc.Close() | |
| c.nc.Drain() | |
| // Block until draining is done. There is also a `ClosedHandler` | |
| // callback option for the NATS connection where a channel | |
| // could be used.. | |
| for c.nc.IsDraining() {} |
| BucketReplicas int | ||
| BucketMaxBytes int64 | ||
| BucketTTL time.Duration |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I presume the goal is an out-of-box "no setup required", but for all other backends (based on a quick scan of the docs) require setting up the bucket ahead of time. These particular properties are operational concerns which should, IMO, be handled out-of-band and would be safer. For instance, the replicas would need to change for some reason. When Litestream would startup, an existing configuration could conflict with the remote settings of that bucket and either overwrite it (depending on the setting) or error since it has changed.
I suggest removing these and the requirement would be that a bucket is pre-created and you only need credentials and the bucket name when configuring this backend.
This would also remove the need to have upsertObjectStore.
| } | ||
|
|
||
| // Returns a list of available generations. Order is undefined. | ||
| func (c *ReplicaClient) Generations(ctx context.Context) (_ []string, err error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did not peak at the other backend implementations, so if this is the idiomatic expectation in the codebase then 👍. However, at least for this backend implementation is a bit more difficult to read which values are being returned at times across some of the methods since there is scoped explicit assignment of err in particular vs. implicit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I didn't define what "it" is in the first sentence.. named return values.
| } | ||
|
|
||
| b := buf.Bytes() | ||
| natsInfo, err := objectStore.PutBytes(ctx, filename, b) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could use Put which takes an io.Reader directly.
| JWT string `yaml:"jwt"` | ||
| Seed string `yaml:"seed"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All auth methods should be supported. If an additional dependency is welcome, this package handles all the options as well as looking up the nats context profile. https://pkg.go.dev/github.com/nats-io/jsm.go/natscontext
|
Annnd.. I just realized I was a bit redundant with the other commentary 🤦. Apologies folks.. |
This PR adds support for NATS JetStream Object Store as a replication destination for Litestream databases. Key features: - Modern NATS Go client (v1.44.0) with JetStream Object Store support - Multiple authentication methods (JWT, credentials, NKey, username/password, token) - TLS support with configurable connection parameters - Full compatibility with Litestream's LTX file format - Comprehensive unit tests and testing documentation - Example configuration file Implementation details: - Uses the latest jetstream package API for optimal performance - Requires pre-created buckets (following community feedback) - Supports all standard Litestream operations - Efficient chunking for large objects via JetStream This implementation is based on the original work in PR #596 by @delaneyj, updated to work with the current Litestream codebase and incorporating all community feedback. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
|
This is really interesting, can someone describe the use cases or advantages for it compared to a regular S3 bucket? I.e. how is this being utilized to do something different than before? |
|
Great question! Litestream supports multiple replica destinations (S3, GCS, Azure, SFTP, File, and NATS), each with different advantages depending on your infrastructure and deployment model. Here are the key use cases and advantages for using NATS as a replication backend: Infrastructure Consolidation: If you're already running NATS for messaging/eventing in your stack, you can use the same infrastructure for database backups without needing separate cloud storage services or credentials. See the NATS replica guide for configuration details. Edge and Distributed Deployments: NATS JetStream excels at edge computing scenarios where you might want to keep backups close to where your application runs. The built-in clustering and replication capabilities make it easier to maintain backups across geographically distributed locations without managing multiple S3 buckets or regions. Self-Hosted Infrastructure: For organizations that prefer to maintain complete control over their infrastructure, NATS provides a robust option without dependency on cloud providers. This aligns well with Litestream's philosophy of simplifying single-server deployments while still maintaining reliable replication. Simplified Operations: In NATS-centric environments (especially Kubernetes clusters with NATS already deployed), you can avoid managing cloud provider accounts, API keys, and IAM policies. Authentication is handled through NATS's existing mechanisms (JWT, NKey, or username/password). Cost Control: For organizations already running NATS infrastructure, there's no additional cost for object storage beyond the infrastructure you already have. This can be more economical than paying per-GB to cloud storage providers, especially for development/staging environments. On-Premises and Air-Gapped Environments: NATS provides an excellent option for environments that can't or don't want to use cloud storage services, while still getting distributed, replicated backup storage. In summary, if you're already invested in the NATS ecosystem or need self-hosted infrastructure, NATS is a natural fit. If you're AWS-native, the S3 replica remains an excellent choice. The beauty of Litestream is having these options to match your specific infrastructure needs. |
NATS is a distributed application framework. It has the ability to store objects similar to S3/Minio in a replicated persistence store. This replica client supports this feature for Litestream.
Note
The
replica_client_test.gowas geared towards only being tested againstfile. Have moved fromfs/oserrors to Litestream specific ones. This meant updating the file replica client to use these as well.