9898import org .apache .ignite .internal .processors .cache .dr .GridCacheDrInfo ;
9999import org .apache .ignite .internal .processors .cache .persistence .CacheDataRow ;
100100import org .apache .ignite .internal .processors .cache .transactions .IgniteInternalTx ;
101+ import org .apache .ignite .internal .processors .cache .transactions .IgniteTxEntry ;
101102import org .apache .ignite .internal .processors .cache .transactions .IgniteTxKey ;
102103import org .apache .ignite .internal .processors .cache .transactions .IgniteTxLocalAdapter ;
103104import org .apache .ignite .internal .processors .cache .transactions .IgniteTxLocalEx ;
159160
160161import static org .apache .ignite .IgniteSystemProperties .IGNITE_CACHE_RETRIES_COUNT ;
161162import static org .apache .ignite .internal .GridClosureCallMode .BROADCAST ;
163+ import static org .apache .ignite .internal .processors .cache .GridCacheOperation .READ ;
162164import static org .apache .ignite .internal .processors .cache .distributed .dht .topology .GridDhtPartitionState .OWNING ;
163165import static org .apache .ignite .internal .processors .dr .GridDrType .DR_LOAD ;
164166import static org .apache .ignite .internal .processors .dr .GridDrType .DR_NONE ;
@@ -538,7 +540,8 @@ public void active(boolean active) {
538540
539541 /**
540542 * @param keys Keys to lock.
541- * @param timeout Lock timeout.
543+ * @param timeout Transaction timeout.
544+ * @param waitTimeout Lock wait timeout.
542545 * @param tx Transaction.
543546 * @param isRead {@code True} for read operations.
544547 * @param retval Flag to return value.
@@ -551,6 +554,7 @@ public void active(boolean active) {
551554 public abstract IgniteInternalFuture <Boolean > txLockAsync (
552555 Collection <KeyCacheObject > keys ,
553556 long timeout ,
557+ long waitTimeout ,
554558 IgniteTxLocalEx tx ,
555559 boolean isRead ,
556560 boolean retval ,
@@ -3028,6 +3032,196 @@ public CacheMetricsImpl metrics0() {
30283032 }
30293033 }
30303034
3035+ /** {@inheritDoc} */
3036+ @ Override public boolean lockTxEntry (CacheEntry <K , V > entry , long waitTimeout ) throws IgniteCheckedException {
3037+ A .notNull (entry , "entry" );
3038+
3039+ return lockTxEntryAsync (entry , waitTimeout ).get ();
3040+ }
3041+
3042+ /** {@inheritDoc} */
3043+ @ Override public boolean lockTxEntries (Collection <CacheEntry <K , V >> entries , long waitTimeout )
3044+ throws IgniteCheckedException {
3045+ A .notNull (entries , "entries" );
3046+
3047+ return lockTxEntriesAsync (entries , waitTimeout ).get ();
3048+ }
3049+
3050+ /** {@inheritDoc} */
3051+ @ Override public IgniteInternalFuture <Boolean > lockTxEntryAsync (CacheEntry <K , V > entry , long waitTimeout ) {
3052+ A .notNull (entry , "entry" );
3053+
3054+ return lockTxEntriesAsync (Collections .singleton (entry ), waitTimeout );
3055+ }
3056+
3057+ /** {@inheritDoc} */
3058+ @ Override public IgniteInternalFuture <Boolean > lockTxEntriesAsync (
3059+ Collection <CacheEntry <K , V >> entries ,
3060+ long waitTimeout
3061+ ) {
3062+ A .notNull (entries , "entries" );
3063+
3064+ GridNearTxLocal tx = tx ();
3065+
3066+ if (tx == null )
3067+ return new GridFinishedFuture <>(
3068+ new IgniteCheckedException ("Failed to acquire transactional lock without transaction." ));
3069+
3070+ if (!tx .pessimistic ())
3071+ return new GridFinishedFuture <>(
3072+ new IgniteCheckedException ("Failed to acquire transactional lock in optimistic transaction." ));
3073+
3074+ // Wait for previous per-transaction async operations to finish.
3075+ tx .txState ().awaitLastFuture ();
3076+
3077+ if (!tx .init ())
3078+ return new GridFinishedFuture <>(new IgniteTxRollbackCheckedException (
3079+ "Failed to acquire transactional lock because transaction has been completed: " + tx ));
3080+
3081+ if (entries .isEmpty ())
3082+ return new GridFinishedFuture <>(true );
3083+
3084+ try {
3085+ tx .addActiveCache (ctx , false );
3086+ }
3087+ catch (IgniteCheckedException e ) {
3088+ return new GridFinishedFuture <>(e );
3089+ }
3090+
3091+ Collection <KeyCacheObject > keys = new ArrayList <>(entries .size ());
3092+ List <IgniteTxEntry > txEntries = new ArrayList <>(entries .size ());
3093+ List <GridCacheVersion > expVers = new ArrayList <>(entries .size ());
3094+ Set <IgniteTxKey > txKeys = new HashSet <>(entries .size ());
3095+
3096+ CacheOperationContext opCtx = ctx .operationContextPerCall ();
3097+
3098+ for (CacheEntry <K , V > entry : entries ) {
3099+ A .notNull (entry , "entry" );
3100+
3101+ KeyCacheObject key = ctx .toCacheKeyObject (entry .getKey ());
3102+ IgniteTxKey txKey = ctx .txKey (key );
3103+
3104+ if (!txKeys .add (txKey ))
3105+ continue ;
3106+
3107+ IgniteTxEntry lockedTxEntry = tx .entry (txKey );
3108+
3109+ if (lockedTxEntry != null && (lockedTxEntry .op () != READ || lockedTxEntry .locked ()))
3110+ continue ;
3111+
3112+ if (!(entry .version () instanceof GridCacheVersion )) {
3113+ tx .removeAndUnlockTxEntries (txEntries );
3114+
3115+ return new GridFinishedFuture <>(new IgniteCheckedException ("Failed to acquire transactional lock for entry with " +
3116+ "unsupported version type [entry=" + entry + ", version=" + entry .version () + ']' ));
3117+ }
3118+
3119+ CacheObject val = ctx .toCacheObject (entry .getValue ());
3120+ GridCacheEntryEx entryEx = ctx .isColocated () ? ctx .colocated ().entryExx (key , tx .topologyVersion (), true ) : entryEx (key );
3121+
3122+ IgniteTxEntry txEntry = tx .addEntry (
3123+ READ ,
3124+ val ,
3125+ null ,
3126+ null ,
3127+ entryEx ,
3128+ null ,
3129+ null ,
3130+ true ,
3131+ -1L ,
3132+ -1L ,
3133+ null ,
3134+ opCtx != null && opCtx .skipStore (),
3135+ opCtx != null && opCtx .skipReadThrough (),
3136+ opCtx != null && opCtx .keepBinaryInInterceptor (),
3137+ opCtx != null && opCtx .isKeepBinary (),
3138+ CU .isNearEnabled (ctx )
3139+ );
3140+
3141+ keys .add (key );
3142+ txEntries .add (txEntry );
3143+ expVers .add ((GridCacheVersion )entry .version ());
3144+ }
3145+
3146+ if (keys .isEmpty ())
3147+ return new GridFinishedFuture <>(true );
3148+
3149+ // Acquire transactional lock future from concrete cache implementation. Use txLockAsync which
3150+ // delegates to cache-specific lockAllAsync implementations for distributed caches.
3151+ long timeout = tx .remainingTime ();
3152+
3153+ IgniteInternalFuture <Boolean > lockFut = txLockAsync (keys ,
3154+ timeout ,
3155+ waitTimeout ,
3156+ tx ,
3157+ /*isRead*/ true ,
3158+ /*retval*/ false ,
3159+ tx .isolation (),
3160+ /*invalidate*/ false ,
3161+ /*createTtl*/ 0L ,
3162+ /*accessTtl*/ 0L );
3163+
3164+ IgniteInternalFuture <Boolean > res = new GridEmbeddedFuture <>(
3165+ lockFut ,
3166+ (locked , ex ) -> {
3167+ if (ex != null )
3168+ return new GridFinishedFuture <>(ex );
3169+
3170+ if (!locked ) {
3171+ tx .removeAndUnlockTxEntries (txEntries );
3172+
3173+ return new GridFinishedFuture <>(false );
3174+ }
3175+
3176+ try {
3177+ for (int i = 0 ; i < txEntries .size (); i ++) {
3178+ GridCacheEntryEx cached = txEntries .get (i ).cached ();
3179+ EntryGetResult getRes = cached .innerGetVersioned (
3180+ null ,
3181+ tx ,
3182+ /*update-metrics*/ false ,
3183+ /*event*/ false ,
3184+ null ,
3185+ tx .resolveTaskName (),
3186+ null ,
3187+ false ,
3188+ null );
3189+
3190+ if (getRes == null || !expVers .get (i ).equals (getRes .version ())) {
3191+ tx .removeAndUnlockTxEntries (txEntries );
3192+
3193+ return new GridFinishedFuture <>(false );
3194+ }
3195+ }
3196+
3197+ return new GridFinishedFuture <>(true );
3198+ }
3199+ catch (IgniteCheckedException | GridCacheEntryRemovedException e ) {
3200+ tx .removeAndUnlockTxEntries (txEntries );
3201+
3202+ return new GridFinishedFuture <>(e );
3203+ }
3204+ }
3205+ );
3206+
3207+ // Register this future in transaction's async-holder so that subsequent operations
3208+ // that call tx.txState().awaitLastFuture() will wait for it.
3209+ GridCacheAdapter .FutureHolder holder = tx .txState ().lastAsyncFuture ();
3210+
3211+ if (holder != null ) {
3212+ holder .lock ();
3213+
3214+ try {
3215+ holder .saveFuture (res );
3216+ }
3217+ finally {
3218+ holder .unlock ();
3219+ }
3220+ }
3221+
3222+ return res ;
3223+ }
3224+
30313225 /** {@inheritDoc} */
30323226 @ Override public boolean isLockedByThread (K key ) {
30333227 A .notNull (key , "key" );
0 commit comments