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

New version of seaweedfs can't list directory using s3 api #4668

Closed
Zen3515 opened this issue Jul 12, 2023 · 42 comments · Fixed by Cottand/seaweedfs#1
Closed

New version of seaweedfs can't list directory using s3 api #4668

Zen3515 opened this issue Jul 12, 2023 · 42 comments · Fixed by Cottand/seaweedfs#1

Comments

@Zen3515
Copy link

Zen3515 commented Jul 12, 2023

Sponsors SeaweedFS via Patreon https://www.patreon.com/seaweedfs
Report issues here. Ask questions here https://stackoverflow.com/questions/tagged/seaweedfs
Please ask questions in https://github.com/seaweedfs/seaweedfs/discussions

Describe the bug
Let me show you the behaviour, the client I used was minio client. with seaweed version 3.53 and 3.43

# using weed version 3.53
$ mc ls weed-admin/images/reverse_bag_images/
# NO OUTPUT FOR THE ABOVE COMMAND
$ mc ls weed-admin/images/reverse_bag_images
[2023-07-12 12:06:09 +07]     0B reverse_bag_images/

# revert update to version 3.43
$ mc ls weed-admin/images/reverse_bag_images
[2023-07-12 12:13:17 +07]     0B 2023-07-03/
[2023-07-12 12:13:17 +07]     0B 2023-07-04/
$ mc ls weed-admin/images/reverse_bag_images/
[2023-07-12 12:13:19 +07]     0B 2023-07-03/
[2023-07-12 12:13:19 +07]     0B 2023-07-04/

As you can see from the above output, s3 file list was only working when I uses the older version of seaweedfs.

My story

I was happily using seaweedfs for a long time without any problem, until yesterday that I try update my servers to newest version, that I encounter this problem. After browsing through the releases changes log, I suspect this release. My current solution is to revert the update, and continue to use 3.43 for now.

System Setup

$ uname -a
Linux example-storage-2 5.15.0-76-generic #83-Ubuntu SMP Thu Jun 15 19:16:32 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux

$ weed version
version 30GB 3.43 3227e4175e2bf8df2ac8aeeff8cf73a819abc5a7 linux amd64

$ /usr/bin/weed master -mdir=/etc/seaweedfs/weed-meta -ip=storage2.example.com -ip.bind=0.0.0.0 -port=9333 -defaultReplication=010 -peers=storage3.example.com:9333,storage4.example.com:9333
$ /usr/bin/weed volume -mserver=storage2.example.com:9333,storage3.example.com:9333,storage4.example.com:9333 -max=0 -ip.bind=0.0.0.0 -port=8081 -dataCenter=main-site -rack=example-storage-2 -dir=/mnt/nvme1/seaweedfs,/mnt/nvme2/seaweedfs,/mnt/nvme3/seaweedfs,/mnt/nvme4/seaweedfs
$ /usr/bin/weed filer -master=storage2.example.com:9333,storage3.example.com:9333,storage4.example.com:9333 -dataCenter=main-site -ip=storage2.example.com -ip.bind=0.0.0.0 -s3 -s3.dataCenter=main-site -s3.config=/etc/seaweedfs/s3-config.json -s3.cert.file=/data/letsencrypt/certificates/example-storage-2.example.com.crt -s3.key.file=/data/letsencrypt/certificates/example-storage-2.example.com.key
$ cat /etc/seaweedfs/filer.toml
# A TOML config file for SeaweedFS filer store

####################################################
# Customizable filer server options
####################################################
[filer.options]
# with http DELETE, by default the filer would check whether a folder is empty.
# recursive_delete will delete all sub folders and files, similar to "rm -Rf"
recursive_delete = false

####################################################
# The following are filer store options
####################################################
# We're forced to use redis sentinel since redis cluster require at least 6 nodes (we have only 4)
[redis3_sentinel]
enabled = true
addresses = ["redis1.storage.example:26379","redis2.storage.example:26379","redis3.storage.example:26379","redis4.storage.example:26379"]
masterName = "mymaster"
username = "default"
password = "RETRACTED"
database = 0

Expected behavior
I expect that I can use s3 api to list files

@ldapomni
Copy link

ldapomni commented Jul 12, 2023

I think this problem is of the same type with #4645. For the correct display of directories via s3, special metadata FolderMimeType = "httpd/unix-directory" was introduced. But it doesn't seem to be finished

@Zen3515
Copy link
Author

Zen3515 commented Jul 12, 2023

I think this problem is of the same type with #4645. For the correct display of directories via s3, special metadata FolderMimeType = "httpd/unix-directory" was introduced. But it doesn't seem to be finished

In that case, can you test if older version of seaweedfs works? Maybe we could pinpoint the exact version that this issue first appear.

@ldapomni
Copy link

Yes, it used to be fine in the old versions

@agipson33
Copy link
Contributor

agipson33 commented Jul 13, 2023

I can confirm this happened with the release of 3.46. Directory listings using the s3 service works as expected through version 3.45.

It is not clear to me which of these two PRs might have caused the issue #4390 or #4391

@chrislusf
Copy link
Collaborator

can not reproduce. Does this problem only happen to old directories?

@ldapomni
Copy link

ldapomni commented Jul 14, 2023

can not reproduce. Does this problem only happen to old directories?

Old directories work fine. Problem when creating new directories in NC. @kmlebedev should be aware of this issue

@Zen3515
Copy link
Author

Zen3515 commented Jul 14, 2023

can not reproduce. Does this problem only happen to old directories?

I could not revert my production back to reproduce, but here are my points based on my observation.

  • Filer works just fine (port 8888)
  • Directory depth may matters, the first folder (bucket) works, the nested one after this doesn't.
# Below are working
$ mc ls S3CON/ # listing bucket
$ mc ls S3CON/bucket/ # listing inside a bucket

# Below is not working
$ mc ls S3CON/bucket/folder_inside_bucket/ # listing folder inside a bucket
  • If I recall correctly, createing directory with fs.mkdir may works, but don't take my word for it.
  • My folder_inside_bucket are created solely with S3 api, and none of them work, despite the version.

@SmsS4
Copy link
Contributor

SmsS4 commented Jul 14, 2023

I have the same problem when connectin Filestash to my s3 and create a folder using Filestash

@chrislusf
Copy link
Collaborator

Some steps to reproduce this would be helpful.

@renweijun
Copy link
Contributor

Here are the steps to reproduce it:

echo "s3.bucket.create -name s3bucket "|weed shell
echo "s3.configure -access_key=s3bucket -secret_key=key123456 -buckets=s3bucket -user=s3bucket-actions=Read,Write,List,Tagging,Admin -apply"|weed shell
echo "mkdir buckets/s3bucket/2023/1" |weed shell
./weed filer.copy  filer.toml http://master1:8888/buckets/s3bucket/2023/1 
download s5cmd ,https://github.com/peak/s5cmd/releases/tag/v2.1.0
export S3_ENDPOINT_URL="http://master1:8888/"
export AWS_ACCESS_KEY_ID=s3bucket
export AWS_SECRET_ACCESS_KEY=key123456
weed version 3.46
./s5cmd ls  s3://s3bucket/2023/1/*
ERROR "ls s3://s3bucket/2023/1/*": no object found
weed version 3.45
./s5cmd ls  s3://s3bucket/2023/1/*
2023/07/14 12:04:42              7906  filer.toml

@chrislusf
Copy link
Collaborator

chrislusf commented Jul 16, 2023

@renweijun followed the steps, seems working well:

chrislu/tmp ()  > echo "s3.bucket.create -name s3bucket "|weed shell
I0716 13:33:23.053597 masterclient.go:209 master localhost:9333 redirected to leader 192.168.2.7:9333
.master: localhost:9333 filers: [192.168.2.7:8888]
create bucket under /buckets
created bucket s3bucket
chrislu/tmp ()  > echo "mkdir buckets/s3bucket/2023/1" |weed shell
I0716 13:33:33.647634 masterclient.go:209 master localhost:9333 redirected to leader 192.168.2.7:9333
.master: localhost:9333 filers: [192.168.2.7:8888]
chrislu/tmp ()  > weed filer.copy local11.option http://localhost:8888/buckets/s3bucket/2023/1
The last argument should be a folder and end with "/"
...
chrislu/tmp ()  > weed filer.copy local11.option http://localhost:8888/buckets/s3bucket/2023/1/
chrislu/tmp ()  > export S3_ENDPOINT_URL="http://localhost:8333/"
export AWS_ACCESS_KEY_ID=admin_access
export AWS_SECRET_ACCESS_KEY=admin_secret

chrislu/tmp ()  > s5cmd ls  s3://s3bucket/2023/1/*
zsh: no matches found: s3://s3bucket/2023/1/*
chrislu/tmp ()  > s5cmd ls  s3://s3bucket/2023/1/
2023/07/16 20:34:21               212  local11.option

@renweijun
Copy link
Contributor

renweijun commented Jul 17, 2023

@chrislusf Sorry, I just tested OK with 3.53 ,and I used 3.46 before

@chrislusf
Copy link
Collaborator

@Zen3515 @SmsS4 do you have some reproducible steps for 3.53?

@Zen3515
Copy link
Author

Zen3515 commented Jul 18, 2023

@Zen3515 @SmsS4 do you have some reproducible steps for 3.53?

I've spin up a new server just for this, surprisingly I could not reproduce it, The only different were

  1. I switch from redis3_sentinel to leveldb2.
  2. I switch from 3 nodes -- 3 masters, 3 volume, 3 filers to 1 node -- 1 master, 1 volume, 1 filer.

Can someone else confirm if they're using the same filer store?
@SmsS4 Do you uses Redis as well?
How many nodes do you have?

My production filer

# A TOML config file for SeaweedFS filer store

####################################################
# Customizable filer server options
####################################################
[filer.options]
# with http DELETE, by default the filer would check whether a folder is empty.
# recursive_delete will delete all sub folders and files, similar to "rm -Rf"
recursive_delete = false

####################################################
# The following are filer store options
####################################################
# We're forced to use redis sentinel since redis cluster require at least 6 nodes (we have only 4)
[redis3_sentinel]
enabled = true
addresses = ["redis1.storage.example:26379","redis2.storage.example:26379","redis3.storage.example:26379","redis4.storage.example:26379"]
masterName = "mymaster"
username = "default"
password = "RETRACTED"
database = 0

My new filer

# A TOML config file for SeaweedFS filer store

####################################################
# Customizable filer server options
####################################################
[filer.options]
# with http DELETE, by default the filer would check whether a folder is empty.
# recursive_delete will delete all sub folders and files, similar to "rm -Rf"
recursive_delete = false

####################################################
# The following are filer store options
####################################################
# We're forced to use redis sentinel since redis cluster require at least 6 nodes (we have only 4)
[leveldb2]
# local on disk, mostly for simple single-machine setup, fairly scalable
# faster than previous leveldb, recommended.
enabled = true
dir = "/etc/seaweedfs/filerldb2"                    # directory to store level db files

PS. If I switch using my production, yes, the problem still persist. I just don't know how should I reproduce it.

@agipson33
Copy link
Contributor

Using redis_cluster3 for filer backend and having the same reported issue.

@chrislusf
Copy link
Collaborator

@agipson33 please share reproducible steps.

@renweijun
Copy link
Contributor

renweijun commented Jul 20, 2023

can not reproduce. Does this problem only happen to old directories?

I could not revert my production back to reproduce, but here are my points based on my observation.

  • Filer works just fine (port 8888)
  • Directory depth may matters, the first folder (bucket) works, the nested one after this doesn't.
# Below are working
$ mc ls S3CON/ # listing bucket
$ mc ls S3CON/bucket/ # listing inside a bucket

# Below is not working
$ mc ls S3CON/bucket/folder_inside_bucket/ # listing folder inside a bucket
  • If I recall correctly, createing directory with fs.mkdir may works, but don't take my word for it.
  • My folder_inside_bucket are created solely with S3 api, and none of them work, despite the version.

This judgment is correct ,createing directory with fs.mkdir is works。my production with "s5cmd cp",
This PR seems to solve similar problems #4331
@chrislusf Here are the steps to reproduce it:

echo "s3.bucket.create -name s3bucket "|weed shell
echo "s3.configure -access_key=s3bucket -secret_key=key123456 -buckets=s3bucket -user=s3bucket-actions=Read,Write,List,Tagging,Admin -apply"|weed shell
download s5cmd ,https://github.com/peak/s5cmd/releases/tag/v2.1.0
export S3_ENDPOINT_URL="http://master1:8333/"
export AWS_ACCESS_KEY_ID=s3bucket
export AWS_SECRET_ACCESS_KEY=key123456

s5cmd cp filer.toml  s3://s3bucket/1

s5cmd cp filer.toml  s3://s3bucket/2/
s5cmd cp filer.toml  s3://s3bucket/3/1
s5cmd cp filer.toml  s3://s3bucket/4/1/
s5cmd cp filer.toml  s3://s3bucket/5/2/1
s5cmd cp filer.toml  s3://s3bucket/6/2/1/

weed version 3.45

s5cmd ls s3://s3bucket/1/
ERROR "ls s3://s3bucket/1/": no object found
s5cmd ls s3://s3bucket/2/
2023/07/20 08:43:39              7906 filer.toml

s5cmd ls  s3://s3bucket/3/
2023/07/20 08:43:39              7906 1
s5cmd ls  s3://s3bucket/3/1/
ERROR "ls s3://s3bucket/3/1/": no object found
s5cmd ls  s3://s3bucket/4/
DIR 1/
s5cmd ls  s3://s3bucket/4/1/
2023/07/20 08:43:41              7906 filer.toml

s5cmd ls  s3://s3bucket/5/
DIR 2/
s5cmd ls  s3://s3bucket/5/2/
2023/07/20 09:24:54              7906 1
s5cmd ls  s3://s3bucket/5/2/1/
ERROR "ls s3://s3bucket/5/2/1/": no object found
s5cmd ls  s3://s3bucket/6/
DIR 2/
s5cmd ls  s3://s3bucket/6/2/
DIR 1/
s5cmd ls  s3://s3bucket/6/2/1/
2023/07/20 09:15:40              7906 filer.toml

weed version 3.46~3.54

s5cmd ls s3://s3bucket/1/
ERROR "ls s3://s3bucket/1/": no object found
s5cmd ls s3://s3bucket/2/
ERROR "ls s3://s3bucket/2/": no object found

s5cmd ls  s3://s3bucket/3/
ERROR "ls s3://s3bucket/3/": no object found
s5cmd ls  s3://s3bucket/3/1/
ERROR "ls s3://s3bucket/3/1/": no object found
s5cmd ls  s3://s3bucket/4/
ERROR "ls s3://s3bucket/4/": no object found
s5cmd ls  s3://s3bucket/4/1/
2023/07/20 08:43:41              7906 filer.toml

s5cmd ls  s3://s3bucket/5/
ERROR "ls s3://s3bucket/5/": no object found
s5cmd ls  s3://s3bucket/5/2/
DIR 1/
s5cmd ls  s3://s3bucket/5/2/1/
2023/07/20 09:15:40              7906 filer.toml
s5cmd ls  s3://s3bucket/6/
ERROR "ls s3://s3bucket/6/": no object found
s5cmd ls  s3://s3bucket/6/2/
2023/07/20 09:24:54              7906 1
s5cmd ls  s3://s3bucket/6/2/1/
ERROR "ls s3://s3bucket/6/2/1/": no object found

@Zen3515
Copy link
Author

Zen3515 commented Jul 26, 2023

Hello,

@chrislusf have you been able to reproduce it yet?

@tomekit
Copy link

tomekit commented Aug 8, 2023

Hey, I was testing SeaweedFS with our cross-platform S3 GUI (https://s3drive.app/) and can confirm that problem is present in 3.55 and is not present in 3.45.

This creates: explicitFolder/ key:
Screenshot from 2023-08-08 13-35-44

3.55 (INCORRECT)

Screenshot from 2023-08-08 13-38-05

3.45 (CORRECT)

Screenshot from 2023-08-08 13-38-14

The implicitFolder (folder without explicit key/entry ending with /, but instead virtual CommonPrefix derived from all of the files and sub-folders inside of that folder) works fine on both versions:
Screenshot from 2023-08-08 13-37-17

Alternative way - using AWS CLI

Insert key

aws --endpoint=http://localhost:8333/ s3api put-object --bucket seabucket --key explicit/

Query

aws --endpoint=http://localhost:8333/ s3api list-objects-v2 --bucket seabucket --prefix explicit --delimiter "/"

Response 3.55 (INCORRECT)

{
    "Contents": [
        {
            "Key": "explicit/",
            "LastModified": "2023-08-08T11:22:57+00:00",
            "ETag": "\"d41d8cd98f00b204e9800998ecf8427e-0\"",
            "Size": 0,
            "StorageClass": "STANDARD",
            "Owner": {
                "ID": "3e8"
            }
        }
    ]
}

Response 3.45 (CORRECT)

{
    "CommonPrefixes": [
        {
            "Prefix": "explicit/"
        }
    ]
}

S3 Filer CMD

I wasn't touching master or volume processes, I was only switching version of S3 Filer.

# v3.55
weed filer -s3 -ip=127.0.0.1 -master=127.0.0.1:9333
# v3.45
weed345 filer -s3 -ip=127.0.0.1 -master=127.0.0.1:9333

CORS missing

You would be able to try it out using our S3 browser client (https://web.s3drive.app) but it I wasn't able to setup CORS properly for SeaweedFS without running front NginX.

@chrislusf
Copy link
Collaborator

@tomekit this seems opposite of what @renweijun 's finding, where implicitly created folders are having issues.

@tomekit
Copy link

tomekit commented Aug 8, 2023

I think we can only assume what's their folder structure, it looks like there maybe implicit (no guarantees though).
I am not finding any issues with implicitFolders though,whilst explicitFolders (according to raw S3 response) are clearly broken.
If I have chance I will play with different folder structures and mc tool.

@Zen3515
Copy link
Author

Zen3515 commented Sep 8, 2023

Potentially connected to PR #4778

It seems that the functionality for listing entries with the method ListDirectoryPrefixedEntries may not have been implemented for Redis and Mongodb, which could be a potential root cause.

@chrislusf
Copy link
Collaborator

can someone confirm whether #4834 fix this issue?

@zemul
Copy link
Contributor

zemul commented Sep 14, 2023

can someone confirm whether #4834 fix this issue?

Using redis, I successfully listed the directories.

@zemul
Copy link
Contributor

zemul commented Sep 14, 2023

Potentially connected to PR #4778

It seems that the functionality for listing entries with the method ListDirectoryPrefixedEntries may not have been implemented for Redis and Mongodb, which could be a potential root cause.

Listing subdirectories is done recursively, the ListDirectoryPrefixedEntries method is not required.

subNextMarker, subErr := s3a.doListFilerEntries(client, dir+"/"+entry.Name, "", cursor, "", delimiter, false, eachEntryFn)

@chrislusf
Copy link
Collaborator

@zemul Thanks for the confirmation!

@tomekit
Copy link

tomekit commented Sep 14, 2023

I am using this version: version 30GB 3.56 5407b8676669a8e6389121b37800301b5e5d4dc2 linux amd64
I think this is still broken as described in: #4668 (comment)

Test script

export BUCKET_NAME=bucket
export ENDPOINT=http://localhost:8333
export AWS_ACCESS_KEY_ID=admin
export AWS_SECRET_ACCESS_KEY=admin

aws --endpoint=$ENDPOINT s3api put-object --bucket $BUCKET_NAME --key explicit/
aws --endpoint=$ENDPOINT s3api list-objects-v2 --bucket $BUCKET_NAME --prefix explicit --delimiter "/"

Actual response (list-objects-v2)

{
    "Contents": [
        {
            "Key": "explicit/",
            "LastModified": "2023-09-14T09:27:18+00:00",
            "ETag": "\"d41d8cd98f00b204e9800998ecf8427e-0\"",
            "Size": 0,
            "StorageClass": "STANDARD",
            "Owner": {
                "ID": "3e8"
            }
        }
    ]
}

Expected

This comes from SeaweedFS 3.4.5, as well as any S3 provider including Backblaze

{
    "CommonPrefixes": [
        {
            "Prefix": "explicit/"
        }
    ]
}

Notes

This issue isn't present on: 3.4.5.

@chrislusf chrislusf reopened this Sep 14, 2023
@chrislusf
Copy link
Collaborator

chrislusf commented Sep 14, 2023

@tomekit The change came from #4399

The expected outputs are opposite.

@tomekit
Copy link

tomekit commented Sep 15, 2023

The expected outputs are opposite.

What do you mean?

The: #4399 uses cmd:

aws --endpoint-url http://127.0.0.1:8000 s3api list-objects-v2 --bucket yournamehere-ir3pmbprn9gt03xb-1 --no-cli-pager --prefix "asdf/" --delimiter '/'

please note that the prefix used above ends with /, whereas in my test case (#4668 (comment)) I am using prefix query that doesn't end with: /.

I am explicitly trying SeaweedFS S3 API to return some path prefixes (aka folders), but it doesn't seem to be the case with the current version.

@lakeland1990
Copy link
Contributor

I found the same problem . s3api cannot list object with prefix when filer store is Cassandra
is there any progress on this issue?

@lakeland1990
Copy link
Contributor

lakeland1990 commented Oct 8, 2023

finnally, I reproduce the problem

restic init

everything is normal when I just run the weed server and just init the repo of restic

(base) [root@cmdb seaweedfs]# ./restic init -r s3:http://10.1.1.110:8333/test357/dmdb --password-file pass.txt
created restic repository 7b0edd99cf at s3:http://10.1.1.110:8333/test357/dmdb

Please note that knowledge of your password is required to access
the repository. Losing your password means that your data is
irrecoverably lost.
(base) [root@cmdb seaweedfs]# s3cmd ls s3://test357/dmdb/keys/
2023-10-08 19:44          460  s3://test357/dmdb/keys/1bac41bc7db15124c072caeb4fcf3a3e7b507612860ca6eb3e100131d4f1f593

restic backup

Then I use restic backup

(base) [root@cmdb seaweedfs]# ./restic backup -r s3:http://10.1.1.110:8333/test357/dmdb --password-file pass.txt /home/zabbix/bin
repository 7b0edd99 opened (version 2, compression level auto)
created new cache in /root/.cache/restic
no parent snapshot found, will read all files

Files:           2 new,     0 changed,     0 unmodified
Dirs:            3 new,     0 changed,     0 unmodified
Added to the repository: 2.585 KiB (1.691 KiB stored)

processed 2 files, 679 B in 0:09
snapshot 323268eb saved

It done well
but after that s3api cannot list keys directory

(base) [root@cmdb seaweedfs]# s3cmd ls s3://test357/dmdb/keys/
(base) [root@cmdb seaweedfs]# 

LOG -v 4

and I set -v 4 the log shows that:
THE ERROR DIRECTORY


I1009 03:54:54.145205 s3api_objects_list_handlers.go:98 ListObjectsV1Handler test357
I1009 03:54:54.145592 filer_grpc_server.go:42 ListEntries directory:"/buckets/test357/dmdb"  prefix:"keys"  limit:1
I1009 03:54:54.148435 filer_client.go:168 exists entry /buckets/test357: directory:"/buckets"  name:"test357"
I1009 03:54:54.148604 filer_grpc_server.go:24 LookupDirectoryEntry /buckets/test357
I1009 03:54:54.151019 error_handler.go:96 status 200 application/xml: <?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Name>test357</Name><Prefix>dmdb/keys/</Prefix><Marker></Marker><MaxKeys>10000</MaxKeys><Delimiter>/</Delimiter><IsTruncated>false</IsTruncated></ListBucketResult>
I1009 03:54:56.808638 volume_grpc_client_to_master.go:232 volume server 10.1.1.110:8080 heartbeat

and when i list the normal directory the log shows this
THE NORMAL DIRECTORY



I1009 04:11:15.371998 filer_grpc_server.go:42 ListEntries directory:"/buckets/test357/emdb"  prefix:"keys"  limit:1
I1009 04:11:15.374991 filer_grpc_server.go:42 ListEntries directory:"/buckets/test357/emdb/keys"  limit:10002
I1009 04:11:15.380565 error_handler.go:96 status 200 application/xml: <?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Name>test357</Name><Prefix>emdb/keys/</Prefix><Marker></Marker><MaxKeys>10000</MaxKeys><Delimiter>/</Delimiter><IsTruncated>false</IsTruncated><Contents><Key>emdb/keys/5420a94ba762866ce3553594438c3dd605ab1a0c8cb62135c4825f58ef9b6d0a</Key><ETag>&#34;a64103be2912d48808863045fead96ef&#34;</ETag><Size>460</Size><Owner><ID>0</ID></Owner><StorageClass>STANDARD</StorageClass><LastModified>2023-10-08T20:10:46Z</LastModified></Contents></ListBucketResult>

What the different between the two directory ?
@chrislusf

After i change to 3.4.5 fixed it
seems s3 fix get list of dir object key with slash suffix #4391 the change of limit caused it ?
3.4.5 log

<LocationConstraint><LocationConstraint xmlns="http://s3.amazonaws.com/doc/2006-03-01/"></LocationConstraint></LocationConstraint>
I1009 05:14:53.738064 s3api_objects_list_handlers.go:100 ListObjectsV1Handler test357
I1009 05:14:53.738355 filer_grpc_server.go:40 ListEntries directory:"/buckets/test357/dmdb"  limit:10002
I1009 05:14:53.741547 filer_grpc_server.go:40 ListEntries directory:"/buckets/test357/dmdb"  startFromFileName:"snapshots"  limit:9996
I1009 05:14:53.743788 error_handler.go:96 status 200 application/xml: <?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><Name>test357</Name><Prefix>dmdb/</Prefix><Marker></Marker><MaxKeys>10000</MaxKeys><Delimiter>/</Delimiter><IsTruncated>false</IsTruncated><Contents><Key>dmdb/config</Key><ETag>&#34;67a35e863d85387eb34660880c14410b&#34;</ETag><Size>155</Size><Owner><ID>0</ID></Owner><StorageClass>STANDARD</StorageClass><LastModified>2023-10-08T19:44:06Z</LastModified></Contents><CommonPrefixes><Prefix>dmdb/data/</Prefix></CommonPrefixes><CommonPrefixes><Prefix>dmdb/index/</Prefix></CommonPrefixes><CommonPrefixes><Prefix>dmdb/keys/</Prefix></CommonPrefixes><CommonPrefixes><Prefix>dmdb/locks/</Prefix></CommonPrefixes><CommonPrefixes><Prefix>dmdb/snapshots/</Prefix></CommonPrefixes></ListBucketResult>

@chrislusf
Copy link
Collaborator

which version you use to reproduce the problem?

@lakeland1990
Copy link
Contributor

lakeland1990 commented Oct 9, 2023

which version you use to reproduce the problem?

3.5.7 3.4.6 large disk version both amd64 and arm64 has this bug

3.4.5 is all right

@kmlebedev
Copy link
Contributor

I just rechecked the compatibility tests with the s3 interface and found no errors for version 3.57

Defaulted container "s3tests" out of: s3tests, seaweedfs
s3tests_boto3.functional.test_s3.test_bucket_list_empty ... ok
s3tests_boto3.functional.test_s3.test_bucket_list_distinct ... ok
s3tests_boto3.functional.test_s3.test_bucket_list_many ... ok
s3tests_boto3.functional.test_s3.test_bucket_listv2_many ... ok
s3tests_boto3.functional.test_s3.test_basic_key_count ... ok
s3tests_boto3.functional.test_s3.test_bucket_listv2_delimiter_prefix ... ok
s3tests_boto3.functional.test_s3.test_bucket_listv2_delimiter_prefix_underscore ... ok
s3tests_boto3.functional.test_s3.test_bucket_list_delimiter_empty ... ok
s3tests_boto3.functional.test_s3.test_bucket_listv2_delimiter_empty ... ok
s3tests_boto3.functional.test_s3.test_bucket_list_delimiter_none ... ok
s3tests_boto3.functional.test_s3.test_bucket_listv2_delimiter_none ... ok
s3tests_boto3.functional.test_s3.test_bucket_listv2_fetchowner_notempty ... ok
s3tests_boto3.functional.test_s3.test_bucket_list_delimiter_not_exist ... ok
s3tests_boto3.functional.test_s3.test_bucket_listv2_delimiter_not_exist ... ok
s3tests_boto3.functional.test_s3.test_bucket_list_prefix_basic ... ok
s3tests_boto3.functional.test_s3.test_bucket_listv2_prefix_basic ... ok
s3tests_boto3.functional.test_s3.test_bucket_list_prefix_alt ... ok
s3tests_boto3.functional.test_s3.test_bucket_listv2_prefix_alt ... ok
s3tests_boto3.functional.test_s3.test_bucket_list_prefix_empty ... ok
s3tests_boto3.functional.test_s3.test_bucket_listv2_prefix_empty ... ok
s3tests_boto3.functional.test_s3.test_bucket_list_prefix_none ... ok
s3tests_boto3.functional.test_s3.test_bucket_listv2_prefix_none ... ok
s3tests_boto3.functional.test_s3.test_bucket_list_prefix_not_exist ... ok
s3tests_boto3.functional.test_s3.test_bucket_listv2_prefix_not_exist ... ok
s3tests_boto3.functional.test_s3.test_bucket_list_prefix_unreadable ... ok
s3tests_boto3.functional.test_s3.test_bucket_listv2_prefix_unreadable ... ok
s3tests_boto3.functional.test_s3.test_bucket_list_prefix_delimiter_basic ... ok
s3tests_boto3.functional.test_s3.test_bucket_listv2_prefix_delimiter_basic ... ok
s3tests_boto3.functional.test_s3.test_bucket_list_maxkeys_one ... ok
s3tests_boto3.functional.test_s3.test_bucket_listv2_maxkeys_one ... ok
s3tests_boto3.functional.test_s3.test_bucket_list_maxkeys_zero ... ok
s3tests_boto3.functional.test_s3.test_bucket_listv2_maxkeys_zero ... ok
s3tests_boto3.functional.test_s3.test_bucket_list_marker_none ... ok
s3tests_boto3.functional.test_s3.test_bucket_list_marker_empty ... ok
s3tests_boto3.functional.test_s3.test_bucket_listv2_continuationtoken ... ok
s3tests_boto3.functional.test_s3.test_bucket_listv2_both_continuationtoken_startafter ... ok
s3tests_boto3.functional.test_s3.test_bucket_list_marker_unreadable ... ok
s3tests_boto3.functional.test_s3.test_bucket_listv2_startafter_unreadable ... ok
s3tests_boto3.functional.test_s3.test_bucket_list_marker_not_in_list ... ok
s3tests_boto3.functional.test_s3.test_bucket_listv2_startafter_not_in_list ... ok
s3tests_boto3.functional.test_s3.test_bucket_list_marker_after_list ... ok
s3tests_boto3.functional.test_s3.test_bucket_listv2_startafter_after_list ... ok
s3tests_boto3.functional.test_s3.test_bucket_delete_notexist ... ok
s3tests_boto3.functional.test_s3.test_bucket_create_delete ... ok
s3tests_boto3.functional.test_s3.test_object_read_not_exist ... ok
s3tests_boto3.functional.test_s3.test_multi_object_delete ... ok
s3tests_boto3.functional.test_s3.test_multi_objectv2_delete ... ok
s3tests_boto3.functional.test_s3.test_object_head_zero_bytes ... ok
s3tests_boto3.functional.test_s3.test_object_write_check_etag ... ok
s3tests_boto3.functional.test_s3.test_object_write_cache_control ... ok
s3tests_boto3.functional.test_s3.test_object_write_expires ... ok
s3tests_boto3.functional.test_s3.test_object_write_read_update_read_delete ... ok
s3tests_boto3.functional.test_s3.test_object_set_get_non_utf8_metadata ... ok
s3tests_boto3.functional.test_s3.test_object_set_get_metadata_empty_to_unreadable_prefix ... ok
s3tests_boto3.functional.test_s3.test_object_set_get_metadata_empty_to_unreadable_suffix ... ok
s3tests_boto3.functional.test_s3.test_object_set_get_metadata_empty_to_unreadable_infix ... ok
s3tests_boto3.functional.test_s3.test_object_metadata_replaced_on_put ... ok
s3tests_boto3.functional.test_s3.test_object_write_file ... ok
s3tests_boto3.functional.test_s3.test_post_object_invalid_date_format ... ok
s3tests_boto3.functional.test_s3.test_post_object_no_key_specified ... ok
s3tests_boto3.functional.test_s3.test_post_object_missing_signature ... ok
s3tests_boto3.functional.test_s3.test_post_object_condition_is_case_sensitive ... ok
s3tests_boto3.functional.test_s3.test_post_object_expires_is_case_sensitive ... ok
s3tests_boto3.functional.test_s3.test_post_object_missing_expires_condition ... ok
s3tests_boto3.functional.test_s3.test_post_object_missing_conditions_list ... ok
s3tests_boto3.functional.test_s3.test_post_object_upload_size_limit_exceeded ... ok
s3tests_boto3.functional.test_s3.test_post_object_missing_content_length_argument ... ok
s3tests_boto3.functional.test_s3.test_post_object_invalid_content_length_argument ... ok
s3tests_boto3.functional.test_s3.test_post_object_upload_size_below_minimum ... ok
s3tests_boto3.functional.test_s3.test_post_object_empty_conditions ... ok
s3tests_boto3.functional.test_s3.test_get_object_ifmatch_good ... ok
s3tests_boto3.functional.test_s3.test_get_object_ifmatch_failed ... ok
s3tests_boto3.functional.test_s3.test_get_object_ifnonematch_good ... ok
s3tests_boto3.functional.test_s3.test_get_object_ifnonematch_failed ... ok
s3tests_boto3.functional.test_s3.test_get_object_ifmodifiedsince_good ... ok
s3tests_boto3.functional.test_s3.test_get_object_ifmodifiedsince_failed ... ok
s3tests_boto3.functional.test_s3.test_get_object_ifunmodifiedsince_failed ... ok
s3tests_boto3.functional.test_s3.test_object_raw_get ... ok
s3tests_boto3.functional.test_s3.test_object_raw_get_object_gone ... ok
s3tests_boto3.functional.test_s3.test_bucket_head ... ok
s3tests_boto3.functional.test_s3.test_bucket_head_notexist ... ok
s3tests_boto3.functional.test_s3.test_object_raw_authenticated ... ok
s3tests_boto3.functional.test_s3.test_object_raw_authenticated_bucket_acl ... ok
s3tests_boto3.functional.test_s3.test_object_raw_authenticated_object_acl ... ok
s3tests_boto3.functional.test_s3.test_object_raw_authenticated_object_gone ... ok
s3tests_boto3.functional.test_s3.test_object_raw_get_x_amz_expires_not_expired ... ok
s3tests_boto3.functional.test_s3.test_object_raw_get_x_amz_expires_out_range_zero ... ok
s3tests_boto3.functional.test_s3.test_object_anon_put ... ok
s3tests_boto3.functional.test_s3.test_object_put_authenticated ... ok
s3tests_boto3.functional.test_s3.test_bucket_recreate_overwrite_acl ... ok
s3tests_boto3.functional.test_s3.test_bucket_recreate_new_acl ... ok
s3tests_boto3.functional.test_s3.test_buckets_create_then_list ... ok
s3tests_boto3.functional.test_s3.test_buckets_list_ctime ... ok
s3tests_boto3.functional.test_s3.test_list_buckets_invalid_auth ... ok
s3tests_boto3.functional.test_s3.test_list_buckets_bad_auth ... ok
s3tests_boto3.functional.test_s3.test_bucket_create_naming_good_starts_alpha ... ok
s3tests_boto3.functional.test_s3.test_bucket_create_naming_good_starts_digit ... ok
s3tests_boto3.functional.test_s3.test_bucket_create_naming_good_contains_period ... ok
s3tests_boto3.functional.test_s3.test_bucket_create_naming_good_contains_hyphen ... ok
s3tests_boto3.functional.test_s3.test_bucket_create_special_key_names ... ok
s3tests_boto3.functional.test_s3.test_bucket_list_special_prefix ... ok
s3tests_boto3.functional.test_s3.test_object_copy_zero_size ... ok
s3tests_boto3.functional.test_s3.test_object_copy_same_bucket ... ok
s3tests_boto3.functional.test_s3.test_object_copy_to_itself ... ok
s3tests_boto3.functional.test_s3.test_object_copy_diff_bucket ... ok
s3tests_boto3.functional.test_s3.test_object_copy_canned_acl ... ok
s3tests_boto3.functional.test_s3.test_object_copy_bucket_not_found ... ok
s3tests_boto3.functional.test_s3.test_object_copy_key_not_found ... ok
s3tests_boto3.functional.test_s3.test_multipart_copy_small ... ok
s3tests_boto3.functional.test_s3.test_multipart_copy_without_range ... ok
s3tests_boto3.functional.test_s3.test_multipart_upload_multiple_sizes ... ok
s3tests_boto3.functional.test_s3.test_multipart_copy_multiple_sizes ... ok
s3tests_boto3.functional.test_s3.test_multipart_upload_contents ... ok
s3tests_boto3.functional.test_s3.test_multipart_upload_overwrite_existing_object ... ok
s3tests_boto3.functional.test_s3.test_abort_multipart_upload ... ok
s3tests_boto3.functional.test_s3.test_list_multipart_upload ... ok
s3tests_boto3.functional.test_s3.test_atomic_read_1mb ... ok
s3tests_boto3.functional.test_s3.test_atomic_read_4mb ... ok
s3tests_boto3.functional.test_s3.test_atomic_read_8mb ... ok
s3tests_boto3.functional.test_s3.test_atomic_write_1mb ... ok
s3tests_boto3.functional.test_s3.test_atomic_write_4mb ... ok
s3tests_boto3.functional.test_s3.test_atomic_write_8mb ... ok
s3tests_boto3.functional.test_s3.test_atomic_dual_write_1mb ... ok
s3tests_boto3.functional.test_s3.test_atomic_dual_write_4mb ... ok
s3tests_boto3.functional.test_s3.test_atomic_dual_write_8mb ... ok
s3tests_boto3.functional.test_s3.test_atomic_multipart_upload_write ... ok
s3tests_boto3.functional.test_s3.test_multipart_resend_first_finishes_last ... ok
s3tests_boto3.functional.test_s3.test_ranged_request_response_code ... ok
s3tests_boto3.functional.test_s3.test_ranged_big_request_response_code ... ok
s3tests_boto3.functional.test_s3.test_ranged_request_skip_leading_bytes_response_code ... ok
s3tests_boto3.functional.test_s3.test_ranged_request_return_trailing_bytes_response_code ... ok
s3tests_boto3.functional.test_s3.test_copy_object_ifmatch_good ... ok
s3tests_boto3.functional.test_s3.test_copy_object_ifnonematch_failed ... ok
s3tests_boto3.functional.test_s3.test_delete_bucket_encryption ... ok

@tomekit
Copy link

tomekit commented Oct 9, 2023

I just rechecked the compatibility tests with the s3 interface and found no errors for version 3.57

Then it's a proof that these tests needs to be updated.

@kmlebedev
Copy link
Contributor

which version you use to reproduce the problem?

3.5.7 3.4.6 large disk version both amd64 and arm64 has this bug

3.4.5 is all right

It seems like it's a 44ad072 thing.

@lakeland1990
Copy link
Contributor

I just rechecked the compatibility tests with the s3 interface and found no errors for version 3.57

Defaulted container "s3tests" out of: s3tests, seaweedfs
s3tests_boto3.functional.test_s3.test_bucket_list_empty ... ok
s3tests_boto3.functional.test_s3.test_bucket_list_distinct ... ok

This error is not shown before i run
restic backup
very strange

@ldapomni
Copy link

Nextcloud v27 default setup, external storage s3 swfs (3.57). Folder bug present.

@lakeland1990
Copy link
Contributor

@chrislusf
@kmlebedev

Potentially connected to PR #4778
It seems that the functionality for listing entries with the method ListDirectoryPrefixedEntries may not have been implemented for Redis and Mongodb, which could be a potential root cause.

Listing subdirectories is done recursively, the ListDirectoryPrefixedEntries method is not required.

subNextMarker, subErr := s3a.doListFilerEntries(client, dir+"/"+entry.Name, "", cursor, "", delimiter, false, eachEntryFn)

I review the code and found ListDirectoryPrefixedEntries method is required by doListFilerEntries
-> then goes to

stream, listErr := client.ListEntries(ctx, request)

-> then
func (fs *FilerServer) ListEntries(req *filer_pb.ListEntriesRequest, stream filer_pb.SeaweedFiler_ListEntriesServer) (err error) {

lastFileName, listErr = fs.filer.StreamListDirectoryEntries(stream.Context(), util.FullPath(req.Directory), lastFileName, includeLastFile, int64(paginationLimit), req.Prefix, "", "", func(entry *filer.Entry) bool {

......
-> at the end it call
func (f *Filer) doListDirectoryEntries(ctx context.Context, p util.FullPath, startFileName string, inclusive bool, limit int64, prefix string, eachEntryFunc ListEachEntryFunc) (expiredCount int64, lastFileName string, err error) {
lastFileName, err = f.Store.ListDirectoryPrefixedEntries(ctx, p, startFileName, inclusive, limit, prefix, func(entry *Entry) bool {
select {

which call ListDirectoryPrefixedEntries method

@lakeland1990
Copy link
Contributor

lakeland1990 commented Oct 18, 2023

@chrislusf
@kmlebedev
After code review and debug,I found the root cause:

ListDirectoryPrefixedEntries in weed/filer/filerstore_wrapper.go called.

and it will try to use ListDirectoryPrefixedEntries of actualStore to get the file list

and when ListDirectoryPrefixedEntries return ErrUnsupportedListDirectoryPrefixed ERROR

it will fall back to ListDirectoryEntries with

func (fsw *FilerStoreWrapper) prefixFilterEntries(ctx context.Context, dirPath util.FullPath, startFileName string, includeStartFile bool, limit int64, prefix string, eachEntryFunc ListEachEntryFunc) (lastFileName string, err error) {
actualStore := fsw.getActualStore(dirPath + "/")
if prefix == "" {
return actualStore.ListDirectoryEntries(ctx, dirPath, startFileName, includeStartFile, limit, eachEntryFunc)
}
var notPrefixed []*Entry
lastFileName, err = actualStore.ListDirectoryEntries(ctx, dirPath, startFileName, includeStartFile, limit, func(entry *Entry) bool {
notPrefixed = append(notPrefixed, entry)
return true
})
if err != nil {
return
}
count := int64(0)
for count < limit && len(notPrefixed) > 0 {
for _, entry := range notPrefixed {
if strings.HasPrefix(entry.Name(), prefix) {
count++
if !eachEntryFunc(entry) {
return
}
if count >= limit {
break
}
}
}
if count < limit && lastFileName <= prefix && len(notPrefixed) == int(limit) {
notPrefixed = notPrefixed[:0]
lastFileName, err = actualStore.ListDirectoryEntries(ctx, dirPath, lastFileName, false, limit, func(entry *Entry) bool {
notPrefixed = append(notPrefixed, entry)
return true
})
if err != nil {
return
}
} else {
break
}
}
return
}

it will works well in the past , but unfortunately,with #4391
the parameter "limit" set to 1
so it will only worked well when the directory you list is the first of the parent directory.

restic init create only one directory named keys and it worked well, after we restic backup directory "data" and other directory created by restic. so s3api cannot list keys directory , it report the key error

I am not familiar with golang , but if I thought change the limit parameter in the fall back in

if err == ErrUnsupportedListDirectoryPrefixed {
lastFileName, err = fsw.prefixFilterEntries(ctx, dirPath, startFileName, includeStartFile, limit, prefix, adjustedEntryFunc)
}

will fix this bug and not need to full fill every ListDirectoryPrefixedEntries in every kind of filer store.

@kmlebedev
Copy link
Contributor

I haven't seen a quick solution yet, it's a very confusing case. It may be worth increasing the limit with a reserve for the service folders in the root. But in any case, first you need to run an integration test with the redis storage

@lakeland1990
Copy link
Contributor

lakeland1990 commented Oct 18, 2023

I haven't seen a quick solution yet, it's a very confusing case. It may be worth increasing the limit with a reserve for the service folders in the root. But in any case, first you need to run an integration test with the redis storage

I found another strange code may be the real problem

in the loop

for count < limit && len(notPrefixed) > 0 {
for _, entry := range notPrefixed {
if strings.HasPrefix(entry.Name(), prefix) {
count++
if !eachEntryFunc(entry) {
return
}
if count >= limit {
break
}
}
}
if count < limit && lastFileName <= prefix && len(notPrefixed) == int(limit) {
notPrefixed = notPrefixed[:0]
lastFileName, err = actualStore.ListDirectoryEntries(ctx, dirPath, lastFileName, false, limit, func(entry *Entry) bool {
notPrefixed = append(notPrefixed, entry)
return true
})
if err != nil {
return
}
} else {
break
}
}

the programe loop to find if files in parent directory have prefix

and in

if count < limit && lastFileName <= prefix && len(notPrefixed) == int(limit) {
notPrefixed = notPrefixed[:0]
lastFileName, err = actualStore.ListDirectoryEntries(ctx, dirPath, lastFileName, false, limit, func(entry *Entry) bool {
notPrefixed = append(notPrefixed, entry)
return true
})
if err != nil {
return
}
} else {
break
}
}

it fetch more object after the lastfile

what really strange is in

if count < limit && lastFileName <= prefix && len(notPrefixed) == int(limit) {

it juge len(notPrefixed) == int(limit)
and i don't know why
I delete this jugement and the s3 api works fine
i found it is commit in 4ee0a6f
@chrislusf

lakeland1990 added a commit to lakeland1990/seaweedfs that referenced this issue Oct 18, 2023
lakeland1990 added a commit to lakeland1990/seaweedfs that referenced this issue Oct 19, 2023
chrislusf added a commit to lakeland1990/seaweedfs that referenced this issue Oct 19, 2023
lakeland1990 added a commit to lakeland1990/seaweedfs that referenced this issue Oct 20, 2023
lakeland1990 added a commit to lakeland1990/seaweedfs that referenced this issue Oct 26, 2023
kmlebedev pushed a commit to kmlebedev/seaweedfs that referenced this issue Dec 22, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

10 participants