-
Notifications
You must be signed in to change notification settings - Fork 3.1k
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
Support Cassandra Storage in v3 #3570
Conversation
Let's put some summary about the feature here before the review. |
zipkin-server/server-core/src/main/java/zipkin/server/core/CoreModuleProvider.java
Outdated
Show resolved
Hide resolved
I haven't been following that well but skimmed the description and wanted to ask, if the tables change significantly as I think they did, it seems like a big migration is needed by users. If a migration would be needed, is there a reason to develop Zipkin v3 vs asking users to switch to skywalking oap? I reread JC's email and Bas's issue comment and don't think I saw that addressed. |
I think there was a discussion on the Zipkin admin mail list(I am not in that list). About immigration, I think the general idea is not to keep the DB schema unchanged, we have new implementation in the OAP for MySQL/PostgreSQL/Elasticsearch/OpenSearch. And Cassandra is following the abstract. |
Thanks - indeed if this is the general idea, I wonder if all of these schema changes will be accompanied by some auto migration logic. If so, it's probably better to start by writing the migration logic before the server logic since it's always possible to run into impossible migrations. If not, I still wonder, what will Zipkin v3 offer over switching to Skywalker for Zipkin users? I think it could be helpful to have a bullet list for that. |
Although we aim to provide a drop in replacement for zipkin server we
discussed data retrocompatibility and decided to not to. Given the high
throughput and ingestion volume of such data, schema reconciliation can be
a pain.
What we agreed was that old data can't be consumed. If there are popular
demand on this (which I believe will be) maybe we can write tooling to
migrate data from one source to another so old data is also available in
new schema. Another option is that before one moves to the new server you
can have intermediate time where you leverage a OTel collector and write to
the two servers and progressively start moving to use new server.
…On Sat, 7 Oct 2023, 08:47 Anuraag Agrawal, ***@***.***> wrote:
I haven't been following that well but skimmed the description and wanted
to ask, if the tables change significantly as I think they did, it seems
like a big migration is needed by users. If a migration would be needed, is
there a reason to develop Zipkin v3 vs asking users to switch to skywalking
oap? I reread JC's email and Bas's issue comment and don't think I saw that
addressed.
—
Reply to this email directly, view it on GitHub
<#3570 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAXOYAR5ABINITK7JC3WOVLX6D3HFAVCNFSM6AAAAAA5WTHAXCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTONJRGYZDKMZWGQ>
.
You are receiving this because your review was requested.Message ID:
***@***.***>
|
Personally, I would say this kind of immigration is not worth so much effect. Because, the TTL of spans is short, such as days. In some cases, the data would lost the value before the immigration is done. The reason for different schema designs in SkyWalking, is from what we learned from large-scale deployment. As SkyWalking agent + OAP usually work for 100% sampling. We created more concepts to help query(including SkyWalking UI and Lens UI) to have better responses.
This PR is the value. SkyWalking as an APM is impossible to work with Cassandra successfully, but Zipkin server can. One of the key point is Zipkin VS SkyWalking OAP is that, OAP is too complex for Zipkin users. It support all cases for an observability team, but Zipkin v3 could be much lighter, more stable and focus on tracing relative things. Meanwhile, we could share the experiences on both sides. |
Yeah I assume that new server for new data, old server for old until deletion will be the practical approach vs trying any migration scripts. So I'm still wondering why is this Zipkin? From what I follow in this thread, skywalking being more than tracing can actually impose limitations such as not working with Cassandra. That also seems weird to me though because if skywalking can't support Cassandra, there should be no need to change the Zipkin data model on Cassandra. If it's intended to be tracing-only Skywalking, isn't it better to still be part of Skywalking? |
Whether SkyWalking community likes this, it would take a long time to make another decision from a totally another perspective. Zipkin server has this storage option, let's keep it supported. And, I think more discussion could be made in another place. Let's keep this PR context clear to the Cassandra itself. |
@mrproliu Please sync and avoid the conflicts as the bugs have been fixed through another PR. |
Let's wait for @michaelsembwever back from vacation and do the review about Cassandra codes and table structure. He knows Cassandra much better than I could ever be. |
|
||
@Override | ||
public void savePolicy(ContinuousProfilingPolicy policy) throws IOException { | ||
|
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 you create a NotImplementedException? And throw from here?
It is better to see a clear exception rather than NPE or other strange behavior as there is nothing executed.
After all, these methods should not be called.
Not having these makes it much harder to review the patch, and to benchmark it. |
if (keyspaceMetadata == null) { | ||
String createKeyspaceCql = String.format( | ||
"CREATE KEYSPACE IF NOT EXISTS %s " + | ||
"WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'} " + |
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.
neither SimpleStrategy nor rf=1 is appropriate for any production system. NetworkTopologyStrategy and rf=3 is the minimum for production.
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.
These are the default settings in the Zipkin CQL file, should I updated?
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.
they are appropriate for first-time / dev usage. but not appropriate for production usage.
it needs to be configurable.
and ideally the application should log a warning when rf<3 saying it's not appropriate for production.
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.
Yes, if rf<3 would be got a warning log. What do you mean configurable?
columnDefinitions.add("PRIMARY KEY (" + ID_COLUMN + (CollectionUtils.isEmpty(shardKeys) ? "" : "," + Joiner.on(", ").join(shardKeys)) + ")"); | ||
} | ||
|
||
final SQLBuilder sql = new SQLBuilder("CREATE TABLE IF NOT EXISTS " + table) |
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.
why are we using jdbc.SQLBuilder here? the java-driver used natively will be safer and cleaner.
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.
Deleted.
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
public class BatchCQLExecutor implements InsertRequest, UpdateRequest { |
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.
don't do batch statements. and don't store then flush writes. this is misunderstanding C*'s design and strengths.
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.
Understood. I have divided each CQL query for execution.
|
||
private final Map<String, Long> lastDeletedTimeBucket = new ConcurrentHashMap<>(); | ||
|
||
public CassandraHistoryDeleteDAO(CassandraClient client, TableHelper tableHelper, CassandraTableInstaller modelInstaller, Clock clock) { |
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 we are bucketing by table do we still need TimeWindowCompactionStrategy?
what is the performance impact of daily creating and dropping tables ?
The correct (best performance and availability) design here is to define the time bucket inside the one table in its primary key, define the TTL at the table/write level, and let TimeWindowCompactionStrategy do what it's best at. Here it feels like we are re-inventing the wheel at the application layer.
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.
Sure, I have updated to use the original Cassandra table settings, nothing more.
completionTraceIds.add(client.executeAsyncQuery("select " + ZipkinSpanRecord.TRACE_ID + " from " + tagTable + | ||
" where " + ZipkinSpanRecord.QUERY + " = ?" + | ||
" and " + ZipkinSpanRecord.TIME_BUCKET + " >= ?" + | ||
" and " + ZipkinSpanRecord.TIME_BUCKET + " <= ? ALLOW FILTERING", |
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.
is the primary key specified in the where clause here?
allow filtering without the primary key specified is a full-table scan and will not work.
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 have updated to use of the latest secondary table in the v2 for searching.
final List<List<Span>> result = new ArrayList<>(); | ||
for (String table : tableHelper.getTablesWithinTTL(ZipkinSpanRecord.INDEX_NAME)) { | ||
final PreparedStatement stmt = client.getSession().prepare("select * from " + table + " where " + | ||
ZipkinSpanRecord.TRACE_ID + " in ?"); |
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.
don't use IN
against. the primary key. async concurrent requests instead.
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.
Updated to using multiple equals query.
if (!indexExists(cassandraClient, table, index)) { | ||
executeSQL( | ||
new SQLBuilder("CREATE INDEX ") |
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.
- Avoid indexes, especially legacy 2i. (v2 uses SASI, if you must better is to wait for 5.0 and use SAI, at minimum create a design that can still run without the index – true scalability as well as availability when an index needs to be rebuilt)
indexExists(…)
does not work in a distributed environment. it can be used as an optimisation but cannot be relied upon. useIF NOT EXISTS
. use the optimisation everywhere though (CREATE TABLE too).
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.
Avoid indexes, especially legacy 2i. (v2 uses SASI, if you must better is to wait for 5.0 and use SAI, at minimum create a design that can still run without the index – true scalability as well as availability when an index needs to be rebuilt)
Updated, still using the SASI index for now. I could update the SAI index when Cassandra v5.0 is ready.
indexExists(…) does not work in a distributed environment. it can be used as an optimisation but cannot be relied upon. use IF NOT EXISTS. use the optimisation everywhere though (CREATE TABLE too).
Update to using the pre-defined table CQL file.
List<StorageData> storageDataList = new ArrayList<>(); | ||
|
||
for (String table : modelTables) { | ||
final SQLBuilder sql = new SQLBuilder("SELECT * FROM " + table + " WHERE id in ") |
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.
use the java-driver natively. and don't use IN
against the primary key.
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.
Understood. I have divided each CQL query for execution.
duration.getEndTimeBucket() | ||
)) { | ||
final SQLAndParameters sqlAndParameters = buildSQLForQueryValues(tagType, tagKey, limit, duration, table); | ||
results.addAll(client.executeQuery(sqlAndParameters.sql() + " ALLOW FILTERING", |
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.
is the primary key specified in the where clause here?
allow filtering without the primary key specified is a full-table scan and will not work.
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.
Sure, the ALLOW FILTERING
has been deleted.
)) { | ||
final SQLAndParameters sqlAndParameters = buildSQLForQueryKeys(tagType, Integer.MAX_VALUE, duration, table); | ||
results.addAll(client.executeQuery(sqlAndParameters.sql().replaceAll("(1=1\\s+and)|(distinct)", "") + " ALLOW FILTERING", | ||
row -> row.getString(TagAutocompleteData.TAG_KEY), sqlAndParameters.parameters())); |
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.
is the primary key specified in the where clause here?
allow filtering without the primary key specified is a full-table scan and will not work.
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.
Sure, the ALLOW FILTERING
has been deleted.
To summarise
If you have integration tests, you can also rely upon C* server-side guardrails to check you're designing and using correctly. (Such a guardrails definition would also be valuable advice to production operators.) |
@michaelsembwever Could you review for another round? |
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.
@michaelsembwever I am going to merge this first, to unblock the Cassandra relative changes at the dependency repo side.
When you have time, feel free to review, and we will follow your recommendations.
Reimplementing Cassandra Storage in Zipkin Using SkyWalking OAP Core.
Data Tables
endpoint
andannotation
information. Now, these would be converted intotext
and stored across multiple fields.Configuration
default_time_to_live
field was predefined in the tables to allow the database to delete data. In the new version, data would be periodically cleared from database tables in Zipkin according to the DATA_TTL configuration.