[
https://issues.apache.org/jira/browse/SENTRY-1855?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16117056#comment-16117056
]
Na Li commented on SENTRY-1855:
-------------------------------
Hello,
Below is the latest proposed approach for permission change. Path change will
use the existing approach because there is only one thread to get HMS changes
and save into DB.
===============================
Requirements:
+ R1.) from sentry 1.8 to sentry 2.0 "do no harm", such as what's our upgrade
experience. Performance comparison
+ R2.) Is the approach scalable in terms of supporting multiple Sentry servers
and concurrent updates
+ R3.) Are the results consistent between Sentry and NN
+ R4.) Does it impact HMS-HA
Constrains from Requirements:
+ C1.) Normal operations should not generate exceptions. The performance of
Sentry 2.0 should be similar to or better than Sentry 2.0
+ C2.) The system should work reasonable well when having more Sentry servers
adding into the system and handling concurrent updates
+ C3.) The order Sentry applies the changes should be the same order NN applies
the changes, so NN will end up with the same result as Sentry and they behave
consistently
Current Approach for Permission Change:
+ The changeID is primary key and manually updated in Sentry application. The
current max(changeID) is read from DB, increased by 1, and then used as
primarykey for new change entry. If more than one threads are saving new change
entry with the same changeID, the transaction in onethread succeeds, such
transactions from other transactions fail, and go through retry with
exponential retry interval.
Benefits of the current approach
+ Satisfies R3.). The changeID is continuously increased. There is no hole in
changeID. Guaranteed consistency for both deltas and full snapshots.
+ Satisfied R4.). It should not affect HMS-HA
Issues with current approach
+ Violates R1.) because normal concurrent transaction could cause transaction
failure due to key conflict. When transaction fails, exponential retry interval
causes additional delay for transaction. When load reaches to certain point,
transaction fails to commit
+ Violates R2.) and it is not scalable. In our endurance test, we see
transaction failed to commit after max retry with two sentry servers
Proposed Approach for Permission Change:
+ The changeID is primary key and auto-incremented in DataStore. When a
transaction fails, it creates permanent hole in changeID. When a transaction
starts early but committed after transactions with larger changeID, it forms
temporary hole in changeID. Once this transaction is committed, the temporary
hole disappears. How long a temporary hole can exist is limited by how long a
transaction can be pending and still be committed successfully.
0) read <write_timeout> from configuration and set to datastore to timeout
transactions that take longer than that value. In this way, we can limit how
long a transaction can be pending and still be committed successfully.
<transaction buffer time> = <buffer factor> * <write_timeout>, and its default
value is 2. This is how long we go back of time to capture temporary holes in
changeID.
1.) Change the MSentryPermChange timestamp to be assigned by DB, not from
sentry server. In this way, we have a single source of truth for the timestamp
of the permission change entries.
1.a) Need to make sure the timestamp (using @CreateTimestamp annotation in
datanucleus) is set by DB, not be set by datanucleus using server time. This
can be done without using @CreateTimestamp if necessary by just doing a
default value of CURRENT_TIMESTAMP(6) on the database table and not setting
anything on the object. If the annotation can handle it, great, but we have to
make sure of the semantics.
2.) When Sentry sends changes to NN, it also includes the "current time in DB"
(Now).
3.) When NN asks for changes, it sends to Sentry the "last received 'current
time in DB'" (referred as Last_Now) together with the last processed changeID
for permission and [imageID, changeID] for path changes
4.) When Sentry receives the request, it sends all entries that are newer than
the time = Last_Now - <transaction buffer time>. NN applies the changes in
increasing order of changeID
Benefits of the proposed approach:
+ Satisfies requirements R1.), R2.), R3.) and R4.). It should not affect HMS-HA
Issues with Proposed Approach:
+ More complicated to implement than current approach is.
+ Require change protocol API between NN and Sentry to pass "current time in
DB"
+ Need to re-apply some old updates. May cause more overhead, but should be
reasonable. We can optimize how to reapply old updates. It will be done in next
release as the change will be local to NN.
Background on R3.)
There are several types of transaction orders: TO1) The order of transaction
starting time; TO2) The order of permission SQL execution time; TO3) The order
of transaction commit time; What order affects the result? NN should apply perm
changes in the order that determines the result, so it will behave consistently
with Sentry.
Our testing result shows that the TO2) is the order that determines the result.
Two transactions: T_1 First
End Result
Transaction Commit Order T_1: Transaction #1
T_1 First T_2 First S_1: SQL in Transaction #1
SQL Execution Order
S_1 First V_2 V_2 V_1: Value of Transaction #1
S_2 First V_1 V_1
Conclusion: SQL execution order determins the end result
The next question is "how to capture the order of permission SQL execution
order" One approach is to get the permission execution timestamp and save it to
the permission change entry. This brings two issues: a) It requires a lot of
code changes to do so. 2) It is possible to have two transactions on the same
authentication object with same timestamp even when it is on microsecond
granularity. In this case, timestamp fails to capture the SQL execution order.
Our test shows that when permission SQL execution is followed by permission
change log execution (that is when changeID is auto-incremented), the order of
changeID is exactly the same as the permission SQL execution. As a result, we
choose to apply changes at NN by the changeID order. This should satisfy R3.).
Two transactions: T_1 First
T_1 contains two SQL: S_1 followed by C_1
S_1: normal SQL query
C_1: log SQL change, primary key changeID is auto-increment
End Result
Log SQL Order
C_1 First C_2 First
SQL Execution Order
S_1 First C_1=1; C_2=2 C_1=1; C_2=2
S_2 First C_2=1; C_1=2 C_2=1; C_1=2
Conclusion: changeID is in the same order as the SQL execution order
Q & A
Question_1: Can we skip applying old updates? For example, the transactions
identified by the changeID are committed in the following timely order
[changeID_1, changeID_3, changeID_2]. When NN gets [changeID_1, changeID_3], it
applies them. When it gets [changeID_1, changeID_3, changeID_2], can it skips
re-applying [changeID: changeID_1, changeID_3], and only applies changeID_2?
That will avoid overhead of re-applying the old changes.
Answer_1: NO. The result is determined by SQL execution order and it is the
same as the changeID order. It is not the transaction commit order. The above
approach applies changes at NN in order of [changeID_1, changeID_3,
changeID_2], which is different from how Sentry applies the changes, which is
[changeID_1, changeID_2, changeID_3]. This will cause inconsistency between
Sentry and NN.
Question_2: Can we hold on applies some changes and until the temporary hole
finishes, and then apply changes in changeID order at NN?
Answer_2: the temporary hole could exist for a minute. Holding applying changes
for a minute may not be desirable. If we wait for every temporary hole for a
minute, the changes could backup, and NN will be out-of-sync from Sentry for a
while.
Thanks,
Lina
> PERM/PATH transactions can fail to commit to the sentry database under load
> ---------------------------------------------------------------------------
>
> Key: SENTRY-1855
> URL: https://issues.apache.org/jira/browse/SENTRY-1855
> Project: Sentry
> Issue Type: Sub-task
> Components: Sentry
> Affects Versions: 2.0.0
> Reporter: Alexander Kolbasov
> Assignee: Na Li
> Fix For: 2.0.0
>
> Attachments: SENTRY-1855.002-sentry-ha-redesign.patch,
> SENTRY-1855.003-master.patch, SENTRY-1855.01-sentry-ha-redesign.patch
>
>
> Looking at the latest stress runs, we noticed that some of the transactions
> could fail to commit to the database (with Duplicate key exception) after
> exhausting all the retries.
> This problem has become more evident if we have more number of clients
> connecting to Sentry to issue the permission updates. Was able to reproduce
> consistently with 15 clients doing 100 operations each.
> In the past we introduced exponential backoff (SENTRY-1821) so as part of
> test run increased the defaults to 750ms sleep and 20 retries. But even after
> this, the cluster still shows up the transaction failures. This change also
> increases the latency of every transaction in sentry.
> We need to revisit this and come up with a better way to solve this problem.
> {code}
> 2017-07-13 13:18:14,449 ERROR
> org.apache.sentry.provider.db.service.persistent.TransactionManager: The
> transaction has reached max retry number, Exception thrown when executing
> query
> javax.jdo.JDOException: Exception thrown when executing query
> at
> org.datanucleus.api.jdo.NucleusJDOHelper.getJDOExceptionForNucleusException(NucleusJDOHelper.java:596)
> at org.datanucleus.api.jdo.JDOQuery.execute(JDOQuery.java:252)
> at
> org.apache.sentry.provider.db.service.persistent.SentryStore.getRole(SentryStore.java:294)
> at
> org.apache.sentry.provider.db.service.persistent.SentryStore.alterSentryRoleGrantPrivilegeCore(SentryStore.java:645)
> at
> org.apache.sentry.provider.db.service.persistent.SentryStore.access$500(SentryStore.java:101)
> at
> org.apache.sentry.provider.db.service.persistent.SentryStore$11.execute(SentryStore.java:601)
> at
> org.apache.sentry.provider.db.service.persistent.TransactionManager.executeTransaction(TransactionManager.java:159)
> at
> org.apache.sentry.provider.db.service.persistent.TransactionManager.access$100(TransactionManager.java:63)
> at
> org.apache.sentry.provider.db.service.persistent.TransactionManager$2.call(TransactionManager.java:213)
> --
> at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:971)
> at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3887)
> at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3823)
> at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2435)
> at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2582)
> at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2530)
> at
> com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1907)
> at
> com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2141)
> at
> com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.java:1773)
> ... 33 more
> 2017-07-13 13:18:14,450 ERROR
> org.apache.sentry.provider.db.service.thrift.SentryPolicyStoreProcessor:
> Unknown error for request:
> TAlterSentryRoleGrantPrivilegeRequest(protocol_version:2,
> requestorUserName:hive, roleName:2017_07_12_15_06_38_1_2_805,
> privileges:[TSentryPrivilege(privilegeScope:DATABASE, serverName:server1,
> dbName:2017_07_12_15_06_38_1_2, tableName:, URI:, action:*,
> createTime:1499904401222, grantOption:FALSE, columnName:)]), message: The
> transaction has reached max retry number, Exception thrown when executing
> query
> java.lang.Exception: The transaction has reached max retry number, Exception
> thrown when executing query
> at
> org.apache.sentry.provider.db.service.persistent.TransactionManager$ExponentialBackoff.execute(TransactionManager.java:255)
> at
> org.apache.sentry.provider.db.service.persistent.TransactionManager.executeTransactionBlocksWithRetry(TransactionManager.java:209)
> at
> org.apache.sentry.provider.db.service.persistent.SentryStore.execute(SentryStore.java:3330)
> at
> org.apache.sentry.provider.db.service.persistent.SentryStore.alterSentryRoleGrantPrivilege(SentryStore.java:593)
> at
> org.apache.sentry.provider.db.service.persistent.SentryStore.alterSentryRoleGrantPrivileges(SentryStore.java:633)
> at
> org.apache.sentry.provider.db.service.thrift.SentryPolicyStoreProcessor.alter_sentry_role_grant_privilege(SentryPolicyStoreProcessor.java:256)
> at
> org.apache.sentry.provider.db.service.thrift.SentryPolicyService$Processor$alter_sentry_role_grant_privilege.getResult(SentryPolicyService.java:997)
> at
> org.apache.sentry.provider.db.service.thrift.SentryPolicyService$Processor$alter_sentry_role_grant_privilege.getResult(SentryPolicyService.java:982)
> at org.apache.thrift.ProcessFunction.process(ProcessFunction.java:39)
> at org.apache.thrift.TBaseProcessor.process(TBaseProcessor.java:39)
> {code}
--
This message was sent by Atlassian JIRA
(v6.4.14#64029)