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

Optimize linear search of WAIT and WAITAOF when unblocking the client #787

Merged
merged 4 commits into from
Aug 18, 2024

Conversation

enjoy-binbin
Copy link
Member

@enjoy-binbin enjoy-binbin commented Jul 15, 2024

Currently, if the client enters a blocked state, it will be
added to the server.clients_waiting_acks list. When the client
is unblocked, that is, when unblockClient is called, we will
need to linearly traverse server.clients_waiting_acks to delete
the client, and this search is O(N).

When WAIT (or WAITAOF) is used extensively in some cases, this
O(N) search may be time-consuming. We can remember the list node
and store it in the blockingState struct and it can avoid the
linear search in unblockClientWaitingReplicas.

Remember the list node, and it can avoid the linear search
in unblockClientWaitingReplicas.

Signed-off-by: Binbin <binloveplay1314@qq.com>
@enjoy-binbin
Copy link
Member Author

An extreme test (wait 10 make sure it will get blocked, and the timeout is just 1ms so it can get unblocked in a short time)

./src/valkey-benchmark -c 5000 -n 1000000 wait 10 1

before:

Summary:
  throughput summary: 52452.14 requests per second
  latency summary (msec):
          avg       min       p50       p95       p99       max
       85.334    27.792    80.575   153.215   166.911   190.591

Summary:
  throughput summary: 52645.43 requests per second
  latency summary (msec):
          avg       min       p50       p95       p99       max
       84.970    27.888    80.383   154.751   164.991   185.215

Summary:
  throughput summary: 51894.13 requests per second
  latency summary (msec):
          avg       min       p50       p95       p99       max
       86.001    29.632    80.767   153.215   163.583   193.279

after:

Summary:
  throughput summary: 61881.19 requests per second
  latency summary (msec):
          avg       min       p50       p95       p99       max
       41.361    21.680    41.567    44.095    45.599    58.559

Summary:
  throughput summary: 62386.93 requests per second
  latency summary (msec):
          avg       min       p50       p95       p99       max
       41.008    19.872    41.247    43.455    45.087    54.143

Summary:
  throughput summary: 61606.70 requests per second
  latency summary (msec):
          avg       min       p50       p95       p99       max
       41.515    19.424    41.695    43.839    45.631    54.943

@zuiderkwast
Copy link
Contributor

Please mention the user-visible changes in PR description. Optimization of WAIT and WAITAOF?

Copy link

codecov bot commented Jul 15, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 70.26%. Comparing base (418901d) to head (c88d5f9).
Report is 61 commits behind head on unstable.

Additional details and impacted files
@@             Coverage Diff              @@
##           unstable     #787      +/-   ##
============================================
- Coverage     70.28%   70.26%   -0.02%     
============================================
  Files           112      112              
  Lines         60587    60592       +5     
============================================
- Hits          42581    42575       -6     
- Misses        18006    18017      +11     
Files Coverage Δ
src/blocked.c 91.60% <100.00%> (+0.14%) ⬆️
src/replication.c 87.23% <100.00%> (ø)
src/server.h 100.00% <ø> (ø)

... and 10 files with indirect coverage changes

@enjoy-binbin enjoy-binbin changed the title Optimize clients_waiting_acks to avoid the linear search Optimize linear search of WAIT and WAITAOF when unblocking the client Jul 15, 2024
Signed-off-by: Binbin <binloveplay1314@qq.com>
@ranshid
Copy link
Member

ranshid commented Jul 17, 2024

Thank you @enjoy-binbin. this optimization looks correct!

I would only suggest that we try and encapsulate all blocking related data in the BlockingState struct in the client.
Currently we keep the:

/* BLOCKED_WAIT and BLOCKED_WAITAOF */
    int numreplicas;      /* Number of replicas we are waiting for ACK. */
    int numlocal;         /* Indication if WAITAOF is waiting for local fsync. */
    long long reploffset; /* Replication offset to reach. */

inside the blocking state, maybe adding the list node ptr there is cleaner?

@ranshid ranshid self-requested a review July 17, 2024 05:33
Signed-off-by: Binbin <binloveplay1314@qq.com>
Signed-off-by: Binbin <binloveplay1314@qq.com>
Copy link
Member

@ranshid ranshid left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Contributor

@zuiderkwast zuiderkwast left a comment

Choose a reason for hiding this comment

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

Looks correct. Only question: Is it worth 8 bytes per client?

I guess it's OK but in the future, we can try to optimize the memory of the client struct. We could change this kind of lists to dicts or something else where we don't need to store a list node.

@enjoy-binbin
Copy link
Member Author

I guess it's OK but in the future, we can try to optimize the memory of the client struct. We could change this kind of lists to dicts or something else where we don't need to store a list node.

postponed_list_node also do the same thing, i think it is ok. yes, we can optimize it in the future.

@enjoy-binbin enjoy-binbin merged commit 70b9285 into valkey-io:unstable Aug 18, 2024
20 checks passed
@enjoy-binbin enjoy-binbin deleted the wait_acks_list branch August 18, 2024 13:20
ranshid added a commit to ranshid/valkey that referenced this pull request Aug 19, 2024
This is to address [This comment](valkey-io#787 (comment))
which was left as part of valkey-io#787.

Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
@enjoy-binbin enjoy-binbin added the release-notes This issue should get a line item in the release notes label Aug 20, 2024
mapleFU pushed a commit to mapleFU/valkey that referenced this pull request Aug 21, 2024
…valkey-io#787)

Currently, if the client enters a blocked state, it will be
added to the server.clients_waiting_acks list. When the client
is unblocked, that is, when unblockClient is called, we will
need to linearly traverse server.clients_waiting_acks to delete
the client, and this search is O(N).

When WAIT (or WAITAOF) is used extensively in some cases, this
O(N) search may be time-consuming. We can remember the list node
and store it in the blockingState struct and it can avoid the
linear search in unblockClientWaitingReplicas.

Signed-off-by: Binbin <binloveplay1314@qq.com>
Signed-off-by: mwish <maplewish117@gmail.com>
mapleFU pushed a commit to mapleFU/valkey that referenced this pull request Aug 22, 2024
…valkey-io#787)

Currently, if the client enters a blocked state, it will be
added to the server.clients_waiting_acks list. When the client
is unblocked, that is, when unblockClient is called, we will
need to linearly traverse server.clients_waiting_acks to delete
the client, and this search is O(N).

When WAIT (or WAITAOF) is used extensively in some cases, this
O(N) search may be time-consuming. We can remember the list node
and store it in the blockingState struct and it can avoid the
linear search in unblockClientWaitingReplicas.

Signed-off-by: Binbin <binloveplay1314@qq.com>
Signed-off-by: mwish <maplewish117@gmail.com>
madolson pushed a commit that referenced this pull request Sep 2, 2024
…#787)

Currently, if the client enters a blocked state, it will be
added to the server.clients_waiting_acks list. When the client
is unblocked, that is, when unblockClient is called, we will
need to linearly traverse server.clients_waiting_acks to delete
the client, and this search is O(N).

When WAIT (or WAITAOF) is used extensively in some cases, this
O(N) search may be time-consuming. We can remember the list node
and store it in the blockingState struct and it can avoid the
linear search in unblockClientWaitingReplicas.

Signed-off-by: Binbin <binloveplay1314@qq.com>
madolson pushed a commit that referenced this pull request Sep 3, 2024
…#787)

Currently, if the client enters a blocked state, it will be
added to the server.clients_waiting_acks list. When the client
is unblocked, that is, when unblockClient is called, we will
need to linearly traverse server.clients_waiting_acks to delete
the client, and this search is O(N).

When WAIT (or WAITAOF) is used extensively in some cases, this
O(N) search may be time-consuming. We can remember the list node
and store it in the blockingState struct and it can avoid the
linear search in unblockClientWaitingReplicas.

Signed-off-by: Binbin <binloveplay1314@qq.com>
PingXie added a commit to PingXie/valkey that referenced this pull request Sep 14, 2024
…valkey-io#787)

Currently, if the client enters a blocked state, it will be
added to the server.clients_waiting_acks list. When the client
is unblocked, that is, when unblockClient is called, we will
need to linearly traverse server.clients_waiting_acks to delete
the client, and this search is O(N).

When WAIT (or WAITAOF) is used extensively in some cases, this
O(N) search may be time-consuming. We can remember the list node
and store it in the blockingState struct and it can avoid the
linear search in unblockClientWaitingReplicas.

Signed-off-by: Binbin <binloveplay1314@qq.com>
Signed-off-by: Ping Xie <pingxie@google.com>
PingXie added a commit to PingXie/valkey that referenced this pull request Sep 14, 2024
…valkey-io#787)

Currently, if the client enters a blocked state, it will be
added to the server.clients_waiting_acks list. When the client
is unblocked, that is, when unblockClient is called, we will
need to linearly traverse server.clients_waiting_acks to delete
the client, and this search is O(N).

When WAIT (or WAITAOF) is used extensively in some cases, this
O(N) search may be time-consuming. We can remember the list node
and store it in the blockingState struct and it can avoid the
linear search in unblockClientWaitingReplicas.

Signed-off-by: Binbin <binloveplay1314@qq.com>
Signed-off-by: Ping Xie <pingxie@google.com>
PingXie added a commit to PingXie/valkey that referenced this pull request Sep 14, 2024
…valkey-io#787)

Currently, if the client enters a blocked state, it will be
added to the server.clients_waiting_acks list. When the client
is unblocked, that is, when unblockClient is called, we will
need to linearly traverse server.clients_waiting_acks to delete
the client, and this search is O(N).

When WAIT (or WAITAOF) is used extensively in some cases, this
O(N) search may be time-consuming. We can remember the list node
and store it in the blockingState struct and it can avoid the
linear search in unblockClientWaitingReplicas.

Signed-off-by: Binbin <binloveplay1314@qq.com>
Signed-off-by: Ping Xie <pingxie@google.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
release-notes This issue should get a line item in the release notes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants