[go: up one dir, main page]

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

Encapsulation of applier logic: Move Txn related code out of applier.go. #13878

Merged
merged 18 commits into from
May 20, 2022

Conversation

ptabor
Copy link
Contributor
@ptabor ptabor commented Apr 3, 2022

The PR removes calls to applierV3base logic from server.go that is NOT part of 'application'.
The original idea was that read-only transaction and Range call shared logic with Apply,
so they can call appliers directly (but bypassing all 'corrupt', 'quota' and 'auth' wrappers).

This PR moves all the logic to a separate file (that later can become package on its own).

This is preparation to fix: #13766 by wrapping backends.

@codecov-commenter
Copy link
codecov-commenter commented Apr 3, 2022

Codecov Report

Merging #13878 (dddedcc) into main (e02ac59) will decrease coverage by 0.28%.
The diff coverage is 84.63%.

❗ Current head dddedcc differs from pull request most recent head 7fe3ab2. Consider uploading reports for the commit 7fe3ab2 to get more accurate results

@@            Coverage Diff             @@
##             main   #13878      +/-   ##
==========================================
- Coverage   72.64%   72.36%   -0.29%     
==========================================
  Files         469      474       +5     
  Lines       38413    38464      +51     
==========================================
- Hits        27906    27834      -72     
- Misses       8733     8841     +108     
- Partials     1774     1789      +15     
Flag Coverage Δ
all 72.36% <84.63%> (-0.29%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
server/etcdmain/etcd.go 45.36% <0.00%> (ø)
server/etcdserver/api/etcdhttp/base.go 46.83% <0.00%> (ø)
server/etcdserver/api/v3rpc/util.go 74.19% <ø> (ø)
server/etcdserver/bootstrap.go 68.70% <0.00%> (ø)
server/etcdserver/etcderrors/errors.go 0.00% <ø> (ø)
server/etcdserver/metrics.go 80.85% <ø> (-2.83%) ⬇️
server/etcdserver/util.go 100.00% <ø> (+13.82%) ⬆️
server/etcdserver/apply/corrupt.go 20.00% <20.00%> (ø)
server/storage/quota.go 57.33% <36.36%> (-0.57%) ⬇️
server/etcdserver/corrupt.go 56.47% <45.45%> (+2.02%) ⬆️
... and 47 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update e02ac59...7fe3ab2. Read the comment docs.

@ptabor ptabor requested a review from serathius April 4, 2022 08:22

// As backends and implementations like alarmsStore changed, we need
// to re-bootstrap Appliers.
s.uberApply = newUberApplier(s)
Copy link
Member
@serathius serathius Apr 4, 2022

Choose a reason for hiding this comment

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

Instead of storing a alarmStore reference, could we store reference to alarmStore getter interface?

I'm worried of pattern if Server.X field changes we need to regenerate Server.Y field

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I would agree in general, but it would require deep reform what Backend is and how restore from snapshot works.

Currently restore from snapshot:

  • creates a new instance of the backend
  • all the objects/helpers pointing on the all backend are 'poinsoned'.
  • we need to rebootstrap them using the new backend

It might be easier / safer to create them from scratch rather make sure there is foo.RebootstrapFromBackend(BE) method that will entirely clean it's state and will load what needed from be

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 we can revisit this comment in a separate PR if needed.

@ptabor
Copy link
Contributor Author
ptabor commented Apr 4, 2022
Sending build context to Docker daemon  84.81MB
Sending build context to Docker daemon  86.13MB


Step 1/14 : FROM ubuntu:21.10

toomanyrequests: You have reached your pull rate limit. You may increase the limit by authenticating and upgrading: https://www.docker.com/increase-rate-limit

@ahrtr
Copy link
Member
ahrtr commented Apr 4, 2022

Is this PR planned on 3.6 or 3.7? I assume it's 3.7, otherwise it will be risky to cherry pick to 3.5.

Are you going to move all read-only transactions out of the normal raft consensus protocol (apply workflow), and instead depends on linearizableReadLoop in the next step? In this case, the consistent_index will only be moved forward by the mvcc transactions?

It seems that there are still some big refactors to completely resolve issues/13766.

@ptabor
Copy link
Contributor Author
ptabor commented Apr 5, 2022

Is this PR planned on 3.6 or 3.7? I assume it's 3.7, otherwise it will be risky to cherry pick to 3.5.

I assumed 3.6 - it's early in the release train I think.
I have doubts about 3.5 - as it's pretty substantial in volume change for patch release.

Are you going to move all read-only transactions out of the normal raft consensus protocol (apply workflow), and instead depends on linearizableReadLoop in the next step? In this case, the consistent_index will only be moved forward by the mvcc transactions?

It seems that there are still some big refactors to completely resolve issues/13766.

No. These transactions (RO through apply) are not harmful as they are fully idempotent.

The idea was to split the serving stack into

Currently:
image

Splitted stack serving 'applies' and read-only calls:
image

Practically needed stack due to performance reasons:
image

But we need to be careful that any 'async' calls will not influence cache content such that it can meaningful impact on the 'apply' determinism.

But all of this seems indeed complex for 3.6.

So in short-term I'm thinking about:

image

That makes a sandwich around existing logic, with verification of origin of the transaction.

@ahrtr
Copy link
Member
ahrtr commented Apr 5, 2022

I assumed 3.6 - it's early in the release train I think.
I have doubts about 3.5 - as it's pretty substantial in volume change for patch release.

If it's planned for 3.6, then my PR 13854 is definitely not needed any more, at least not needed for 3.6. What's the plan for 3.5?

So in short-term I'm thinking about:

I do not get time to review through this PR (will probably had a deep review tomorrow), but based on my quick review this morning, it seems that this PR has already completed this short-term, correct?

@serathius
Copy link
Member

@ahrtr PTAL #13854 (comment)

@ptabor ptabor force-pushed the 20220403-applier-clean branch 3 times, most recently from c3811d2 to ad920ed Compare April 9, 2022 08:00
@serathius
Copy link
Member

Working on the postmortem #13967 made me in different direction than @ptabor. I think that overall idea of using consistent_index fields on EtcdServer and using hooks on backend to flush it is wrong. Will think on this more and maybe send a draft.

@ptabor
Copy link
Contributor Author
ptabor commented Apr 21, 2022

I think that overall idea of using consistent_index fields on EtcdServer and using hooks on backend to flush it is wrong

Putting on side future directions, do you think that this refactoring is not worthwhile on its own ?

  • splitting enormous etc-server package
  • reducing number of applier layers directly exposed in EtcdServer object
  • avoiding recursive logic of applier's calling back to server to call nested applier's again
  • splitting post-raft code (appliers) from code that is sometimes called as part of application, and sometimes on its own (txn).

@serathius
Copy link
Member

You're right, it's not an alternative, but supplementary improvement. We would greatly benefit from it.

@ptabor ptabor force-pushed the 20220403-applier-clean branch 3 times, most recently from cb80a48 to 7fe3ab2 Compare April 22, 2022 11:43
server/etcdserver/api/v3rpc/quota.go Outdated Show resolved Hide resolved
server/etcdserver/server.go Outdated Show resolved Hide resolved
server/etcdserver/etcderrors/errors.go Outdated Show resolved Hide resolved
server/etcdserver/apply/apply.go Show resolved Hide resolved
server/etcdserver/apply/apply.go Show resolved Hide resolved
server/etcdserver/apply/uber_applier.go Outdated Show resolved Hide resolved
}
if corruptAlarms {
a.applyV3 = newApplierV3Corrupt(a.applyV3)
}
Copy link
Member

Choose a reason for hiding this comment

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

Can both noSpaceAlarms and corruptAlarms be true? If not, then please add a comment to clarify or update the code to avoid confusion.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unlikely but theoretically possible. This preserves the original logic:

if len(as.Get(pb.AlarmType_NOSPACE)) > 0 {
s.applyV3 = newApplierV3Capped(s.applyV3)
}
if len(as.Get(pb.AlarmType_CORRUPT)) > 0 {
s.applyV3 = newApplierV3Corrupt(s.applyV3)
}

Copy link
Member
@ahrtr ahrtr May 9, 2022

Choose a reason for hiding this comment

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

minor comment: Probably we should add a verification (only one kind of alarm can exist) here in case whatever potential bug or error.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't see why only 1 alarm is an invariant of the system.
What if we are in NOSPACE alarm and the corruption check will declare the in-consistency ?
I assume the applier will have 2 wrappers, but everything should work as expected.

Copy link
Member

Choose a reason for hiding this comment

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

You do not change it in this PR, Let's keep it as it is for now. We can address it separately if needed.

server/etcdserver/apply/uber_applier.go Outdated Show resolved Hide resolved
server/etcdserver/apply/uber_applier.go Outdated Show resolved Hide resolved
server/etcdserver/txn/txn.go Outdated Show resolved Hide resolved
@ahrtr
Copy link
Member
ahrtr commented May 13, 2022

This is a huge PR, which in general I don't think it's good for review. It took me huge time to review this PR, although I still don't think it's well reviewed, but overall it looks good to me.

If I do this PR, I will breakdown it to three PRs at least. The first PR just moves all apply related code into package/folder apply. The second PR move transaction related code into txn. The last PR will add some interface and refine the code.

In summary, I'd like to let this PR to go, reasons:

  1. The pipeline are all green (minimal requirement);
  2. I have already reviewed this PR, but would be better if @serathius can double check;
  3. It has big impact on other PRs, and also is impacted by other PR (which means you need to rebase from time to time), so I'd like this PR to be merged soon.

@ptabor
Copy link
Contributor Author
ptabor commented May 13, 2022

Thank you and really appreciate your time and insightful review.

If I do this PR, I will breakdown it to three PRs at least. The first PR just moves all apply related code into package/folder apply. The second PR move transaction related code into txn. The last PR will add some interface and refine the code.

I tried to make it commit by commit. Just move of 'apply' is infeasible because of spaghettini code. Probably txn could go moved first... followed by changes of interfaces and final move to apply.go.

}

func (s *EtcdServer) NewUberApplier() *uberApplier {
return newUberApplier(s.lg, s.be, s.KV(), s.alarmStore, s.authStore, s.lessor, s.cluster, s, s, s.consistIndex,
Copy link
Member

Choose a reason for hiding this comment

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

Are we sure that during runtime (after bootstrap) none of those fields on server are recreated? For example restoring snapshot, recovering auth etcd?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They are in the applySnapshot and this is handled. I haven't found other places were we 'recreate' them.

Copy link
Member
@serathius serathius left a comment

Choose a reason for hiding this comment

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

one small with function removal

ptabor added 18 commits May 20, 2022 14:32
The PR removes calls to applierV3base logic from server.go that is NOT part of 'application'.
The original idea was that read-only transaction and Range call shared logic with Apply,
so they can call appliers directly (but bypassing all 'corrupt', 'quota' and 'auth' wrappers).

This PR moves all the logic to a separate file (that later can become package on its own).
… server

This PR:
 - moves wrapping of appliers (due to Alarms) out of server.go into uber_applier.go
 - clearly devides the application logic into: chain of:
     a) 'WrapApply' (generic logic across all the methods)
     b) dispatcher (translation of Apply into specific method like 'Put')
     c) chain of 'wrappers' of the specific methods (like Put).
 - when we do recovery (restore from snapshot) we create new instance of appliers.

The purpose is to make sure we control all the depencies of the apply process, i.e.
we can supply e.g. special instance of 'backend' to the application logic.
It was misleading and error prone vs. ClusterId.
All the depencies are explicily passed to the UberApplier factory method.
Side effect: applySec(0.4s) used to be reported as 0s, now it's correctly 0.4s.
@ptabor
Copy link
Contributor Author
ptabor commented May 20, 2022

FTR: Flake due to port conflict:

=== Failed
=== FAIL: integration TestAuthority/Size:_1,_Scenario:_"https://address[:port]" (0.00s)
    cluster.go:532: Creating listener with addr: 127.0.0.1:2131405814
    cluster.go:532: Creating listener with addr: 127.0.0.1:213[150](https://github.com/etcd-io/etcd/runs/6524932268?check_suite_focus=true#step:5:151)5814
    logger.go:130: 2022-05-20T12:46:04.608Z	INFO	m0	LISTEN GRPC	{"member": "m0", "grpcAddr": "127.0.0.1:33130", "m.Name": "m0", "workdir": "/tmp/TestAuthoritySize_1,_Scenario_httpsaddressport821414494/001"}
    cluster.go:724: listenGRPC FAILED: listen failed on grpc socket 127.0.0.1:33130 (listen tcp 127.0.0.1:33130: bind: address already in use)
    --- FAIL: TestAuthority/Size:_1,_Scenario:_"https://address[:port]" (0.00s)

@serathius
Copy link
Member

LGTM

@ptabor ptabor merged commit 9bb1342 into etcd-io:main May 20, 2022
@ptabor ptabor deleted the 20220403-applier-clean branch May 20, 2022 16:07
if !ok || tv.RequestTxn == nil {
continue
}
txnPath = append(txnPath, compareToPath(rv, tv.RequestTxn)...)

Choose a reason for hiding this comment

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

What if the nested transaction's conditions become different after applying previous ops?

Copy link

Choose a reason for hiding this comment

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

@tisonkun interesting question. I think all (including nested) compare clauses are evaluated before applyTxn, so applying nested Txn won't affect results of compare

Choose a reason for hiding this comment

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

Yeah. That's how the current logic is implemented. But it seems not quite intuitive. Said in transaction, users can assume the requests are handled one by one, so the following requests should be able to see previous mutations.

etcd implements read after write, that if you write put k1 v1 and get k1 in one transaction, get k1 sees v1, but it's not applied for compares.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

Inconsistent revision and data occurs
6 participants