-
Notifications
You must be signed in to change notification settings - Fork 23
Replace backbeat client with cloudserver client #2679
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
base: development/9.1
Are you sure you want to change the base?
Conversation
Hello sylvainsenechal,My role is to assist you with the merge of this Available options
Available commands
Status report is not available. |
6398439 to
269de9b
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files
... and 5 files with indirect coverage changes
@@ Coverage Diff @@
## development/9.1 #2679 +/- ##
===================================================
+ Coverage 74.81% 74.84% +0.02%
===================================================
Files 201 200 -1
Lines 13579 13102 -477
===================================================
- Hits 10159 9806 -353
+ Misses 3410 3286 -124
Partials 10 10
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
| "vaultclient": "scality/vaultclient#8.5.1", | ||
| "werelogs": "scality/werelogs#8.2.1" | ||
| "werelogs": "scality/werelogs#8.2.1", | ||
| "@scality/cloudserverclient": "file:../cloudserverclient" |
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.
TODO
conf/config.json
Outdated
| "replicaSet": "rs0", | ||
| "readPreference": "primary", | ||
| "database": "metadata" | ||
| "replicaSetHosts": "localhost:27017", |
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.
TODO
d06be27 to
11d7667
Compare
Waiting for approvalThe following approvals are needed before I can proceed with the merge:
|
130fc22 to
5c4933f
Compare
| const { attachReqUids } = require('../../../lib/clients/utils'); | ||
|
|
||
| const BackbeatTask = require('../../../lib/tasks/BackbeatTask'); | ||
| const { BatchDeleteCommand } = require('../../../lib/clients/smithy/build/smithy/source/typescript-codegen'); |
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.
Do not pay attention to these imports path. They will be updated once the new library is released
5c4933f to
616c43b
Compare
| return done(err); | ||
| } | ||
|
|
||
| const backbeatClient = this.getBackbeatClient(accountId); |
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.
Side note : There are a lot of variables, function names and possibly file names that would benefit from a rename, from backbeatClient to cloudserverClient etc.
I don't wanna do it now because it will add a lot of noise to that PR, and it's not even that straightforward to do with this silly programming language
616c43b to
dcd1111
Compare
| Key: destEntry.getObjectKey(), | ||
| CanonicalID: destEntry.getOwnerId(), | ||
| // TODO : missing content length | ||
| ContentLength: partSize, |
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 removed content-length. Smithy doesn't allow us to have a content length variable that we defined ourselves to be used as the header, because it's calculating the header itself.
Only thing I'm worried about is that the new ContentLength from Smithy might not be equal to the previous one : I think the new ones uses the whole request, when our old content length was partObj.getObjSize()
404c946 to
897ee3e
Compare
| // eslint-disable-next-line no-param-reassign | ||
| err.origin = 'target'; | ||
| if (err.ObjNotFound || err.code === 'ObjNotFound') { | ||
| if (err.ObjNotFound || err.code === 'ObjNotFound' || |
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 isn't super nice error checking 🫤
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.
we cannot guaarantee the type of error there? is it always an arsenal error, or an sdk one? if sdk you should be able to use instanceof, no?
Side note: can't it also be a NoSuchVersion in this case, if the versionID is not empty?
| } | ||
|
|
||
| _getAndPutObjectOnce(sourceEntry, log, done) { | ||
| log.debug('getting object data', { entry: sourceEntry.getLogInfo() }); |
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.
TODO: review this function again
| .then(data => { | ||
| sourceEntry.setReplicationSiteDataStoreVersionId(this.site, | ||
| data.versionId); | ||
| // TODO : review metadata metrics |
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.
@francoisferrand I think using JSON.stringify will work, I see that the function .getSerialized() in Arsenal also returns a string from JSON.stringify that is then used in this same _publishMetadataWriteMetrics, but I'm not sure that "command.input" is equivalent to the old "destReq.httpRequest.body" data we used to call it with
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.
There are a few occurences where this _publishMetadataWriteMetrics is an issue for me.
Actually, we can add a middleware to extract the request body like this, and pass it to the metrics just as before.
Although, I have found that here it would likely be useless, because as you can see in the command above, there is Body in the command, so the length would always be 0.
I have only found one occurence where we the request has a body
command.middlewareStack.add(
next => async args => {
const request = args.request as any;
console.log("content-length:", request?.headers?.['content-length'])
console.log("len", Buffer.byteLength(request.body as any))
return next(args);
},
{ step: 'finalizeRequest' }
);
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.
One more thought : With the old SDK, we were using destReq.httpRequest.body, but if you look at the command, there is no proper body parameter, and with the new v3 client, I tried to get the body with the middleware and it is undefined...
Anyways, probably not worth it to overthink this problem, we just need to clearly state what is the metric we want to compute first (I believe that here it's the Tags, since the api is putTagging), so we probably just want to do something like
this._publishMetadataWriteMetrics(JSON.stringify(command.input.Tags), writeStartTime)
| * @return {BackbeatClient} The S3 client instance to make requests with | ||
| */ | ||
| getBackbeatClient(accountId) { | ||
| // TODO : review why credentials creation before client cache check |
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 wonder if we could check if the client is in the cache directly, the getCredentials function seems rather expensive. But I also wonder if there is some logic in getCredentials that makes it important to run it even when we later return a client from the cache
| if (err) { | ||
| return this._ringReader.send(command) | ||
| .then(data => { | ||
| // TODO : Review : data[0] doesn't make sense, considering the output shape.. : |
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.
Need input from other ppl who know this stuff.
Considering the output of getRaftId is a string,
Either,
- We indeed want to read the first character of that string
- Or we expect it to be an array and want to read the first element, but in that case there is a problem with api definition
Either way, weird api design
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.
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 each raft session has an id, so if you have 10 then it'll be 2 digits
For instance in Scuba we type it like htis: https://github.com/scality/scuba/blob/d6c285163fb7fa2113aecc32f14623bda60e7508/lib/utils/logId.ts#L6
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.
Mhh, so if raftId == '13', sounds like the old code getting the raftId from data[0] would be getting '1' instead of '13' 🤔
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.
maybe @scality/hopocalypse team can help better understand this, i.e. how the API behaves and maybe why we used to take only the first character of the string?
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 is not used in S3C, we don't have an IngestionPopulator.
Here is our list of entrypoints for backbeat:
- https://github.com/scality/Federation/blob/development/9.5/roles/run-backbeat/templates/supervisord.conf.j2
- https://github.com/scality/Federation/blob/development/9.5/roles/run-bucket-notifications/templates/supervisord.conf.j2
No bin/ingestion.js in there
| ], done); | ||
| } | ||
|
|
||
| // TODO : review streaming logic.. |
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.
TODO : review
897ee3e to
3f6b0a3
Compare
| Key: entry.getObjectKey(), | ||
| VersionId: entry.getEncodedVersionId(), | ||
| AccountId: accountId, | ||
| // TODO : content length not available |
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.
Probably gonna remove this :
Smithy won't let use create Requests with a field named Content-length that would be used as a header : It's a reserved name because the value will be auto-computed
| ], done); | ||
| } | ||
|
|
||
| getRaftLog(raftId, begin, limit, targetLeader, done) { |
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 function is heavily modified, in theory it should be simpler, as the Api doesn't returns a stream anymore (example of returned response below)
But I'm worried about the impact, it means that the function calling getRaftLog() : _processReadRecords() will get a completely different type compared with before. In this function, we assign the variable batchState.logRes with the response of that getRaftLog(), but it also looks like batchState.logRes is assigned in other places in the code, and there is potentially broader impact (see function _processPrepareEntries below)
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.
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 don't know how big this can be, but if big, this is probably not scalable not to use any stream... When you say the api doesn't return a stream anymore, do you mean because we stop using ListRecordStream but the class from the smithy generated lib? Did we change the way of getting the oplog?
I'm not a backbeat expert but we usually use bucketclient to consume the oplog, please see the latest version of the approach in scuba (the utilization service that uses the oplog to get the events from the metadata backend) https://github.com/scality/scuba/blob/d6c285163fb7fa2113aecc32f14623bda60e7508/lib/reader/LogConsumerV2.ts#L110
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.
The api is paginated though, although I can't be sure the pagination is always used.. (but on the metadata doc, it says if there is no pagination, it will return max 10k elements)
I looked in metadata and it seems like the response from this api is indeed a stream, it's weird that in the old backbeat client, the response was not marked as a stream, I think I should change it and use a stream.
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.
some random comments after seeing your ask for early reviews
| done(null); | ||
| }) | ||
| .catch(err => { | ||
| if (err?.$metadata?.httpStatusCode === errors.MethodNotAllowed.code) { |
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.
shall we test for the err "instanceof" first?
| // eslint-disable-next-line no-param-reassign | ||
| err.origin = 'target'; | ||
| if (err.ObjNotFound || err.code === 'ObjNotFound') { | ||
| if (err.ObjNotFound || err.code === 'ObjNotFound' || |
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.
we cannot guaarantee the type of error there? is it always an arsenal error, or an sdk one? if sdk you should be able to use instanceof, no?
Side note: can't it also be a NoSuchVersion in this case, if the versionID is not empty?
| if (err) { | ||
| return this._ringReader.send(command) | ||
| .then(data => { | ||
| // TODO : Review : data[0] doesn't make sense, considering the output shape.. : |
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 each raft session has an id, so if you have 10 then it'll be 2 digits
For instance in Scuba we type it like htis: https://github.com/scality/scuba/blob/d6c285163fb7fa2113aecc32f14623bda60e7508/lib/utils/logId.ts#L6
| ], done); | ||
| } | ||
|
|
||
| getRaftLog(raftId, begin, limit, targetLeader, done) { |
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 don't know how big this can be, but if big, this is probably not scalable not to use any stream... When you say the api doesn't return a stream anymore, do you mean because we stop using ListRecordStream but the class from the smithy generated lib? Did we change the way of getting the oplog?
I'm not a backbeat expert but we usually use bucketclient to consume the oplog, please see the latest version of the approach in scuba (the utilization service that uses the oplog to get the events from the metadata backend) https://github.com/scality/scuba/blob/d6c285163fb7fa2113aecc32f14623bda60e7508/lib/reader/LogConsumerV2.ts#L110
| if (err.ObjNotFound || err.code === 'ObjNotFound' || err.code === 'InvalidBucketState') { | ||
| // TODO : Review these errors.. | ||
| if (err.ObjNotFound || | ||
| err.code === 'ObjNotFound' || err.name === 'ObjNotFound' || |
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.
Maybe if the .name & . code are both required I think not, but as we are lacking typescript...), you could do a kind of "macro" function you may use to reduce the code duplication
| CanonicalID: destEntry.getOwnerId(), | ||
| ContentLength: partSize, | ||
| ContentMD5: partObj.getPartETag(), | ||
| Body: incomingMsg, |
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.
When Maha finished the sdk v3 migration, incomingMsg's type should change (it will be a StreamingBlobPayloadOutputTypes) and be compatible
f17fa5f to
adf2539
Compare
7304ff0 to
7fd666c
Compare
454ceaf to
c2ec8ea
Compare


WIP
Issue: BB-706