felixhuttmann
In contrast, when a kubernetes controller or operator crashes, it can be expected to continue seamlessly where it left off.
It is easier to write kubernetes controllers that are able to continue seamlessly then to write terraform providers that do so, because of the granularity of the persistence of the state machine. Terraform locks the remote state, then applies all resources in the current root module, then unlocks the remote state again. In contrast, kubernetes operators can granularly update individual objects after each API call that is performed.
Github has the same feature upcoming [2], which allows to get also directly AWS or GCP credentials restricted by branch name [3, 4].
[1] https://docs.gitlab.com/ee/ci/examples/authenticating-with-h... [2] https://github.com/github/roadmap/issues/249 [3] https://awsteele.com/blog/2021/09/15/aws-federation-comes-to... [4] https://github.com/sethvargo/oidc-auth-google-cloud
Partitioning: 1) DynamoDb: Partitioning is explicit and one of the most important parts of schema design 2) Spanner, Cockroach: Database automatically partitions the key ranges. 3) Postgres: You will probably never reach the scale where you need to partition your dataset!
Transactions: 1) Spanner, firestore - no stored procedures, client-side transactions are important 2) Dynamodb: No stored procedures, no client-side transactions, only transactions where all items involved are known by primary key in advance. 3) Fauna, Supabase: Stored procedures are the way to go! You do not need application code, access your database from the client! 4) Postgres: We have everything, use what fits your particular use-case!
If database internals did not matter, why are they all doing something different and are sometimes quite opinionated about it?
I see the problem with mutations in foreign systems if those foreign systems do not support idempotency themselves. IMHO, though, stripe should abstract away faults in banks, and figure out how to work around faults in bank's systems using e.g. automated refunds when a duplicate charge is detected, and not just bubble up a 500 to stripe's customers and leave it to them to figure out. If stripe cannot figure it out in an automated way toward the bank whether the request suceeded, stripe's api customers certainly can't either, and stripe should risk double-charging the end customer knowing the the end customer will complain and request a chargeback.
> The most honest answer is that Stripe wasn't built on a data store where transactions are supported
Transactions are not necessary if one can do an insert conditional on the key not yet existing, but then it is required to have the idempotency key from the client enter into the primary key.
I find it insightful to think about a transport layer that is much less reliable than the internet: traditional/snail mail. In a proper business communication, every invoice has an invoice number such that the recipient can know whether they processed that invoice previously or not.
Adyen seems to similarly implement the idempotency in a separate data store (https://docs.adyen.com/development-resources/api-idempotency...).
Both stripe's and adyen's implementation seem to not treat transient faults within their own systems correctly.
In the case of the parsing code from the article, the emerging property is that a serialization followed by a deserialization should always yield the original result.
In the case of the 'binary or not' case from the article, the non-trivial, emergent property is that the function never fails with an exception.
Most modern software development is plumbing, stitching together platforms, libraries and frameworks, and there are rarely non-trivial, emergent properties where property-based testing is useful.
1) AWS dynamodb has a parallel scanning functionality for this exact use case. https://docs.aws.amazon.com/amazondynamodb/latest/developerg...
2) A typical database already internally maintains an approximately balanced b-tree for every index. Therefore, it should in principal be cheap for the database to return a list of keys that approximately divide the keyrange into N similarly large ranges, even if the key distribution is very uneven. Is somebody aware of a way where this information could be obtained in a query in e.g. postgres?
3) The term 'cursor pagination' is sometimes used for different things, either referring to an in-database concept of cursor, or sometimes as an opaque pagination token. Therefore, for the concept described in the article, I have come to prefer the term keyset pagination, as described in https://www.citusdata.com/blog/2016/03/30/five-ways-to-pagin.... The term keyset pagination makes it clear that we are paginating using conditions on a set of columns that form a unique key for the table.
1) Long-living connections. One part of your application offers large file downloads, so that your users sometimes take significant amount of time to download, and the error rate shall be low. Think people doing `curl -L https://github.com/.../some-commit-hash.zip` in CI. I'd guess this is not served by github's ruby monolith. Or you use websockets. Redeployments typically stop all running tcp connections. Splitting this part off allows you to redeploy the main application frequently without disrupting websockets or downloads.
2) Reduce startup time. For some languages/ecosystems, startup time is significant and scales with the size of the codebase. For example, a typical java enterpise app like keycloak has half a million lines of code and takes about 1 minute only for startup. This is even more relevant when running things in AWS Lambda or google cloud run, and can also be relevant for integration tests. Having several deployables each handling a subset of the functionality may give you better cold start times than having one that contains all code.
3) Resiliency toward resource exhaustion. If two functionalities are served in the same process or VM, and one of them
* has a memory leak
* uses the connection pool to the database inefficiently and thereby clogs it up
* has an endpoint that is overloaded by a misbehaving client
then the other functionality is also affected, which can be avoided by putting them into separate processes/containers/VMs. Splitting services allows to reduce the effort spent on resource hygiene and rate limiting.
4) Binary size. It is often convenient to compile static info or asset-like things into the application, think geoinformation on timezones. This bloats the binary, splitting that part off into an independent deployable can give you a much smaller binary for the main application.
GCP datastore and firestore are strongly consistent nosql databases that are available in multi-region configurations [2].
[1] https://docs.aws.amazon.com/AmazonS3/latest/dev/replication.... [2] https://cloud.google.com/datastore/docs/locations
In general, AWS more often shifts the harder parts of global distributed systems onto their customers, rather than solving them for their customers, like GCP does. For example, GCP cloud storage (s3 equivalent) and datastore (nosql database) provide strongly consistent operations in multi-region configurations, while dynamodb and s3 have only eventually consistent replication across regions; and google's VPCs, message queues, console VM listings, and loadbalancers are global, while AWS's are regional.