Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
N
neoppod
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Vincent Pelletier
neoppod
Commits
071c6bf5
Commit
071c6bf5
authored
Feb 07, 2024
by
Julien Muchembled
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
New API to iterate over non-deleted OIDs
This will be used by an external GC. To be pushed upstream.
parent
ad62c5c7
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
137 additions
and
5 deletions
+137
-5
neo/client/app.py
neo/client/app.py
+38
-1
neo/client/handlers/storage.py
neo/client/handlers/storage.py
+3
-0
neo/lib/protocol.py
neo/lib/protocol.py
+8
-1
neo/storage/database/importer.py
neo/storage/database/importer.py
+2
-1
neo/storage/database/manager.py
neo/storage/database/manager.py
+8
-0
neo/storage/database/mysql.py
neo/storage/database/mysql.py
+17
-1
neo/storage/database/sqlite.py
neo/storage/database/sqlite.py
+15
-1
neo/storage/handlers/client.py
neo/storage/handlers/client.py
+7
-0
neo/tests/protocol
neo/tests/protocol
+2
-0
neo/tests/threaded/test.py
neo/tests/threaded/test.py
+37
-0
No files found.
neo/client/app.py
View file @
071c6bf5
...
@@ -18,6 +18,7 @@ import heapq
...
@@ -18,6 +18,7 @@ import heapq
import
random
import
random
import
time
import
time
from
collections
import
defaultdict
from
collections
import
defaultdict
from
functools
import
partial
try
:
try
:
from
ZODB._compat
import
dumps
,
loads
,
_protocol
from
ZODB._compat
import
dumps
,
loads
,
_protocol
...
@@ -29,7 +30,7 @@ from ZODB.POSException import UndoError, ConflictError, ReadConflictError
...
@@ -29,7 +30,7 @@ from ZODB.POSException import UndoError, ConflictError, ReadConflictError
from
neo.lib
import
logging
from
neo.lib
import
logging
from
neo.lib.compress
import
decompress_list
,
getCompress
from
neo.lib.compress
import
decompress_list
,
getCompress
from
neo.lib.protocol
import
NodeTypes
,
Packets
,
\
from
neo.lib.protocol
import
NodeTypes
,
Packets
,
\
INVALID_PARTITION
,
MAX_TID
,
ZERO_HASH
,
ZERO_TID
INVALID_PARTITION
,
MAX_TID
,
ZERO_HASH
,
ZERO_
OID
,
ZERO_
TID
from
neo.lib.util
import
makeChecksum
,
dump
from
neo.lib.util
import
makeChecksum
,
dump
from
neo.lib.locking
import
Empty
,
Lock
from
neo.lib.locking
import
Empty
,
Lock
from
neo.lib.connection
import
MTClientConnection
,
ConnectionClosed
from
neo.lib.connection
import
MTClientConnection
,
ConnectionClosed
...
@@ -952,6 +953,42 @@ class Application(ThreadedApplication):
...
@@ -952,6 +953,42 @@ class Application(ThreadedApplication):
append
(
txn_info
)
append
(
txn_info
)
return
(
tid
,
txn_list
)
return
(
tid
,
txn_list
)
def
oids
(
self
,
tid
=
None
,
min_oid
=
None
,
max_oid
=
None
):
if
tid
is
None
:
tid
=
self
.
last_tid
h
=
[]
def
oids
(
offset
,
oid
=
min_oid
or
ZERO_OID
):
while
True
:
oid
,
oid_list
=
self
.
_askStorageForRead
(
offset
,
Packets
.
AskOIDsFrom
(
offset
,
1000
,
oid
,
tid
))
i
=
partial
(
next
,
iter
(
oid_list
))
try
:
return
[
i
(),
i
,
offset
,
oid
]
except
StopIteration
:
if
oid
is
None
or
None
is
not
max_oid
<
oid
:
return
h
=
[
x
for
x
in
map
(
oids
,
xrange
(
self
.
pt
.
getPartitions
()))
if
x
]
heapq
.
heapify
(
h
)
heappop
=
partial
(
heapq
.
heappop
,
h
)
heappushpop
=
partial
(
heapq
.
heappushpop
,
h
)
while
h
:
x
=
heappop
()
while
True
:
oid
=
x
[
0
]
if
None
is
not
max_oid
<
oid
:
return
yield
oid
try
:
x
[
0
]
=
x
[
1
]()
except
StopIteration
:
oid
=
x
[
3
]
if
oid
is
None
:
break
x
=
oids
(
x
[
2
],
oid
)
if
not
x
:
break
x
=
heappushpop
(
x
)
def
history
(
self
,
oid
,
size
=
1
,
filter
=
None
):
def
history
(
self
,
oid
,
size
=
1
,
filter
=
None
):
packet
=
Packets
.
AskObjectHistory
(
oid
,
0
,
size
)
packet
=
Packets
.
AskObjectHistory
(
oid
,
0
,
size
)
result
=
[]
result
=
[]
...
...
neo/client/handlers/storage.py
View file @
071c6bf5
...
@@ -168,6 +168,9 @@ class StorageAnswersHandler(AnswerBaseHandler):
...
@@ -168,6 +168,9 @@ class StorageAnswersHandler(AnswerBaseHandler):
logging
.
debug
(
'Get %u TIDs from %r'
,
len
(
tid_list
),
conn
)
logging
.
debug
(
'Get %u TIDs from %r'
,
len
(
tid_list
),
conn
)
self
.
app
.
setHandlerData
(
tid_list
)
self
.
app
.
setHandlerData
(
tid_list
)
def
answerOIDsFrom
(
self
,
conn
,
*
args
):
self
.
app
.
setHandlerData
(
args
)
def
answerTransactionInformation
(
self
,
conn
,
tid
,
def
answerTransactionInformation
(
self
,
conn
,
tid
,
user
,
desc
,
ext
,
packed
,
oid_list
):
user
,
desc
,
ext
,
packed
,
oid_list
):
self
.
app
.
setHandlerData
(({
self
.
app
.
setHandlerData
(({
...
...
neo/lib/protocol.py
View file @
071c6bf5
...
@@ -26,7 +26,7 @@ except ImportError:
...
@@ -26,7 +26,7 @@ except ImportError:
# The protocol version must be increased whenever upgrading a node may require
# The protocol version must be increased whenever upgrading a node may require
# to upgrade other nodes.
# to upgrade other nodes.
PROTOCOL_VERSION
=
3
PROTOCOL_VERSION
=
4
# By encoding the handshake packet with msgpack, the whole NEO stream can be
# By encoding the handshake packet with msgpack, the whole NEO stream can be
# decoded with msgpack. The first byte is 0x92, which is different from TLS
# decoded with msgpack. The first byte is 0x92, which is different from TLS
# Handshake (0x16).
# Handshake (0x16).
...
@@ -673,6 +673,13 @@ class Packets(dict):
...
@@ -673,6 +673,13 @@ class Packets(dict):
:nodes: C -> S
:nodes: C -> S
"""
)
"""
)
AskOIDsFrom
,
AnswerOIDsFrom
=
request
(
"""
Iterate over non-deleted OIDs starting at min_oid.
The order of OIDs is ascending.
:nodes: C -> S
"""
)
WaitForPack
,
WaitedForPack
=
request
(
"""
WaitForPack
,
WaitedForPack
=
request
(
"""
Wait until pack given by tid is completed.
Wait until pack given by tid is completed.
...
...
neo/storage/database/importer.py
View file @
071c6bf5
...
@@ -378,7 +378,8 @@ class ImporterDatabaseManager(DatabaseManager):
...
@@ -378,7 +378,8 @@ class ImporterDatabaseManager(DatabaseManager):
*
args
,
**
kw
)
*
args
,
**
kw
)
implements
(
self
,
"""_getNextTID checkSerialRange checkTIDRange _pack
implements
(
self
,
"""_getNextTID checkSerialRange checkTIDRange _pack
deleteObject deleteTransaction _dropPartition _getLastTID nonempty
deleteObject deleteTransaction _dropPartition _getLastTID nonempty
getReplicationObjectList _getTIDList _setPartitionPacked"""
.
split
())
getReplicationObjectList _getTIDList _setPartitionPacked oidsFrom
"""
.
split
())
_getPartition
=
property
(
lambda
self
:
self
.
db
.
_getPartition
)
_getPartition
=
property
(
lambda
self
:
self
.
db
.
_getPartition
)
_getReadablePartition
=
property
(
lambda
self
:
self
.
db
.
_getReadablePartition
)
_getReadablePartition
=
property
(
lambda
self
:
self
.
db
.
_getReadablePartition
)
...
...
neo/storage/database/manager.py
View file @
071c6bf5
...
@@ -1445,6 +1445,14 @@ class DatabaseManager(object):
...
@@ -1445,6 +1445,14 @@ class DatabaseManager(object):
raise
NonReadableCell
raise
NonReadableCell
return
()
return
()
@
abstract
def
oidsFrom
(
self
,
partition
,
length
,
min_oid
,
tid
):
"""Return a 2-tuple where the second item is a list of non-deleted OIDs
of the specified partition, at given tid, in ascending order starting
from min_oid and at most the specified length. The first item of the
returned value is the min_oid value to use to get the next OIDs.
"""
@
abstract
@
abstract
def
getReplicationTIDList
(
self
,
min_tid
,
max_tid
,
length
,
partition
):
def
getReplicationTIDList
(
self
,
min_tid
,
max_tid
,
length
,
partition
):
"""Return a list of TIDs in ascending order from an initial tid value,
"""Return a list of TIDs in ascending order from an initial tid value,
...
...
neo/storage/database/mysql.py
View file @
071c6bf5
...
@@ -52,7 +52,7 @@ else:
...
@@ -52,7 +52,7 @@ else:
from
.
import
LOG_QUERIES
,
DatabaseFailure
from
.
import
LOG_QUERIES
,
DatabaseFailure
from
.manager
import
MVCCDatabaseManager
,
splitOIDField
from
.manager
import
MVCCDatabaseManager
,
splitOIDField
from
neo.lib
import
logging
,
util
from
neo.lib
import
logging
,
util
from
neo.lib.exception
import
UndoPackError
from
neo.lib.exception
import
NonReadableCell
,
UndoPackError
from
neo.lib.interfaces
import
implements
from
neo.lib.interfaces
import
implements
from
neo.lib.protocol
import
CellStates
,
ZERO_OID
,
ZERO_TID
,
ZERO_HASH
from
neo.lib.protocol
import
CellStates
,
ZERO_OID
,
ZERO_TID
,
ZERO_HASH
...
@@ -976,6 +976,22 @@ class MySQLDatabaseManager(MVCCDatabaseManager):
...
@@ -976,6 +976,22 @@ class MySQLDatabaseManager(MVCCDatabaseManager):
" ORDER BY tid DESC LIMIT %d,%d"
" ORDER BY tid DESC LIMIT %d,%d"
%
(
','
.
join
(
map
(
str
,
partition_list
)),
offset
,
length
)))
%
(
','
.
join
(
map
(
str
,
partition_list
)),
offset
,
length
)))
def
oidsFrom
(
self
,
partition
,
length
,
min_oid
,
tid
):
if
partition
not
in
self
.
_readable_set
:
raise
NonReadableCell
p64
=
util
.
p64
u64
=
util
.
u64
r
=
self
.
query
(
"SELECT oid, data_id"
" FROM obj FORCE INDEX(PRIMARY) JOIN ("
"SELECT `partition`, oid, MAX(tid) AS tid"
" FROM obj FORCE INDEX(PRIMARY)"
" WHERE `partition`=%s AND oid>=%s AND tid<=%s"
" GROUP BY oid LIMIT %s"
") AS t USING (`partition`, oid, tid)"
%
(
partition
,
u64
(
min_oid
),
u64
(
tid
),
length
))
return
None
if
len
(
r
)
<
length
else
p64
(
r
[
-
1
][
0
]
+
self
.
np
),
\
[
p64
(
oid
)
for
oid
,
data_id
in
r
if
data_id
is
not
None
]
def
getReplicationTIDList
(
self
,
min_tid
,
max_tid
,
length
,
partition
):
def
getReplicationTIDList
(
self
,
min_tid
,
max_tid
,
length
,
partition
):
u64
=
util
.
u64
u64
=
util
.
u64
p64
=
util
.
p64
p64
=
util
.
p64
...
...
neo/storage/database/sqlite.py
View file @
071c6bf5
...
@@ -24,7 +24,7 @@ import traceback
...
@@ -24,7 +24,7 @@ import traceback
from
.
import
LOG_QUERIES
from
.
import
LOG_QUERIES
from
.manager
import
DatabaseManager
,
splitOIDField
from
.manager
import
DatabaseManager
,
splitOIDField
from
neo.lib
import
logging
,
util
from
neo.lib
import
logging
,
util
from
neo.lib.exception
import
UndoPackError
from
neo.lib.exception
import
NonReadableCell
,
UndoPackError
from
neo.lib.interfaces
import
implements
from
neo.lib.interfaces
import
implements
from
neo.lib.protocol
import
CellStates
,
ZERO_OID
,
ZERO_TID
,
ZERO_HASH
from
neo.lib.protocol
import
CellStates
,
ZERO_OID
,
ZERO_TID
,
ZERO_HASH
...
@@ -710,6 +710,20 @@ class SQLiteDatabaseManager(DatabaseManager):
...
@@ -710,6 +710,20 @@ class SQLiteDatabaseManager(DatabaseManager):
" ORDER BY tid DESC LIMIT %d,%d"
" ORDER BY tid DESC LIMIT %d,%d"
%
(
','
.
join
(
map
(
str
,
partition_list
)),
offset
,
length
)))
%
(
','
.
join
(
map
(
str
,
partition_list
)),
offset
,
length
)))
def
oidsFrom
(
self
,
partition
,
length
,
min_oid
,
tid
):
if
partition
not
in
self
.
_readable_set
:
raise
NonReadableCell
p64
=
util
.
p64
u64
=
util
.
u64
r
=
self
.
query
(
"SELECT oid, data_id FROM obj JOIN ("
"SELECT `partition`, oid, MAX(tid) AS tid FROM obj"
" WHERE partition=? AND oid>=? AND tid<=?"
" GROUP BY oid LIMIT ?"
") AS t USING (`partition`, oid, tid)"
,
(
partition
,
u64
(
min_oid
),
u64
(
tid
),
length
)).
fetchall
()
return
None
if
len
(
r
)
<
length
else
p64
(
r
[
-
1
][
0
]
+
self
.
np
),
\
[
p64
(
oid
)
for
oid
,
data_id
in
r
if
data_id
is
not
None
]
def
getReplicationTIDList
(
self
,
min_tid
,
max_tid
,
length
,
partition
):
def
getReplicationTIDList
(
self
,
min_tid
,
max_tid
,
length
,
partition
):
u64
=
util
.
u64
u64
=
util
.
u64
p64
=
util
.
p64
p64
=
util
.
p64
...
...
neo/storage/handlers/client.py
View file @
071c6bf5
...
@@ -244,6 +244,13 @@ class ClientOperationHandler(BaseHandler):
...
@@ -244,6 +244,13 @@ class ClientOperationHandler(BaseHandler):
logging
.
info
(
'CheckCurrentSerial delay: %.02fs'
,
duration
)
logging
.
info
(
'CheckCurrentSerial delay: %.02fs'
,
duration
)
conn
.
answer
(
Packets
.
AnswerCheckCurrentSerial
(
locked
))
conn
.
answer
(
Packets
.
AnswerCheckCurrentSerial
(
locked
))
def
askOIDsFrom
(
self
,
conn
,
partition
,
length
,
min_oid
,
tid
):
app
=
self
.
app
if
app
.
tm
.
isLockedTid
(
tid
):
raise
DelayEvent
conn
.
answer
(
Packets
.
AnswerOIDsFrom
(
*
app
.
dm
.
oidsFrom
(
partition
,
length
,
min_oid
,
tid
)))
# like ClientOperationHandler but read-only & only for tid <= backup_tid
# like ClientOperationHandler but read-only & only for tid <= backup_tid
class
ClientReadOnlyOperationHandler
(
ClientOperationHandler
):
class
ClientReadOnlyOperationHandler
(
ClientOperationHandler
):
...
...
neo/tests/protocol
View file @
071c6bf5
...
@@ -22,6 +22,7 @@ AnswerNodeList([(NodeTypes,?(bin,int),?int,NodeStates,?float)])
...
@@ -22,6 +22,7 @@ AnswerNodeList([(NodeTypes,?(bin,int),?int,NodeStates,?float)])
AnswerObject(p64,p64,?p64,?int,bin,bin,?p64)
AnswerObject(p64,p64,?p64,?int,bin,bin,?p64)
AnswerObjectHistory(p64,[(p64,int)])
AnswerObjectHistory(p64,[(p64,int)])
AnswerObjectUndoSerial({p64:(p64,?p64,bool)})
AnswerObjectUndoSerial({p64:(p64,?p64,bool)})
AnswerOIDsFrom(?p64,[p64])
AnswerPackOrders([(p64,?bool,bool,?[p64],p64)])
AnswerPackOrders([(p64,?bool,bool,?[p64],p64)])
AnswerPartitionList(int,int,[[(int,CellStates)]])
AnswerPartitionList(int,int,[[(int,CellStates)]])
AnswerPartitionTable(int,int,[[(int,CellStates)]])
AnswerPartitionTable(int,int,[[(int,CellStates)]])
...
@@ -56,6 +57,7 @@ AskNodeList(NodeTypes)
...
@@ -56,6 +57,7 @@ AskNodeList(NodeTypes)
AskObject(p64,?p64,?p64)
AskObject(p64,?p64,?p64)
AskObjectHistory(p64,int,int)
AskObjectHistory(p64,int,int)
AskObjectUndoSerial(p64,p64,p64,[p64])
AskObjectUndoSerial(p64,p64,p64,[p64])
AskOIDsFrom(int,int,p64,p64)
AskPackOrders(p64)
AskPackOrders(p64)
AskPartitionList(int,int,?)
AskPartitionList(int,int,?)
AskPartitionTable()
AskPartitionTable()
...
...
neo/tests/threaded/test.py
View file @
071c6bf5
...
@@ -16,6 +16,7 @@
...
@@ -16,6 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import
os
import
os
import
random
import
sys
import
sys
import
threading
import
threading
import
time
import
time
...
@@ -142,6 +143,42 @@ class Test(NEOThreadedTest):
...
@@ -142,6 +143,42 @@ class Test(NEOThreadedTest):
self
.
assertRaises
(
POSException
.
POSKeyError
,
self
.
assertRaises
(
POSException
.
POSKeyError
,
storage
.
load
,
oid
,
''
)
storage
.
load
,
oid
,
''
)
@
with_cluster
(
storage_count
=
3
,
replicas
=
1
,
partitions
=
5
)
def
testIterOIDs
(
self
,
cluster
):
storage
=
cluster
.
getZODBStorage
()
client
=
cluster
.
client
oids
=
[]
for
i
in
xrange
(
5
):
txn
=
transaction
.
Transaction
()
storage
.
tpc_begin
(
txn
)
for
i
in
xrange
(
7
):
oid
=
client
.
new_oid
()
oids
.
append
(
u64
(
oid
))
storage
.
store
(
oid
,
None
,
''
,
''
,
txn
)
client
.
new_oid
()
storage
.
tpc_vote
(
txn
)
storage
.
tpc_finish
(
txn
)
tid
=
client
.
last_tid
self
.
assertEqual
(
oids
,
map
(
u64
,
client
.
oids
(
tid
)))
def
askOIDsFrom
(
orig
,
self
,
conn
,
partition
,
length
,
min_oid
,
tid
):
return
orig
(
self
,
conn
,
partition
,
3
,
min_oid
,
tid
)
with
Patch
(
ClientOperationHandler
,
askOIDsFrom
=
askOIDsFrom
):
self
.
assertEqual
(
oids
[
3
:
-
3
],
map
(
u64
,
client
.
oids
(
tid
,
p64
(
oids
[
3
]),
p64
(
oids
[
-
4
]))))
random
.
shuffle
(
oids
)
while
oids
:
txn
=
transaction
.
Transaction
()
storage
.
tpc_begin
(
txn
)
for
i
in
oids
[
-
6
:]:
oid
=
p64
(
i
)
storage
.
deleteObject
(
oid
,
storage
.
load
(
oid
)[
1
],
txn
)
storage
.
tpc_vote
(
txn
)
i
=
storage
.
tpc_finish
(
txn
)
self
.
assertEqual
(
sorted
(
oids
),
map
(
u64
,
client
.
oids
(
tid
)))
del
oids
[
-
6
:]
tid
=
i
self
.
assertEqual
(
sorted
(
oids
),
map
(
u64
,
client
.
oids
(
tid
)))
def
_testUndoConflict
(
self
,
cluster
,
*
inc
):
def
_testUndoConflict
(
self
,
cluster
,
*
inc
):
def
waitResponses
(
orig
,
*
args
):
def
waitResponses
(
orig
,
*
args
):
orig
(
*
args
)
orig
(
*
args
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment